Zero to DashboardThe Complete Power BI Course
Hands-on · build one real thing

From messy files to a published dashboard.

You don't learn Power BI by reading. You learn it by cleaning ugly data, wiring up a model, writing measures, and shipping a dashboard people actually use. This course walks that whole path on one realistic dataset, and explains the why behind every step.

Golden rule Clean in Power Query · Model with relationships · Calculate with measures · Design for one question at a time. The entire course is these four ideas.
Sales Performance FY2024–2025 · refreshed today Year ▾ Region ▾ TOTAL REVENUE $4.24M REVENUE YOY +18.3% VS TARGET 104% ORDERS 1,520 Revenue vs Target ■ Revenue – Target JanApr JulOctDec Revenue by Team Ent.MidSMB By Category Hardware 40% Software 28% Services 20% Accessories 12%

What you'll build, the capstone Sales Performance dashboard, from the raw files in this course.

6
real messy data files
9
modules + capstone
1
dashboard you ship
3
skill tracks in one

Three tracks, one path

This is a mixed-level course. Follow the main path top to bottom; watch for these boxes so nobody's bored or lost.

Stretch

The deeper, "professional" way. Skip on a first pass, come back for it.

Excel bridge

The Excel skill you already have, and its Power BI equivalent.

Checkpoint

How to know you got it right before moving on.

The data pack

All files live in data/raw/. Every messy thing in them is deliberate, each flaw teaches a specific skill.

FileTypeRoleTeaches
Sales_Raw.csvCSVMain fact table (~1,500 orders)Importing + cleaning nearly everything
Reps.csvCSVSales reps dimensionRelationships, the why relate lesson
Products.xlsxExcelProduct dimensionConnecting to an emailed workbook
Targets_Wide.csvCSVMonthly targets (months as columns)Unpivot & % of target
Employees_Raw.csvCSVHR headcountGrouping many orgs into HR vs Other
Sales_December_2025.xlsxExcelA "later" emailed monthly fileAppend / refresh, static vs dynamic
Stretch: look at the raw data first

Open Sales_Raw.csv in Notepad (not Excel, which silently "fixes" things and hides the mess). Notice dates as 2024-03-15, 3/15/2024, 15-Mar-2024, and blanks; Region as North, north, EAST, S., Norht, and " North " with spaces; UnitPrice as $1,518.50. This is what real exported data looks like. Never trust a column is clean until you've proven it.

MODULE 0

Setup, the interface & how licensing actually works

Install the tool, learn the map of the screen, and understand the license landscape before you build, because the license you have changes how you publish later.

Install Power BI Desktop (free)

Power BI Desktop is where you build, and it's free.

  • Best on Windows: Microsoft Store → search "Power BI Desktop" → Install. The Store version auto-updates monthly, which matters. Power BI ships features every month.
  • Alternative: download the installer from powerbi.microsoft.com.
  • On a Mac? No native version. You'll need Windows via a VM (Parallels) or a cloud PC. The browser Service lets you view, not fully build.

The three views + one door

  • Report view (chart icon), build visuals and the dashboard.
  • Data view (grid icon), inspect the actual loaded rows.
  • Model view (relationship icon), see tables and the lines between them.
  • Transform data (Home ribbon), opens the Power Query Editor, the cleaning room.
X Excel bridge

Power Query = Get & Transform in Excel (same engine). The data model = the Data Model behind PivotTables (Power Pivot). Report view = a far more powerful version of charts on a sheet. If you've used Get & Transform, you already know ~40% of this.

Licensing, the 3-minute version

  • Desktop, free, build anything locally.
  • Free account (Service), publish to your own My Workspace; generally can't share.
  • Pro, publish to shared workspaces and share reports (viewers also need Pro, unless the workspace is on Premium).
  • Premium Per User (PPU). Pro plus bigger/faster features.
  • Premium / Fabric capacity, org buys capacity; then viewers can be free. How big companies share widely.

Module 8 turns this into a decision tree.

MODULE 1

Getting data, and the emailed-file problem

Connect to files the right way, and learn the single most important sharing concept: static vs. dynamic connections.

Connect to the main file (CSV)

  1. Home → Get dataText/CSV → pick Sales_Raw.csv → Open.
  2. A preview appears. Power BI guesses types, and will guess wrong on this messy file. Expected.
  3. Do NOT click Load. Click Transform Data.
! The #1 beginner mistake

Clicking Load and dealing with the mess later inside the report. Always click Transform Data first and clean in Power Query. Loading dirty data means broken visuals and wrong numbers downstream.

Static vs. dynamic: the concept that separates amateurs from pros

When you connect to a file, where that file lives determines whether your report can refresh by itself.

Static (fragile): you connect to a path only your machine can see, C:\Users\you\Downloads\Sales_Raw.csv. When you publish to the cloud, the cloud can't reach your Downloads. To update, you open Desktop, re-import, and re-publish by hand every time.

Dynamic (robust): you connect to a place the cloud can also reach, OneDrive for Business, SharePoint, a gateway'd network path, or a database. Now you can schedule automatic refresh: the report re-reads the source on a timer, no human needed.

Why the file's location decides everything

STATIC · FRAGILE Emailed .xlsx save Your Downloads C:\Users\you\… ☁ Cloud No refresh, re-import by hand DYNAMIC · ROBUST Emailed .xlsx save OneDrive / SharePoint cloud-reachable ☁ Cloud Scheduled auto-refresh
Same file, different home. The cloud can't reach your PC's Downloads, so a published report goes stale. Put the file where the cloud can see it and refresh runs itself.

So when a colleague emails you Sales_Report.xlsx every Monday, you have three tiers:

ApproachHowAuto-refresh?
Save to Downloads, importGet data → local pathNo
Save to synced OneDrive/SharePoint, import from thereGet data → SharePoint/OneDriveYes
Sender drops into a fixed folder, connect to the folderGet data → Folder connectorYes ++
Stretch: the Folder connector is the pro move

Connect to a folder instead of one file (Get data → More → Folder). Power Query combines every file matching the shape. Next month, the sender drops Sales_February.xlsx in → you hit Refresh → February appears. No re-import, no editing queries. This is the answer to "the data gets emailed to me every month." We use it in Module 3.

! Emailed-attachment reality

Outlook attachments live inside the email, not on disk. Power BI can't read a mailbox as a normal user. The workflow is always: attachment → saved to a cloud-synced folder → Power BI reads the folder. Automating the save step is a job for Power Automate (Stretch topic).

Connect to the emailed Excel dimension

  1. Get data → Excel workbookProducts.xlsx → Open.
  2. In the Navigator, tick the Products sheet/table.
  3. Transform Data (again, always).
X Excel bridge: table vs. sheet

If the sender formatted their data as a proper Excel Table (Ctrl+T), connecting is rock-solid. If it's just cells, you connect to the sheet's used range, fragile if they add notes or blank rows. Coach your data senders to use real Tables; it's the biggest gift they can give you.

Checkpoint

You can explain in one sentence why saving an emailed file to Downloads makes a published report go stale, the cloud can't reach your Downloads, so scheduled refresh has nothing to read.

MODULE 2

Power Query: fixing bad data

Turn Sales_Raw.csv from garbage into a trustworthy table. This is where 70% of real Power BI work happens, and where beginners under-invest.

X The whole point of Power Query

The Applied Steps pane records every change as replayable instructions that re-run on every refresh. You're not cleaning data, you're writing a repeatable recipe that fixes next month's file automatically too.

The same rows, before & after Power Query

Sales_Raw · MESSY DateRegionPrice 3/15/2024" North "$1,518.50 (blank)north1,729.66 15-Mar-2024EAST129.08 Power Query CLEANED DateRegionPrice 2024-03-15North1518.50 (null)North1729.66 2024-03-15East129.08
Fix the types and text once as recorded steps, and every future file gets the same treatment automatically.

Fix data types (the #1 source of wrong numbers)

  • OrderDate: text (mixed formats). Change type → Date. Ambiguous US/UK dates? Right-click → Change Type → Using Locale → English (United States). Blanks become null, correct.
  • UnitPrice ($1,518.50): Replace $ → nothing, replace , → nothing, then change type → Decimal.
  • Units (1,200, N/A): replace ,; convert to Whole Number; then Replace Errors with null (a bad count is unknown, not zero).
Stretch: one deliberate type step

Delete the auto "Changed Type" step, do text cleanup first, then add one Changed Type step near the end. Stray multiple type steps are a top cause of refresh errors when next month's values differ.

Trim & clean whitespace (the invisible bug)

" North " and " Enterprise " look identical to North but are different values, they'll split your totals into two bars. Select the columns → Transform → Format → Trim, then Format → Clean.

! Why it matters

Untrimmed text is the #1 cause of "why do I have two 'North' bars?" It's invisible in the data but real to the engine. Trim early, trim always.

Standardize typos & abbreviations

Region still has north, EAST, S., Norht. Quick fix: Replace Values, one per variant. Brittle, a new typo next month slips through.

Stretch: the robust way is a mapping table

Build a small map (Variant → CleanRegion), Merge it in, pull the clean value. New variants get added to the map, not the query logic. Same idea powers the HR grouping in Module 3.

Remove duplicate rows & handle Discount

  • Select all columns → Home → Remove Rows → Remove Duplicates (removes the ~20 exact-duplicate orders).
  • Discount mixes blank / 0 / 10% / 0.1. Replace blanks with 0; convert 10% text to 0.10 via a custom column (if Text.Contains([Discount],"%") then …).
! "Duplicate" means what, exactly?

Removing duplicates on all columns removes truly identical rows (safe). On one column it's a different, aggressive operation. For a fact table, only remove fully identical rows.

Checkpoint

OrderDate is a real Date; UnitPrice and Units are numbers; Region/Segment have no stray spaces or typos; duplicates are gone. Your Applied Steps read like a clean recipe.

MODULE 3

Reshaping data into something meaningful

The moves that turn "technically clean" into "actually useful", including your exact ask: collapse many organizations into HR vs Other.

Add revenue, duplicate columns, extract parts

  • Custom Column LineRevenue = [Units] * [UnitPrice] * (1 - [Discount]).
  • Duplicate Column (right-click) when you need the same source shaped two ways, keep the original and a grouped/extracted version. This sets up the HR grouping below.
  • Column From Examples, type the output you want for a few rows; Power Query writes the logic.
Stretch: column vs. measure

Compute per-row values you'll filter on as columns; compute filter-aware aggregations (totals, YoY) as measures. Row-level revenue is a fair column; "Total Revenue" will be a measure. When in doubt, measure, columns bloat the model.

★ Grouping many categories into few: HR vs Other

Collapse ~22 organizations into two meaningful buckets

ORGANIZATION · 22 VALUES Human Resources People & Culture Recruiting Finance Engineering IT · Legal · … Conditional Column (duplicate the column first) HR 8 variants → 1 bucket Other everything else
Duplicate Organization, then a Conditional Column (or a mapping-table merge) sorts every value into HR or Other, you keep the detail and gain the summary.

Employees_Raw.csv's Organization column has ~22 values (Human Resources, HR, People & Culture, Talent Acquisition, Recruiting, Learning & Development, HR Operations, People Ops, plus Finance, Engineering, IT…). You want just HR vs Other.

Step 1, duplicate Organization → rename the copy OrgGroup (keep the detail).

Step 2, collapse it. Three methods, worst → best:

  • A · Replace Values, map each HR variant to HR. Awkward to turn "everything else" into Other.
  • B · Conditional Column (recommended). Add Column → Conditional Column: If Organization equals each HR variant Then HR; Else Other. Reads like plain English and is easy to extend. Trim/Clean first so equals matches.
  • C · Mapping table (scales to 200 categories), a two-column lookup (Organization → OrgGroup, shipped at data/reference/Org_Grouping_Key.csv), then Merge Queries on Organization and expand OrgGroup. Grouping logic lives in a spreadsheet a business user can edit, no query surgery when a new department appears.
X Excel bridge

Method A ≈ nested IF/SUBSTITUTE; Method B ≈ IFS; Method C ≈ XLOOKUP against a mapping tab. Same logic you already use, made repeatable.

Checkpoint

Employees has both Organization (detailed) and OrgGroup (HR / Other only). You kept the detail and gained the summary, that's why we duplicated the column.

Unpivot: fix "months as columns"

Targets_Wide.csv has Team then one column per month. Power BI wants long data: one row per Team per Month. Select Team → right-click → Unpivot Other Columns. Rename Attribute → MonthLabel, Value → Target; convert MonthLabel to a real date.

Wide → long: what "unpivot" actually does

WIDE · months across the top TeamJanFebMar Enterprise420k415k438k Mid-Market260k271k259k SMB150k170k178k Unpivot LONG · one row per team per month TeamMonthTarget EnterpriseJan420,000 EnterpriseFeb415,000 Mid-MarketJan260,000
Long ("tidy") data is what slicers, relationships, and time-intelligence all expect. Dates or categories across the top? Your instinct should be "unpivot."
Stretch: why long beats wide, always

Wide data can't be sliced, can't relate to a date table, and breaks when a new month is added. When a table has dates or categories running across the top, your instinct should be "unpivot."

Append the "next month" emailed file

Sales_December_2025.xlsx just arrived. Manual way: Append Queries. Dynamic way: the Folder connector from Module 1, drop next month's file in the folder, hit Refresh, done. Append needs matching columns/types, exactly why we cleaned types deliberately in Module 2. Then Home → Close & Apply.

MODULE 4

The data model & relationships, the "why"

Understand why you split data across tables and connect them, the concept that makes everything after it possible.

The problem relationships solve

Sales has RepID = 1017. That number is meaningless alone, who is 1017? What team? When hired? That lives in Reps. A relationship tells Power BI "the RepID in Sales points to the RepID in Reps." Now every order silently knows its rep's name, team, and region, and you can slice sales by team even though Sales has no team column.

1 · ∗ ∗ · 1 ∗ · 1 Sales fact · the events Reps RepID → name, team Products ProductID → category Date one row per day
The star schema: one fact table in the middle, dimension tables pointing in. Each line is many-to-one.
X Excel bridge

This is XLOOKUP, but set up once, at the table level, instead of dragging a formula down a million rows. Keep one clean Reps list and link to it. That link is a relationship.

Why not one big table?

Jamming rep name, product, category onto every order, a "flat" table, causes bloat & slowness (names repeat on thousands of rows), inconsistency (same rep spelled two ways), and rigidity (no clean list of all reps, only those who sold). The pro shape is a star schema: one fact table (events + keys + numbers) surrounded by dimension tables (the descriptions you filter by).

Create relationships

Model view → drag Sales[RepID] onto Reps[RepID]; drag Sales[ProductID] onto Products[ProductID]. Double-click a line to inspect:

  • Cardinality: Many-to-one (∗:1), many sales to one rep. The healthy default.
  • Cross-filter direction: Single (the "1" side filters the "many"). Leave it single unless you have a reason.
! Watch for the unmatched key

We planted RepID 9999, a rep not in Reps. Those orders still count but show under a blank rep. That blank is Power BI telling you your data has an orphan key. Real skill: noticing it and asking "why is there a sale from a rep we don't have on file?"

Build a proper Date table (do not skip)

Time intelligence (YoY, YTD, running totals) needs a dedicated Date dimension, one row per day, related to Sales[OrderDate].

New table · DAX
Date =
ADDCOLUMNS(
    CALENDAR(DATE(2024,1,1), DATE(2025,12,31)),
    "Year",      YEAR([Date]),
    "MonthNo",   MONTH([Date]),
    "Month",     FORMAT([Date],"MMM"),
    "MonthYear", FORMAT([Date],"MMM yyyy"),
    "Quarter",   "Q" & FORMAT([Date],"Q")
)

Sort Month by MonthNo; relate Date[Date] → Sales[OrderDate]; then Table tools → Mark as Date Table.

Stretch: why a separate Date table

Slicing by Sales[OrderDate] only shows dates that had a sale, and can't compare against Targets (different dates). A shared Date dimension relates to both, so one slicer filters everything, and SAMEPERIODLASTYEAR/DATESYTD require a marked date table. This one habit prevents a whole class of "my YoY is wrong" bugs.

Checkpoint

Model view shows a star: Sales in the middle, lines to Reps, Products, Date. Every line many-to-one, single direction. No table stranded. You can say out loud why each line exists.

MODULE 5

Measures & DAX

Write calculations that respond to whatever the user filters. This is where a report becomes intelligent.

Measure vs. calculated column

A column is computed once per row at refresh, and stored (use for row attributes you filter by). A measure is computed on the fly for the current filter context (use for all aggregations).

! The trap

Beginners make everything a column because it feels like Excel. Columns bloat the model. Default to measures for anything you'd sum, average, or compare. If I'm aggregating, it's a measure.

Core measures, build in order

DAX
Total Revenue   = SUM ( Sales[LineRevenue] )
Total Units     = SUM ( Sales[Units] )
Order Count     = DISTINCTCOUNT ( Sales[OrderID] )
Avg Order Value = DIVIDE ( [Total Revenue], [Order Count] )

Use DIVIDE, never /, it handles divide-by-zero gracefully (returns blank, not an error).

SUMX, then CALCULATE, the most important function in DAX

DAX · iterators & filter context
Total Revenue (SUMX) =
    SUMX ( Sales, Sales[Units] * Sales[UnitPrice] * (1 - Sales[Discount]) )

Enterprise Revenue = CALCULATE ( [Total Revenue], Reps[Team] = "Enterprise" )

% of Region Total =
    DIVIDE ( [Total Revenue], CALCULATE ( [Total Revenue], ALL ( Sales[Region] ) ) )

SUMX walks each row then sums. CALCULATE changes the filter context, add a filter, or remove one with ALL.

Stretch: CALCULATE is the whole ballgame

Almost every "advanced" measure is CALCULATE([base], <filter change>). Understand CALCULATE + filter context deeply and you understand DAX. Invest here.

Time intelligence & % of target

DAX · needs the marked Date table
Revenue LY  = CALCULATE ( [Total Revenue], SAMEPERIODLASTYEAR ( 'Date'[Date] ) )
Revenue YoY % = DIVIDE ( [Total Revenue] - [Revenue LY], [Revenue LY] )
Revenue YTD = TOTALYTD ( [Total Revenue], 'Date'[Date] )

Total Target = SUM ( Targets[Target] )
Revenue vs Target % = DIVIDE ( [Total Revenue], [Total Target] )
Checkpoint

Drop Total Revenue, Revenue YoY %, Revenue vs Target % into a table by Reps[Team]. Numbers change sensibly when you click a region slicer. YoY blank everywhere? Your Date table isn't marked or related, back to Module 4.

MODULE 6

Visualizations: how to think about them

Stop asking "what chart looks cool" and start asking "what question am I answering." Chart choice is a communication decision, not decoration.

Start from the question, then pick the visual

The question is about…Sounds like…Use
Comparison between categories"Which team sells most?"Bar / Column
Trend over time"How is revenue trending?"Line / area
Part-to-whole"What share is each region?"Stacked bar; pie only for 2–4 slices
A single headline number"What's total revenue?"Card / KPI
Progress to a goal"Are we hitting target?"Gauge / KPI / bar + target line
Relationship of two measures"Do bigger orders discount more?"Scatter
Detail / lookup"Show me the actual orders"Table / Matrix
Geography"Revenue by region on a map"Map (only if geography truly matters)

When to use what, the six workhorses

Bar / Column Compare categories sort it, zero baseline Line / Area Trend over time needs a Date table Stacked bar Part-to-whole pie only for 2–4 42 Card / KPI One big number the 3-second answer Scatter Relationship (x vs y) spot correlations Table / Matrix Detail / lookup exact values
Pick the visual by the question, not by looks. Gauge or a bar-with-target-line covers "progress to a goal"; a map only when geography truly carries the story.
! Pie chart discipline

Humans can't compare angles well. Pie only for 2–4 parts summing to a meaningful whole. For "revenue by 8 products," a bar chart wins every time.

Data storytelling, what you show is the story

The same numbers can inform or mislead depending on which data you show and how you display it. That choice is the real analysis, the chart is your argument, not decoration. A dashboard that shows everything says nothing; a good one answers a decision at a glance.

Two habits carry most of it: (1) show the comparison that answers the question and cut the rest, and (2) make the takeaway readable in seconds, sort it, start value axes at zero, use one colour that means something, label directly, and write the title as the insight ("Laptop Pro drives 22%"), not the field name.

Same data, two ways, the display makes or breaks the story

8 PRODUCTS AS A PIE ✗ Which is biggest? Good luck. …AS A SORTED BAR ✓ Laptop Pro22% Analytics Suite17% Monitor 3214% Dock Station12% BI License11% Headset9% Webcam HD8% USB-C Cable7% Ranking is obvious in one glance.
Identical numbers. The pie buries the ranking in near-identical wedges and a rainbow of meaningless colour; the sorted bar, one accent, the leader highlighted, direct labels, tells the story instantly. The display is the decision.
Stretch: make the chart argue for you

Title states the takeaway, not the field. Grey out everything and colour the one bar that matters. Annotate the moment on a trend line ("new pricing launched"). Order categories by value, not alphabetically. You're not just plotting numbers, you're making a point, and removing everything that competes with it.

Build one, then format so it earns its keep

  • Clustered column → Reps[Team] on X, [Total Revenue] on Y. Add a Year slicer, it responds live.
  • Sort bars by value descending (the "…" menu). Sorted bars are instantly readable.
  • Axis starts at zero for bar/column, truncating exaggerates differences (that's how charts lie).
  • Titles as answers, not field names. Kill leftover "Sum of LineRevenue."
  • Color means something (highlight the max, red/green vs target), never rainbow decoration.
Stretch: cross-filter, drill, and the "so what" test

Clicking a bar cross-filters every other visual (the main reason dashboards beat static reports). Add drill-down (Year→Quarter→Month) and drill-through detail pages. For every visual ask: if an exec glanced at this for 3 seconds, would they leave with the right takeaway? If not, it's decoration, cut it or fix it.

Checkpoint

You have a sorted column chart (Revenue by Team), a line (Revenue over time), KPI cards, and clicking any bar cross-filters the rest of the page.

MODULE 7

Assembling an impressive dashboard

Go from "a page of charts" to something designed that answers a decision. Design guides the eye to the decision fast, it isn't decoration.

Decide the story first

Write the headline before placing a visual: "How are we tracking to target this year, who's driving it, and where's the risk?" Everything on the page serves that sentence; anything else goes on a detail page.

Layout: the F-pattern & hierarchy

  • Top strip: 3–5 KPI cards, the headline numbers (Revenue, YoY %, vs Target %, Orders). The "answer in 3 seconds" row.
  • Upper-middle: the main trend (revenue over time vs. target), biggest real estate.
  • Lower: breakdowns, by team, region, product.
  • Consistent slicers (Year, Region, Team) where users expect them.
  • Alignment & whitespace, snap to a grid. Aligned edges read as "designed"; ragged edges read as "amateur."

A consistent visual system

  • Theme: View → Themes, define the palette once; every visual inherits it.
  • Color with meaning: one accent; reserve red/green strictly for bad/good.
  • Typography: one font, sized for hierarchy, big KPI numbers, medium titles, small labels.
  • Bookmarks + buttons: build toggles ("By Region" vs "By Product"), a slide-in slicer panel, a reset button, page navigation.
The polish checklist & 10-second test

KPI row up top · one clear main visual · everything aligned · one theme · titles tell the story · slicers consistent · numbers formatted ($1,519, not 1518.5) · a subtitle saying what it is and when it refreshed. Then show it to someone new, within ~10 seconds they should tell you the headline. If not, fix the hierarchy, don't add charts.

Stretch: accessibility & mobile

Check color contrast (don't rely on color alone, add labels/icons), set tab order (Selection pane), and build a phone layout (View → Mobile layout).

MODULE 8

Publishing & sharing

Get your dashboard off your laptop and into colleagues' hands, with refresh, and pick the right license path.

The mental model: Desktop → Service → Audience

You build in Desktop (a .pbix), publish to the Service (app.powerbi.com), where reports live in workspaces. You share by granting workspace access, or by packaging a workspace into an App.

How a report gets from your laptop to an audience

publishintopackagedeliver Desktop the .pbix file ☁ Service app.powerbi.com Workspace shared, roles App curated, read-only Viewers your audience free free acct Pro Pro Pro · or free* * free viewers only when the workspace runs on Premium / Fabric capacity.
Sharing lives in workspaces, not My Workspace, the license each person needs depends on where the report lands and who opens it.

Personal vs. Pro vs. Workspace

DestinationLicenseGood for / limitation
My Workspace (personal)FreeYourself only. View in browser/mobile, refresh for your own use. Generally can't share.
Shared WorkspacePro (you)Publish here, add colleagues as Admin/Member/Contributor/Viewer. Viewers need Pro unless the workspace is on Premium.
AppPro + workspaceCurated, read-only package with its own navigation. How you distribute cleanly to many people.

Which license do I actually need?

  • Just me, my machine: Desktop (free).
  • Just me, in the cloud/phone: free account, My Workspace.
  • Share with a few colleagues: Pro for you and each viewer.
  • Share widely without buying everyone Pro: Premium/Fabric capacity → viewers can be free.
  • You need big models / faster refresh / paginated reports: Premium Per User (PPU).
! The classic gotcha

You publish a beautiful report to My Workspace, send the link, and colleagues get "you don't have access." Fix: publish to a shared workspace (Pro), add them as Viewers, or distribute via an App. Sharing lives in workspaces, not My Workspace.

Scheduled refresh, closing the static-vs-dynamic loop

  1. In the Service, find your semantic model → Settings.
  2. Data source credentials: sign in to the source.
  3. Scheduled refresh: on, pick times (Pro: 8/day; Premium: 48/day).
  4. Only works if the source is cloud-reachable, exactly why Module 1 insisted on OneDrive/SharePoint/Folder.
Stretch: gateway, RLS & good citizenship

On-premises Data Gateway: a small service on an always-on machine that lets the cloud reach on-prem/network data for refresh. Row-Level Security: each viewer sees only their slice. Certify shared datasets so others reuse the trusted source instead of rebuilding.

Checkpoint

Your dashboard is in a shared workspace, colleagues can open it, scheduled refresh is green, and you can explain which license each person needs to view it.

CAPSTONE

Build & publish a "Sales Performance" dashboard

  1. Get data from all files; append Sales_December_2025.xlsx (bonus: via a Folder connection).
  2. Clean in Power Query: types, dates, currency text, whitespace, region typos, duplicates.
  3. Reshape: LineRevenue; unpivot Targets; produce OrgGroup = HR vs Other.
  4. Model: star schema with a marked Date table; relate both facts → Date; handle orphan RepID 9999.
  5. Measures: Revenue, Orders, AOV, YoY %, YTD, vs Target %.
  6. Visualize: KPI row, trend vs target, by team/category/region, sorted, formatted, cross-filtering.
  7. Design: one theme, aligned grid, story titles, Year/Region/Team slicers.
  8. Publish to a shared workspace; set scheduled refresh; note which license each viewer needs.

Grading rubric

AreaExcellentPts
Data cleaningTypes correct; no whitespace/typos; dupes removed; errors handled20
ReshapingLineRevenue correct; Targets unpivoted; HR/Other clean & extensible15
Data modelTrue star; marked Date table; correct cardinality; orphan handled20
MeasuresCorrect, use DIVIDE, time intelligence works, % of target correct20
VisualizationRight chart per question; sorted; formatted; sensible interactions15
Design & polishHierarchy, theme, alignment, story titles; passes 10-second test5
PublishingCorrect workspace; refresh configured; license reasoning correct5
CHEAT SHEETS

Quick reference

Power Query cleanup

ProblemFix
Dates as text / mixed formatsChange Type → Date (or Using Locale)
$1,299.00 as textReplace $, replace , → Change Type Decimal
N/A in a number columnChange type → Replace Errors with null
" North " paddedTrim then Clean
north, EAST, Norht, S.Replace Values, or mapping table + Merge
Exact duplicate rowsRemove Duplicates (all columns)
Months as columnsUnpivot Other Columns
Many orgs → HR/OtherDuplicate column → Conditional Column (or mapping merge)
New file each monthFolder connector (auto-combine)

DAX starter pack

Total Revenue       = SUM ( Sales[LineRevenue] )
Order Count         = DISTINCTCOUNT ( Sales[OrderID] )
Avg Order Value     = DIVIDE ( [Total Revenue], [Order Count] )
Revenue LY          = CALCULATE ( [Total Revenue], SAMEPERIODLASTYEAR ( 'Date'[Date] ) )
Revenue YoY %       = DIVIDE ( [Total Revenue] - [Revenue LY], [Revenue LY] )
Revenue YTD         = TOTALYTD ( [Total Revenue], 'Date'[Date] )
Total Target        = SUM ( Targets[Target] )
Revenue vs Target % = DIVIDE ( [Total Revenue], [Total Target] )