<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:media="http://search.yahoo.com/mrss/" >

<channel>
	<title>Learn SharePoint, Microsoft Power Platform and SPFx Tutorials &#8211; SPGuides</title>
	<atom:link href="https://www.spguides.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.spguides.com</link>
	<description>Learn SharePoint, Power Platform, SPFx, etc, SharePoint training courses</description>
	<lastBuildDate>Sun, 13 Sep 2026 09:44:19 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.8</generator>

<image>
	<url>https://www.spguides.com/wp-content/uploads/2026/05/SPguides-Favicon-new-348x215.avif</url>
	<title>Learn SharePoint, Microsoft Power Platform and SPFx Tutorials &#8211; SPGuides</title>
	<link>https://www.spguides.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Dataverse Solution: The Complete Guide to Creating, Exporting, and Deploying Solutions</title>
		<link>https://www.spguides.com/dataverse-solution/</link>
		
		<dc:creator><![CDATA[Bijay Kumar]]></dc:creator>
		<pubDate>Sun, 13 Sep 2026 06:29:23 +0000</pubDate>
				<category><![CDATA[Power Apps]]></category>
		<category><![CDATA[Dataverse Solution]]></category>
		<guid isPermaLink="false">https://www.spguides.com/?p=52414</guid>

					<description><![CDATA[I still remember the first time a client asked me to &#8220;just move the app to production real quick.&#8221; I had built everything directly inside the default environment, with no solution at all. Moving it turned into a two-day mess of missing tables and broken relationships. That mistake taught me why a Dataverse solution exists ... <a title="Dataverse Solution: The Complete Guide to Creating, Exporting, and Deploying Solutions" class="read-more" href="https://www.spguides.com/dataverse-solution/" aria-label="Read more about Dataverse Solution: The Complete Guide to Creating, Exporting, and Deploying Solutions">read more...</a>]]></description>
										<content:encoded><![CDATA[
<p>I still remember the first time a client asked me to &#8220;just move the app to production real quick.&#8221; I had built everything directly inside the default environment, with no solution at all. Moving it turned into a two-day mess of missing tables and broken relationships.</p>



<p>That mistake taught me why a <strong>Dataverse solution</strong> exists in the first place.</p>



<p>A solution is the container that holds your tables, apps, flows, and other components together. It&#8217;s how you move your work from a development environment to test, and then to production, without losing anything along the way.</p>



<p>In this tutorial, I&#8217;ll walk you through what a Dataverse solution is, why you need one, how to create a solution with a proper publisher, how to add components to it, and how to export and import it between environments the right way.</p>



<h2 class="wp-block-heading">The sample setup we&#8217;ll use throughout this guide</h2>



<p>To keep things practical, I&#8217;ll use a real example: an <strong>IT Helpdesk</strong> app built on <strong>Microsoft Dataverse</strong>. This is the kind of small business app I build for clients all the time, and it&#8217;s a perfect fit for showing how solutions work.</p>



<p>Our app is built on a Dataverse table called <strong>Tickets</strong>, with these columns:</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>Column Name</th><th>Data Type</th></tr></thead><tbody><tr><td>Title (Ticket Subject)</td><td>Single line of text</td></tr><tr><td>Status</td><td>Choice</td></tr><tr><td>Priority</td><td>Choice</td></tr><tr><td>RaisedBy</td><td>Lookup (to Contact table)</td></tr><tr><td>DueDate</td><td>Date only</td></tr><tr><td>Category</td><td>Choice</td></tr><tr><td>Description</td><td>Multiple lines of text</td></tr></tbody></table></figure>



<p>Here&#8217;s what the sample data in our <strong>Tickets</strong> table looks like:</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>Title</th><th>Status</th><th>Priority</th><th>RaisedBy</th><th>DueDate</th><th>Category</th></tr></thead><tbody><tr><td>VPN not connecting</td><td>Open</td><td>High</td><td>John Miller</td><td>09/16/2026</td><td>Network</td></tr><tr><td>Outlook keeps crashing</td><td>In Progress</td><td>Medium</td><td>Sarah Lee</td><td>09/18/2026</td><td>Software</td></tr><tr><td>New laptop request</td><td>Open</td><td>Low</td><td>David Chen</td><td>09/25/2026</td><td>Hardware</td></tr><tr><td>Password reset needed</td><td>Resolved</td><td>High</td><td>Priya Nair</td><td>09/12/2026</td><td>Access</td></tr><tr><td>Printer offline</td><td>Open</td><td>Medium</td><td>Mark Adams</td><td>09/17/2026</td><td>Hardware</td></tr><tr><td>Shared drive access</td><td>In Progress</td><td>Low</td><td>Lisa Wong</td><td>09/20/2026</td><td>Access</td></tr></tbody></table></figure>



<p>I&#8217;ll refer back to this <strong>Tickets</strong> table and its sample rows throughout this article. Every screen, every component, and every solution I describe below is built around this same <strong>IT Helpdesk</strong> app, so nothing feels abstract.</p>



<p>If you haven&#8217;t worked with Dataverse tables before, it helps to first read <a href="https://www.spguides.com/what-is-microsoft-dataverse/">what is Microsoft Dataverse</a> and how to <a href="https://www.spguides.com/dataverse-create-table/">create a table in Dataverse</a> before jumping into solutions.</p>



<h2 class="wp-block-heading">What is a Dataverse solution?</h2>



<p>A <strong>Dataverse solution</strong> is a container that packages your customizations — tables, columns, forms, views, canvas apps, model-driven apps, and Power Automate flows — into one transportable unit.</p>



<p>Think of it like a box you pack before moving houses. Instead of carrying your table, your app, and your flow separately from one environment to another, you put them all in one box (the solution) and move the whole box at once.</p>



<p>Every customization you make in Dataverse lives inside a solution, whether you create one on purpose or not. If you don&#8217;t pick a solution, Microsoft dumps your work into the <strong>default solution</strong>, which is a shared, messy container you should never rely on for real projects.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>Pro Tip:</strong> I&#8217;ve lost count of how many client environments I&#8217;ve inherited where everything was built in the default solution. Untangling components from the default solution later is painful and sometimes impossible. Create a custom solution before you build anything — even a single table.</p>
</blockquote>



<h2 class="wp-block-heading">Why you need a custom solution (not the default one)</h2>



<p>The default solution contains every component in your environment, including things you didn&#8217;t build yourself. That makes it useless for moving just your app to another environment.</p>



<p>A custom solution only contains what you explicitly add to it. This gives you a clean, predictable package that you control completely.</p>



<p>This matters most when you&#8217;re doing <strong>application lifecycle management (ALM)</strong> — the process of moving your work from development, to test, to production in a structured way.</p>



<p>If you&#8217;re still deciding between <strong>SharePoint</strong> and <strong>Dataverse</strong> as your data source, I&#8217;d recommend reading <a href="https://www.spguides.com/power-apps-dataverse-vs-sharepoint-list/">Power Apps Dataverse vs SharePoint list</a> first. Solutions are one of the biggest reasons teams choose Dataverse over SharePoint for serious business apps.</p>



<h2 class="wp-block-heading">Managed vs unmanaged solutions: what&#8217;s the difference?</h2>



<p>This is the concept that trips up almost every beginner, so let&#8217;s slow down here.</p>



<p><strong>Unmanaged solutions</strong> are open for editing. You can freely add, change, or remove components inside them. You build and develop inside unmanaged solutions.</p>



<p><strong>Managed solutions</strong> are locked packages meant for deployment. Once you import a managed solution into an environment, users can&#8217;t directly edit its components there.</p>



<p>Here&#8217;s the rule I follow on every single project: <strong>develop unmanaged, deploy managed.</strong></p>



<p>You build your Tickets table, your forms, and your canvas app inside an unmanaged solution in your development environment. When you&#8217;re ready to move it to test or production, you export that solution as managed, then import the managed version into the target environment.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>Pro Tip:</strong> In my experience, teams that skip this pattern and import unmanaged solutions straight into production almost always regret it. Someone eventually opens the solution in production and tweaks a column directly, and now your dev and production environments no longer match.</p>
</blockquote>



<h3 class="wp-block-heading">Why managed solutions protect production</h3>



<p>When you import a managed solution, its components become locked in that environment. People can still use the Tickets app and submit tickets normally.</p>



<p>But they can&#8217;t change the Status choice values, rename columns, or edit the form layout directly in production. That protection prevents accidental changes that could break the app for everyone.</p>



<p>Deleting a managed solution also removes all its components from that environment, which can mean data loss. Deleting an unmanaged solution only removes the container — the tables and data stay behind. Keep this difference in mind before you delete anything.</p>



<h2 class="wp-block-heading">Step 1: Create a custom publisher</h2>



<p>Before you create your first solution, you need a <strong>publisher</strong>. A publisher represents who built the solution and adds a prefix to every component you create.</p>



<p>Here&#8217;s how I set one up for the IT Helpdesk project:</p>



<ol class="wp-block-list">
<li>Go to <a href="https://make.powerapps.com" target="_blank" rel="noopener">make.powerapps.com</a> and select your environment.</li>



<li>Click <strong>Solutions</strong> in the left navigation.</li>



<li>Click <strong>New solution</strong>, then select <strong>New publisher</strong> from the Publisher dropdown.</li>



<li>Enter a <strong>Display name</strong> (for example, &#8220;Contoso IT Team&#8221;).</li>



<li>Enter a <strong>Name</strong> (no spaces, for example, &#8220;ContosoIT&#8221;).</li>



<li>Set a <strong>Prefix</strong> — a short code like &#8220;cit&#8221; that gets added before every table and column you create (for example, <code>cit_tickets</code>, <code>cit_priority</code>).</li>



<li>Click <strong>Save and Close</strong>.</li>
</ol>



<p>That prefix matters more than it looks. Every custom table, column, and choice you build gets this prefix automatically, so <code>cit_tickets</code> immediately tells anyone on the team that this table came from your Contoso IT publisher, not from another team&#8217;s solution.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>Pro Tip:</strong> Never use the default publisher for real projects. I once worked on an environment where three different teams had all used the default publisher, and their table prefixes were indistinguishable. Picking apart ownership took hours we didn&#8217;t have.</p>
</blockquote>



<h2 class="wp-block-heading">Step 2: Create the solution</h2>



<p>With the publisher ready, creating the actual solution takes less than a minute.</p>



<ol class="wp-block-list">
<li>Still in <strong>Solutions</strong>, click <strong>New solution</strong>.</li>



<li>Enter a <strong>Display name</strong>, like &#8220;IT Helpdesk Solution.&#8221;</li>



<li>Choose the publisher you just created from the dropdown.</li>



<li>Set a <strong>Version</strong> number, such as 1.0.0.0.</li>



<li>Click <strong>Create</strong>.</li>
</ol>



<p>Your solution now exists as an empty container, ready for components. Version numbers matter for tracking changes over time, so I always bump the version before every export I plan to move somewhere.</p>



<h2 class="wp-block-heading">Step 3: Add components to your solution</h2>



<p>Now it&#8217;s time to add the actual pieces of your <strong>IT Helpdesk</strong> app to the solution.</p>



<p>Inside your new solution, click <strong>Add existing</strong>, then choose the type of component you want to add:</p>



<ul class="wp-block-list">
<li><strong>Table</strong> — Select your Tickets table (or use <a href="https://www.spguides.com/dataverse-create-table-from-sharepoint-list/">create table from SharePoint list</a> if you&#8217;re migrating from SharePoint).</li>



<li><strong>Canvas app</strong> — Select the IT Helpdesk canvas app you built on top of the Tickets table.</li>



<li><strong>Cloud flow</strong> — Select any Power Automate flow that emails a technician when a new ticket is raised.</li>



<li><strong>Choice</strong> — Include any global choices you created, like Status or Priority, if they aren&#8217;t already part of the table.</li>
</ul>



<p>When you add the Tickets table, Dataverse asks if you want to include all subcomponents, like forms, views, and business rules. I usually add only what I&#8217;m actively customizing to keep the solution clean, following the &#8220;segmenting&#8221; principle — smaller, focused solutions are easier to manage than one giant solution with everything in it.</p>



<p>If your Tickets table needs a calculated field, this is a good point to also learn about the <a href="https://www.spguides.com/dataverse-formula-column/">Dataverse formula column</a>, which you can add straight into the same solution.</p>



<h3 class="wp-block-heading">Building the canvas app inside the solution</h3>



<p>Once your Tickets table is in the solution, you can build your canvas app directly from within it.</p>



<ol class="wp-block-list">
<li>Inside the solution, click <strong>New</strong> &gt; <strong>App</strong> &gt; <strong>Canvas app</strong>.</li>



<li>Choose <strong>Tablet format</strong>, then connect it to your Tickets table.</li>



<li>Add a <strong>Gallery</strong> control that shows all open tickets, sorted by DueDate.</li>



<li>Add a <strong>Form</strong> control set to New form mode for submitting new tickets.</li>
</ol>



<p>The Gallery pulls records straight from our sample Tickets data. Set the gallery&#8217;s Items property to a filter like this:</p>



<pre class="wp-block-code"><code>Filter(Tickets, Status.Value = "Open")</code></pre>



<p>Using our sample rows, this formula returns &#8220;VPN not connecting,&#8221; &#8220;New laptop request,&#8221; and &#8220;Printer offline&#8221; — every row where Status equals Open. The &#8220;Password reset needed&#8221; ticket, marked Resolved, correctly drops out of the view.</p>



<p>If you want a search box on top of that gallery, check out <a href="https://www.spguides.com/filter-power-apps-gallery-search-box/">filter Power Apps gallery search box</a> for the exact formula pattern.</p>



<p>To save a new ticket from the form back to Dataverse, use:</p>



<pre class="wp-block-code"><code>SubmitForm(NewTicketForm)</code></pre>



<p>This one line submits whatever the user filled into the form and creates a new row in the Tickets table, following the same column structure as our sample data. If you&#8217;d rather write directly to specific fields without a form control, look at <a href="https://www.spguides.com/patch-dataverse-number-field-in-power-apps/">Patch Dataverse number field in Power Apps</a> for a Patch-based approach instead.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>Pro Tip:</strong> Always build your canvas app from inside the solution, not from the general Power Apps home page. Apps built outside a solution get dropped into the default solution automatically, and you&#8217;ll have to manually move them back in later.</p>
</blockquote>



<h2 class="wp-block-heading">Step 4: Publish your customizations</h2>



<p>After adding or editing components, always click <strong>Publish all customizations</strong> before you test or export.</p>



<p>Dataverse holds your changes in a draft state until you publish them. Skipping this step is one of the most common reasons a canvas app doesn&#8217;t reflect a column you just added to the Tickets table.</p>



<p>I make this a habit: build a component, publish, test, then move to the next component. It catches errors early instead of letting five unpublished changes pile up.</p>



<h2 class="wp-block-heading">Step 5: Export the solution</h2>



<p>Once your IT Helpdesk solution is built and tested in your development environment, it&#8217;s time to export it.</p>



<ol class="wp-block-list">
<li>Open <strong>Solutions</strong>, and select your IT Helpdesk solution.</li>



<li>Click <strong>Export solution</strong>.</li>



<li>Choose whether to export it as <strong>Managed</strong> or <strong>Unmanaged</strong>.</li>



<li>Click <strong>Export</strong>, and Dataverse downloads a <code>.zip</code> file to your computer.</li>
</ol>



<p>For moving to test or production, always choose <strong>Managed</strong>. Keep your working copy unmanaged in development, and only export a managed copy when you&#8217;re ready to deploy.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>Pro Tip:</strong> I always bump the solution&#8217;s version number before exporting. A version history like 1.0.0.1, 1.0.0.2, and 1.0.0.3 tells me and my team exactly what shipped and when, which saves a lot of guesswork during troubleshooting later.</p>
</blockquote>



<h2 class="wp-block-heading">Step 6: Import the solution into another environment</h2>



<p>With your managed <code>.zip</code> file ready, importing it into your test or production environment is straightforward.</p>



<ol class="wp-block-list">
<li>Go to <a href="https://make.powerapps.com" target="_blank" rel="noopener">make.powerapps.com</a> and switch to the <strong>target environment</strong>.</li>



<li>Click <strong>Solutions</strong>, then <strong>Import solution</strong>.</li>



<li>Browse to your exported <code>.zip</code> file and select it.</li>



<li>Review any connection references or environment variables Dataverse asks you to configure.</li>



<li>Click <strong>Import</strong>.</li>
</ol>



<p>Dataverse validates the package, checks for missing dependencies, and installs the Tickets table, the canvas app, and the flow together, exactly as they existed in development.</p>



<p>If your solution includes a flow that connects to an external system, you&#8217;ll be asked to set up connections again in the new environment, since credentials don&#8217;t travel inside the solution package.</p>



<h2 class="wp-block-heading">Solution layering: how Dataverse handles multiple solutions</h2>



<p>Once you start deploying managed solutions regularly, it helps to understand <strong>solution layering</strong>.</p>



<p>Dataverse organizes solutions into two layers: the <strong>unmanaged layer</strong>, where all unmanaged solutions and customizations live, and the <strong>managed layer</strong>, where managed solutions and the core system solution live.</p>



<p>When you install a second managed solution that touches the same component as an earlier one, the newer managed solution sits on top and controls the current behavior. If you later uninstall it, the layer underneath takes over again.</p>



<p>This layering is exactly why Microsoft recommends keeping your solution structure simple, and why I avoid creating multiple overlapping solutions that touch the same tables. It adds a layer of complexity most small teams don&#8217;t need.</p>



<h2 class="wp-block-heading">Patching a solution instead of re-exporting everything</h2>



<p>If you only need to add one new column to the Tickets table, you don&#8217;t have to export the entire solution again. You can create a <strong>solution patch</strong>.</p>



<p>A patch is a smaller solution tied to your main solution&#8217;s version, containing only the new or changed components. It gets deployed on top of the original, keeping your deployment history clean and your packages small.</p>



<p>I use patches constantly for small tweaks like adding a new Choice value or fixing a form layout, saving the full solution re-export for bigger releases with several new components.</p>



<h2 class="wp-block-heading">Things to keep in mind with Dataverse solutions</h2>



<ul class="wp-block-list">
<li><strong>Always use a custom publisher.</strong> Never build on the default publisher or default solution — it makes ownership and prefixes impossible to track later.</li>



<li><strong>Segment your solutions.</strong> Only add the components you&#8217;re actively changing. Including everything &#8220;just in case&#8221; creates bloated, hard-to-manage solutions.</li>



<li><strong>Watch for missing dependencies on import.</strong> If your solution references a table or choice that doesn&#8217;t exist in the target environment, the import will fail until you fix the dependency chain.</li>



<li><strong>Reconfigure connection references after import.</strong> Flows and connectors don&#8217;t carry credentials across environments, so budget time to reset these after every deployment.</li>



<li><strong>Don&#8217;t edit managed solutions directly in production.</strong> If you need a change, make it in development, then re-export and re-import — resist the temptation to &#8220;quickly&#8221; unlock and edit it live.</li>



<li><strong>Version your solutions consistently.</strong> A clear version number scheme makes rollback and troubleshooting far easier when something breaks after a deployment.</li>
</ul>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-dominant-color="abc0c1" data-has-transparency="false" style="--dominant-color: #abc0c1;" fetchpriority="high" decoding="async" width="1024" height="576" sizes="(max-width: 1024px) 100vw, 1024px" src="https://www.spguides.com/wp-content/uploads/2026/09/Dataverse-Solution-1024x576.avif" alt="Dataverse Solution" class="wp-image-137448 not-transparent" title="Dataverse Solution" srcset="https://www.spguides.com/wp-content/uploads/2026/09/Dataverse-Solution-1024x576.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/09/Dataverse-Solution-300x169.avif 300w, https://www.spguides.com/wp-content/uploads/2026/09/Dataverse-Solution-768x432.avif 768w, https://www.spguides.com/wp-content/uploads/2026/09/Dataverse-Solution.avif 1280w" /></figure></div>


<h2 class="wp-block-heading">Frequently Asked Questions</h2>


<div id="rank-math-faq" class="rank-math-block">
<div class="rank-math-list ">
<div id="faq-question-1789280824410" class="rank-math-list-item">
<h3 class="rank-math-question ">What happens if I don&#8217;t create a solution in Dataverse?</h3>
<div class="rank-math-answer ">

<p>Your components land in the <strong>default solution</strong> automatically. This works fine for quick testing, but it makes it nearly impossible to cleanly move only your app and its related components to another environment later.</p>

</div>
</div>
<div id="faq-question-1789280833855" class="rank-math-list-item">
<h3 class="rank-math-question ">Can I convert an unmanaged solution to managed?</h3>
<div class="rank-math-answer ">

<p>Yes. When you export a solution, Dataverse gives you the choice between <strong>Managed</strong> and <strong>Unmanaged</strong> formats. Exporting as Managed creates a separate, locked package from the same unmanaged source, without changing your original development copy.</p>

</div>
</div>
<div id="faq-question-1789280845798" class="rank-math-list-item">
<h3 class="rank-math-question ">What is a connection reference in a Dataverse solution?</h3>
<div class="rank-math-answer ">

<p>A <strong>connection reference</strong> is a placeholder inside your solution that points to a specific connector, like Outlook or SharePoint, used by a flow or app. It lets the solution travel between environments without hardcoding one person&#8217;s login credentials into the package.</p>

</div>
</div>
<div id="faq-question-1789280866733" class="rank-math-list-item">
<h3 class="rank-math-question ">Why did my solution import fail with a missing dependency error?</h3>
<div class="rank-math-answer ">

<p>This usually means the solution references a table, choice, or component that doesn&#8217;t exist yet in the target environment. Check the error details for the specific component name, then either add it manually first or include it in your solution before re-exporting.</p>

</div>
</div>
<div id="faq-question-1789280879318" class="rank-math-list-item">
<h3 class="rank-math-question ">Can multiple people work in the same unmanaged solution at once?</h3>
<div class="rank-math-answer ">

<p>Yes, but I don&#8217;t recommend it without care. Two people editing the same table or form at the same time can overwrite each other&#8217;s changes, so coordinate who&#8217;s working on what, or split the work into separate patches.</p>

</div>
</div>
<div id="faq-question-1789280910414" class="rank-math-list-item">
<h3 class="rank-math-question ">How do I roll back a solution after a bad deployment?</h3>
<div class="rank-math-answer ">

<p>If you kept the previous managed solution version, you can reimport it as an upgrade to overwrite the newer version. This is another reason to always keep a copy of every managed solution <code>.zip</code> you&#8217;ve deployed, not just the latest one.</p>

</div>
</div>
</div>
</div>


<p>If there&#8217;s one sequence I want you to remember from this tutorial, it&#8217;s this: create a custom publisher first, build your Tickets table and canvas app inside a custom unmanaged solution, publish your changes, then export as managed for anything outside development.</p>



<p>Following that order in that exact sequence saves you from the exact mistake I made on my first project — building loose in the default solution and paying for it later. Build this habit early, and every future deployment will feel a lot less stressful.</p>



<p>You may also like the following tutorials:</p>



<ul class="wp-block-list">
<li><a href="https://www.spguides.com/what-is-microsoft-dataverse/">What is Microsoft Dataverse?</a></li>



<li><a href="https://www.spguides.com/power-apps-dataverse-vs-sharepoint-list/">Power Apps Dataverse vs SharePoint List</a></li>



<li><a href="https://www.spguides.com/migrate-sharepoint-online-list-to-dataverse/">Migrate SharePoint Online List to Dataverse</a></li>



<li><a href="https://www.spguides.com/dataverse-create-table-from-sharepoint-list/">Dataverse Create Table from SharePoint List</a></li>



<li><a href="https://www.spguides.com/preferred-solution-power-platform/">Preferred Solution in Power Platform</a></li>
</ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>What Is Microsoft Dataverse? A Complete Guide for Power Apps Beginners</title>
		<link>https://www.spguides.com/what-is-microsoft-dataverse/</link>
					<comments>https://www.spguides.com/what-is-microsoft-dataverse/#respond</comments>
		
		<dc:creator><![CDATA[Bijay Kumar]]></dc:creator>
		<pubDate>Sun, 13 Sep 2026 06:17:13 +0000</pubDate>
				<category><![CDATA[Power Apps]]></category>
		<category><![CDATA[What is Microsoft Dataverse]]></category>
		<guid isPermaLink="false">https://www.spguides.com/?p=42939</guid>

					<description><![CDATA[I still remember the first time a client asked me to move their entire operations off five different SharePoint lists and three Excel files. Every list had different column names for the same thing. Nothing talked to anything else. That&#8217;s the exact problem Microsoft Dataverse was built to solve. If you&#8217;ve been building Canvas apps ... <a title="What Is Microsoft Dataverse? A Complete Guide for Power Apps Beginners" class="read-more" href="https://www.spguides.com/what-is-microsoft-dataverse/" aria-label="Read more about What Is Microsoft Dataverse? A Complete Guide for Power Apps Beginners">read more...</a>]]></description>
										<content:encoded><![CDATA[
<p>I still remember the first time a client asked me to move their entire operations off five different SharePoint lists and three Excel files. Every list had different column names for the same thing. Nothing talked to anything else.</p>



<p>That&#8217;s the exact problem <strong>Microsoft Dataverse</strong> was built to solve.</p>



<p>If you&#8217;ve been building <strong>Canvas apps</strong> on <strong>SharePoint lists</strong> and you&#8217;re starting to feel the limits — slow galleries, messy relationships, weak security — Dataverse is probably the next tool you need to learn.</p>



<p>In this guide, I&#8217;ll walk you through what Dataverse actually is, how it&#8217;s structured, and how to create your first table using a real example.</p>



<h2 class="wp-block-heading">The Sample Data We&#8217;ll Use Throughout This Guide</h2>



<p>Let&#8217;s ground this in something real instead of abstract theory. Say you&#8217;re building an <strong>IT Service Desk app</strong> for internal support tickets.</p>



<p>In Dataverse, you&#8217;d create a <strong>table</strong> called <code>Tickets</code>. A table in Dataverse is the same idea as a list in SharePoint or a sheet in Excel — it&#8217;s where your records live.</p>



<p>Here are the columns (Dataverse calls fields &#8220;columns,&#8221; same as SharePoint):</p>



<ul class="wp-block-list">
<li><strong>Title</strong> (text) — short description of the issue</li>



<li><strong>Status</strong> (choice) — New, In Progress, Resolved</li>



<li><strong>Priority</strong> (choice) — Low, Medium, High</li>



<li><strong>Due Date</strong> (date and time)</li>



<li><strong>Assigned To</strong> (lookup to the Users table)</li>



<li><strong>Category</strong> (choice) — Hardware, Software, Network, Access</li>
</ul>



<p>Here&#8217;s what a handful of sample rows would look like:</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>Title</th><th>Status</th><th>Priority</th><th>Due Date</th><th>Assigned To</th><th>Category</th></tr></thead><tbody><tr><td>VPN not connecting</td><td>In Progress</td><td>High</td><td>2026-09-15</td><td>Priya Nair</td><td>Network</td></tr><tr><td>Laptop screen flickering</td><td>New</td><td>Medium</td><td>2026-09-18</td><td>David Chen</td><td>Hardware</td></tr><tr><td>Need Excel license</td><td>New</td><td>Low</td><td>2026-09-20</td><td>Priya Nair</td><td>Software</td></tr><tr><td>Printer offline (3rd floor)</td><td>Resolved</td><td>Medium</td><td>2026-09-10</td><td>Sam Okafor</td><td>Hardware</td></tr><tr><td>Password reset request</td><td>New</td><td>High</td><td>2026-09-14</td><td>David Chen</td><td>Access</td></tr><tr><td>Outlook crashing on open</td><td>In Progress</td><td>Medium</td><td>2026-09-16</td><td>Sam Okafor</td><td>Software</td></tr></tbody></table></figure>



<p>I&#8217;ll keep referring back to this <code>Tickets</code> table as we go, so every concept has a real anchor instead of floating in theory.</p>



<h2 class="wp-block-heading">What Is Microsoft Dataverse, Exactly?</h2>



<p><strong>Microsoft Dataverse</strong> is a cloud-based data platform that sits underneath the entire <strong>Power Platform</strong> — Power Apps, Power Automate, Power BI, and Copilot Studio.</p>



<p>Think of it as a secure, structured database that&#8217;s already built for business applications. You don&#8217;t manage servers, write SQL to set it up, or worry about backups.</p>



<p>Microsoft describes it as the data platform that lets you securely store and manage the data used by your business applications. That&#8217;s the whole point — one place for your data, many apps built on top of it.</p>



<p>When you build a <strong>Canvas app</strong> or a <strong>Model-driven app</strong> in Power Apps, Dataverse is usually the recommended data source, especially once your app grows past a simple list.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>Pro Tip:</strong> In my experience, teams often start on SharePoint because it&#8217;s free and familiar, then hit a wall around 5,000–10,000 items or when they need real relationships between lists. That&#8217;s usually the trigger to move to Dataverse.</p>
</blockquote>



<h2 class="wp-block-heading">Why Not Just Use SharePoint or Excel?</h2>



<p>This is the question I get most from clients. SharePoint and Excel work fine for simple tracking. They fall apart when your app needs to scale or handle real business logic.</p>



<p>SharePoint lists don&#8217;t enforce strict data relationships. You can build lookup columns, but they&#8217;re fragile and break easily during migrations.</p>



<p>Excel has no real concurrency control. Two people editing the same file at once is a recipe for lost data.</p>



<p>Dataverse was designed from the ground up to avoid these problems. It has proper relational tables, row-level security, and built-in business logic — not workarounds bolted onto a document library.</p>



<p>I&#8217;ve written a full breakdown comparing the two if you want the detailed trade-offs: <a href="https://www.spguides.com/power-apps-dataverse-vs-sharepoint-list/">Power Apps Dataverse vs SharePoint List</a>.</p>



<h2 class="wp-block-heading">The Core Building Blocks of Dataverse</h2>



<p>Dataverse has its own vocabulary. Once these five concepts click, the rest of the platform makes a lot more sense.</p>



<h3 class="wp-block-heading">Tables</h3>



<p>A <strong>table</strong> is a collection of rows and columns, just like our <code>Tickets</code> example above. Dataverse ships with standard tables (Account, Contact, User) and lets you create custom tables for your own business needs.</p>



<p>If you&#8217;re bringing over an existing list, this guide walks through the exact process: <a href="https://www.spguides.com/migrate-sharepoint-online-list-to-dataverse/">Migrate a SharePoint Online List to Dataverse</a>.</p>



<h3 class="wp-block-heading">Columns</h3>



<p><strong>Columns</strong> define what kind of data each field holds — text, number, date, choice, lookup, image, and more. Every table needs a <strong>primary name column</strong>, which is the main identifier shown in lookups and views.</p>



<p>For our <code>Tickets</code> table, that primary column would be <code>Title</code>. I&#8217;ve got a separate walkthrough on customizing this behavior here: <a href="https://www.spguides.com/dataverse-primary-name-column-autonumber/">Dataverse Primary Name Column Autonumber</a>.</p>



<h3 class="wp-block-heading">Choices</h3>



<p><strong>Choice columns</strong> are Dataverse&#8217;s version of SharePoint&#8217;s choice field — a dropdown with a fixed set of options. Our <code>Status</code> and <code>Priority</code> columns are both choices.</p>



<p>The difference is that Dataverse choices can be reused across multiple tables, which SharePoint choice columns can&#8217;t do easily. See <a href="https://www.spguides.com/power-apps-dataverse-choices/">Power Apps Dataverse Choices</a> for the full setup.</p>



<h3 class="wp-block-heading">Relationships</h3>



<p>This is where Dataverse really pulls ahead of SharePoint. <strong>Table relationships</strong> let you connect tables properly — one ticket to one assigned user, one user to many tickets.</p>



<p>Our <code>Assigned To</code> column is a <strong>lookup</strong>, which creates a relationship between <code>Tickets</code> and the <code>Users</code> table. That relationship stays intact even as data grows into the millions of rows.</p>



<h3 class="wp-block-heading">Business Rules and Formula Columns</h3>



<p><strong>Business rules</strong> let you apply logic without writing code — for example, automatically setting <code>Priority</code> to High whenever <code>Category</code> is &#8220;Network.&#8221; <strong>Formula columns</strong> calculate values automatically, similar to an Excel formula but stored right in the table.</p>



<p>If you want to see formula columns in action, check out <a href="https://www.spguides.com/dataverse-formula-column/">Dataverse Formula Column</a>.</p>



<h2 class="wp-block-heading">How Dataverse Connects to the Rest of the Power Platform</h2>



<p>The real value of Dataverse isn&#8217;t the table itself — it&#8217;s how many tools can plug into the same data.</p>



<p>Your <strong>Canvas app</strong> can read and write to the <code>Tickets</code> table directly using <strong>Power Fx</strong>, the formula language behind Power Apps. Here&#8217;s a simple example of adding a new ticket with Patch:</p>



<pre class="wp-block-code"><code>Patch(
    Tickets,
    Defaults(Tickets),
    {
        Title: TextInputTitle.Text,
        Status: 'Status (Tickets)'.New,
        Priority: 'Priority (Tickets)'.Medium,
        Category: 'Category (Tickets)'.Software
    }
)</code></pre>



<p>Notice the choice values are written as <code>'Status (Tickets)'.New</code> instead of plain text. That&#8217;s because Dataverse choices are strongly typed — the app needs to reference the exact choice set name.</p>



<p>If you&#8217;re building the read side of this, this tutorial covers it well: <a href="https://www.spguides.com/get-data-from-dataverse-power-apps/">Get Data from Dataverse in Power Apps</a>.</p>



<p><strong>Power Automate</strong> flows can watch the same <code>Tickets</code> table and trigger notifications the moment a new high-priority ticket lands. See <a href="https://www.spguides.com/power-automate-dataverse-add-new-row/">Power Automate Dataverse Add New Row</a> for that trigger pattern.</p>



<p><strong>Power BI</strong> connects natively too, so you can build a live dashboard of open tickets by category without exporting anything. I cover that connection here: <a href="https://www.spguides.com/dataverse-power-bi/">Dataverse Power BI</a>.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>Pro Tip:</strong> I always tell junior developers — build the table structure right the first time. Every downstream tool (flows, dashboards, apps) inherits whatever structure you define in Dataverse, so fixing it later means touching everything connected to it.</p>
</blockquote>



<h2 class="wp-block-heading">Environments and Security in Dataverse</h2>



<p>Every Dataverse database lives inside an <strong>environment</strong> — a container that groups your apps, flows, and data for a specific purpose (like Development, Test, or Production).</p>



<p>This matters because it lets you test changes safely before pushing them to the environment your whole team uses.</p>



<p>Security in Dataverse works through <strong>security roles</strong>, which control exactly what each user can see and do — down to the individual column level if needed. This is far more granular than SharePoint&#8217;s site permissions.</p>



<p>For example, you could let support agents view and edit <code>Tickets</code>, but only let managers change the <code>Priority</code> field. That kind of fine-grained control just isn&#8217;t possible with a standard SharePoint list.</p>



<h2 class="wp-block-heading">How to Create Your First Dataverse Table</h2>



<p>Let&#8217;s actually build the <code>Tickets</code> table so this isn&#8217;t just theory.</p>



<ol class="wp-block-list">
<li><strong>Open Power Apps</strong> and go to <strong>Tables</strong> under the <strong>Dataverse</strong> section in the left navigation.</li>



<li><strong>Click &#8220;New table&#8221;</strong> and choose &#8220;Standard&#8221; so you get built-in system columns like Created On and Modified By.</li>



<li><strong>Name your table</strong> <code>Tickets</code> and give it a clear description. Dataverse will auto-generate the primary column for you.</li>



<li><strong>Add your columns</strong> one by one — Status, Priority, Due Date, Category, and the Assigned To lookup — matching data types to what your data actually needs.</li>



<li><strong>Save and create a view</strong> so you can see your data in a grid immediately.</li>
</ol>



<p>For a step-by-step version of this with screenshots-level detail, see <a href="https://www.spguides.com/dataverse-create-table/">Dataverse Create Table</a>. And once your table exists, you&#8217;ll want a filtered view for your app — <a href="https://www.spguides.com/dataverse-create-view/">Dataverse Create View</a> covers exactly that.</p>



<p>If you&#8217;d rather bring in existing data instead of starting from scratch, both of these are common paths:</p>



<ul class="wp-block-list">
<li><a href="https://www.spguides.com/dataverse-create-table-from-sharepoint-list/">Dataverse Create Table from SharePoint List</a></li>



<li><a href="https://www.spguides.com/power-apps-create-dataverse-table-from-excel/">Power Apps Create Dataverse Table from Excel</a></li>
</ul>



<h2 class="wp-block-heading">Dataverse vs SharePoint: A Quick Gut-Check</h2>



<p>I&#8217;m not going to repeat the full comparison here since I&#8217;ve already linked the dedicated article above, but here&#8217;s my honest, condensed take.</p>



<p>Choose <strong>SharePoint</strong> when your app is simple, your team is small, and you don&#8217;t need complex relationships or row-level security.</p>



<p>Choose <strong>Dataverse</strong> when you&#8217;re building something that needs to scale, needs proper security roles, or needs to connect cleanly to Power BI and multiple apps at once.</p>



<p>Our <code>Tickets</code> example genuinely fits Dataverse well — support tickets grow fast, need relationships to users, and often need reporting.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-dominant-color="b6ccca" data-has-transparency="false" style="--dominant-color: #b6ccca;" decoding="async" width="1024" height="576" sizes="(max-width: 1024px) 100vw, 1024px" src="https://www.spguides.com/wp-content/uploads/2026/09/What-Is-Microsoft-Dataverse-1024x576.avif" alt="What Is Microsoft Dataverse" class="wp-image-137446 not-transparent" title="What Is Microsoft Dataverse" srcset="https://www.spguides.com/wp-content/uploads/2026/09/What-Is-Microsoft-Dataverse-1024x576.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/09/What-Is-Microsoft-Dataverse-300x169.avif 300w, https://www.spguides.com/wp-content/uploads/2026/09/What-Is-Microsoft-Dataverse-768x432.avif 768w, https://www.spguides.com/wp-content/uploads/2026/09/What-Is-Microsoft-Dataverse.avif 1280w" /></figure></div>


<h2 class="wp-block-heading">Things to Keep in Mind with Dataverse</h2>



<ul class="wp-block-list">
<li><strong>Licensing isn&#8217;t free like SharePoint.</strong> Dataverse typically requires a Power Apps per-app or per-user license, so budget for this before committing a client to it.</li>



<li><strong>Choice sets are shared across tables by default.</strong> Renaming a global choice can quietly affect every table using it — always check dependencies first.</li>



<li><strong>Solutions matter for real projects.</strong> Don&#8217;t build tables directly in your default environment for production work. Use a proper <a href="https://www.spguides.com/dataverse-solution/">Dataverse Solution</a> so you can move changes cleanly between environments.</li>



<li><strong>Delegation limits still apply in Canvas apps.</strong> Even with Dataverse&#8217;s better performance, formulas like <code>Filter</code> and <code>Search</code> still need to be delegable, or you&#8217;ll only work against a subset of your data.</li>



<li><strong>Security roles take planning time.</strong> Don&#8217;t skip this step to save time early — retrofitting security roles onto live data is far more painful than designing them upfront.</li>



<li><strong>Version history has a retention window.</strong> Dataverse keeps <a href="https://www.spguides.com/dataverse-version-history/">version history</a> for auditing, but it&#8217;s not a substitute for a real backup strategy.</li>
</ul>



<h2 class="wp-block-heading">Frequently Asked Questions</h2>


<div id="rank-math-faq" class="rank-math-block">
<div class="rank-math-list ">
<div id="faq-question-1789280053830" class="rank-math-list-item">
<h3 class="rank-math-question ">Is Microsoft Dataverse free to use?</h3>
<div class="rank-math-answer ">

<p>No, Dataverse itself requires a <strong>Power Apps license</strong> — either per-app or per-user — beyond the free tier. Some Microsoft 365 licenses include limited Dataverse capacity, but production apps usually need a dedicated license.</p>

</div>
</div>
<div id="faq-question-1789280062456" class="rank-math-list-item">
<h3 class="rank-math-question ">Can I use Dataverse with SharePoint at the same time?</h3>
<div class="rank-math-answer ">

<p>Yes. Many real projects use both — Dataverse for core structured business data, and SharePoint for document storage and collaboration. You can even connect a SharePoint document library to a Dataverse table for file attachments.</p>

</div>
</div>
<div id="faq-question-1789280077944" class="rank-math-list-item">
<h3 class="rank-math-question ">What&#8217;s the difference between a Dataverse table and a SharePoint list?</h3>
<div class="rank-math-answer ">

<p>A Dataverse table supports proper relational data, row-level security, and business logic like rules and formula columns. A SharePoint list is simpler and free but lacks that same depth of structure and security control.</p>

</div>
</div>
<div id="faq-question-1789280087335" class="rank-math-list-item">
<h3 class="rank-math-question ">Do I need to know SQL to use Dataverse?</h3>
<div class="rank-math-answer ">

<p>No. Dataverse is designed for <strong>low-code</strong> development through Power Apps, Power Automate, and Power Fx. Developers can go deeper with the Dataverse Web API and code, but it&#8217;s not required for typical business apps.</p>

</div>
</div>
<div id="faq-question-1789280096786" class="rank-math-list-item">
<h3 class="rank-math-question ">How much data can Dataverse actually hold?</h3>
<div class="rank-math-answer ">

<p>Dataverse is built for enterprise scale, comfortably handling millions of rows per table, far beyond what SharePoint lists can manage before performance and delegation issues appear.</p>

</div>
</div>
<div id="faq-question-1789280113312" class="rank-math-list-item">
<h3 class="rank-math-question ">Can I import my existing Excel data into Dataverse?</h3>
<div class="rank-math-answer ">

<p>Yes, Dataverse supports importing directly from Excel files or through dataflows for more complex transformations. This is a common first step when moving off spreadsheet-based tracking.</p>

</div>
</div>
</div>
</div>


<p>If there&#8217;s one thing I want you to take from this article, it&#8217;s that Dataverse isn&#8217;t just &#8220;SharePoint but fancier.&#8221; Start by learning tables and columns, then relationships, then security roles — in that order.</p>



<p>That sequence matters because each layer builds on the last, and skipping straight to security roles before understanding your table structure almost always means redoing work later. Good luck building your first table.</p>



<p>You May Also Like the Following Tutorials</p>



<ul class="wp-block-list">
<li><a href="https://www.spguides.com/power-apps-dataverse-vs-sharepoint-list/">Power Apps Dataverse vs SharePoint List</a></li>



<li><a href="https://www.spguides.com/migrate-sharepoint-online-list-to-dataverse/">Migrate a SharePoint Online List to Dataverse</a></li>



<li><a href="https://www.spguides.com/standard-vs-premium-connectors-in-power-apps/">Standard vs Premium Connectors in Power Apps</a></li>



<li><a href="https://www.spguides.com/power-apps-dataverse-choices/">Power Apps Dataverse Choices</a></li>



<li><a href="https://www.spguides.com/dataverse-solution/">Dataverse Solution</a></li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://www.spguides.com/what-is-microsoft-dataverse/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>How to Configure Agent Instructions &#038; Conversation Starters in Copilot Studio</title>
		<link>https://www.spguides.com/configure-agent-instructions-and-conversation-starters-copilot-studio/</link>
		
		<dc:creator><![CDATA[Bijay Kumar]]></dc:creator>
		<pubDate>Sat, 12 Sep 2026 12:00:00 +0000</pubDate>
				<category><![CDATA[Microsoft Copilot Studio]]></category>
		<category><![CDATA[Configure Agent Instructions & Conversation Starters in Copilot Studio]]></category>
		<guid isPermaLink="false">https://www.spguides.com/?p=137412</guid>

					<description><![CDATA[When I started building an Employee HR Assistant in Microsoft Copilot Studio, I noticed something very quickly. Adding company documents as knowledge was not enough. Employees asked simple questions like, “How many annual leave days do I get?”, “Can I work from home?”, and “How do I submit an expense claim?” The agent had the ... <a title="How to Configure Agent Instructions &#038; Conversation Starters in Copilot Studio" class="read-more" href="https://www.spguides.com/configure-agent-instructions-and-conversation-starters-copilot-studio/" aria-label="Read more about How to Configure Agent Instructions &#038; Conversation Starters in Copilot Studio">read more...</a>]]></description>
										<content:encoded><![CDATA[
<p>When I started building an <strong>Employee HR Assistant in Microsoft Copilot Studio</strong>, I noticed something very quickly. Adding company documents as knowledge was not enough.</p>



<p>Employees asked simple questions like, “How many annual leave days do I get?”, “Can I work from home?”, and “How do I submit an expense claim?” The agent had the information, but I still needed to control how it answered those questions.</p>



<p>That is where <strong>Agent Instructions</strong> and <strong>Conversation Starters</strong> became important. In this guide, I will show you how I configure Agent Instructions in Microsoft Copilot Studio, add an HR policy PDF as knowledge, create useful conversation starters, test the agent, and publish it for users.</p>



<h2 class="wp-block-heading">What Are Agent Instructions in Microsoft Copilot Studio?</h2>



<p>I think of <strong>Agent Instructions</strong> as rules I give to my agent.</p>



<p>Knowledge gives the agent information, but instructions tell the agent how I want it to use that information.</p>



<p>For example, I may want my HR assistant to:</p>



<ul class="wp-block-list">
<li>Answer only HR-related questions.</li>



<li>Use simple and professional language.</li>



<li>Give short answers unless the user asks for details.</li>



<li>Give numbered steps when explaining a process.</li>



<li>Never make up company policies.</li>



<li>Tell employees when information is unavailable.</li>



<li>Ask a question when the employee&#8217;s request is unclear.</li>



<li>Avoid sharing confidential employee information.</li>
</ul>



<p>Without clear instructions, the agent may still answer questions, but I have less control over its behavior.</p>



<p>The easiest way I remember these concepts is:</p>



<ul class="wp-block-list">
<li><strong>Knowledge = What my agent knows</strong></li>



<li><strong>Instructions = How my agent behaves</strong></li>



<li><strong>Conversation Starters = What I help users ask</strong></li>
</ul>



<p>If you are completely new to Copilot Studio, you can first learn how to <a href="https://www.spguides.com/create-agent-using-microsoft-copilot-studio/">create an agent using Microsoft Copilot Studio</a>.</p>



<h2 class="wp-block-heading">My Example: Employee HR Assistant</h2>



<p>For this tutorial, I am using an <strong>Employee HR Assistant</strong>. I like this example because it is close to a real business requirement.</p>



<p>Imagine I am working with a company that has around 200 employees. The HR team gets the same questions every week.</p>



<p>Employees ask things like:</p>



<ul class="wp-block-list">
<li>How many annual leave days do I get?</li>



<li>What is the sick leave policy?</li>



<li>Can I work from home?</li>



<li>What are the standard working hours?</li>



<li>How do I submit an expense claim?</li>



<li>What are the company holidays?</li>



<li>How can I contact HR?</li>
</ul>



<p>Instead of asking HR to answer these basic questions every time, I can create an HR assistant using <strong>Microsoft Copilot Studio</strong>.</p>



<p>The assistant will answer questions based on company HR information.</p>



<p>I can also use this <a href="https://www.spguides.com/build-and-deploy-smart-hr-assistant-bot-using-copilot-studio/">Smart HR Assistant using Copilot Studio</a> example if I want to explore a complete HR chatbot scenario later.</p>



<h2 class="wp-block-heading">Create the Employee HR Assistant</h2>



<p>First, I open <a href="https://copilotstudio.preview.microsoft.com/" data-type="link" data-id="https://copilotstudio.preview.microsoft.com/" target="_blank" rel="noopener">Microsoft Copilot Studio</a> and create a new agent.</p>



<p>For my example, I use:</p>



<p><strong>Name:</strong> Employee HR Assistant</p>



<p>For the description, I enter:</p>



<blockquote class="wp-block-quote has-contrast-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p class="has-base-2-background-color has-background">An AI assistant that helps employees find information about company HR policies, leave, work from home, holidays, expenses, and common HR questions.</p>
</blockquote>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="fafafa" data-has-transparency="false" style="--dominant-color: #fafafa;" decoding="async" width="957" height="336" sizes="(max-width: 957px) 100vw, 957px" src="https://www.spguides.com/wp-content/uploads/2026/09/Microsoft-Copilot-Studio-Agent-Instructions.avif" alt="Microsoft Copilot Studio Agent Instructions" class="wp-image-137413 not-transparent" title="Microsoft Copilot Studio Agent Instructions" srcset="https://www.spguides.com/wp-content/uploads/2026/09/Microsoft-Copilot-Studio-Agent-Instructions.avif 957w, https://www.spguides.com/wp-content/uploads/2026/09/Microsoft-Copilot-Studio-Agent-Instructions-300x105.avif 300w, https://www.spguides.com/wp-content/uploads/2026/09/Microsoft-Copilot-Studio-Agent-Instructions-768x270.avif 768w" /></figure></div>


<p>This description gives Copilot Studio a basic idea about what I want to build. However, I do not depend only on the description.</p>



<p>After creating the agent, I configure its <strong>Knowledge</strong>, <strong>Instructions</strong>, and <strong>Conversation Starters</strong> separately.</p>



<p>This gives me better control over the final experience.</p>



<h2 class="wp-block-heading">Add an HR Policy PDF as Knowledge</h2>



<p>The next thing I do is give my agent company-specific information.</p>



<p>For this example, I created an <strong>Employee HR Policy Handbook PDF</strong>.</p>



<p>My document contains information about:</p>



<ul class="wp-block-list">
<li>Annual leave</li>



<li>Sick leave</li>



<li>Casual leave</li>



<li>Work-from-home policy</li>



<li>Working hours</li>



<li>Company holidays</li>



<li>Expense reimbursement</li>



<li>Employee benefits</li>



<li>HR support</li>
</ul>



<p>Inside my agent, I go to <strong>Knowledge</strong> and add the HR Policy Handbook.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-dominant-color="f5f8f9" data-has-transparency="false" style="--dominant-color: #f5f8f9;" loading="lazy" decoding="async" width="1024" height="245" sizes="(max-width: 1024px) 100vw, 1024px" src="https://www.spguides.com/wp-content/uploads/2026/09/How-to-Configure-Agent-Instructions-in-Microsoft-Copilot-Studio-1024x245.avif" alt="How to Configure Agent Instructions in Microsoft Copilot Studio" class="wp-image-137414 not-transparent" title="How to Configure Agent Instructions in Microsoft Copilot Studio" srcset="https://www.spguides.com/wp-content/uploads/2026/09/How-to-Configure-Agent-Instructions-in-Microsoft-Copilot-Studio-1024x245.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/09/How-to-Configure-Agent-Instructions-in-Microsoft-Copilot-Studio-300x72.avif 300w, https://www.spguides.com/wp-content/uploads/2026/09/How-to-Configure-Agent-Instructions-in-Microsoft-Copilot-Studio-768x184.avif 768w, https://www.spguides.com/wp-content/uploads/2026/09/How-to-Configure-Agent-Instructions-in-Microsoft-Copilot-Studio.avif 1198w" /></figure></div>


<p>After Copilot Studio processes the document, my agent can use that information when employees ask HR-related questions.</p>



<p>For example, if my PDF says:</p>



<blockquote class="wp-block-quote has-contrast-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p class="has-base-2-background-color has-background">Full-time employees receive 18 working days of annual leave per calendar year.</p>
</blockquote>



<p>Then an employee can ask:</p>



<blockquote class="wp-block-quote has-base-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p>How many annual leave days do I get?</p>
</blockquote>



<p>The agent can use the HR Policy Handbook to answer that question.</p>



<p>This is why I say:</p>



<p><strong>Knowledge tells the agent what it knows.</strong></p>



<h3 class="wp-block-heading">PDF Knowledge vs SharePoint Knowledge</h3>



<p>A PDF works very well for my demo because it is simple.</p>



<p>But in a real company, HR policies may already be stored in <strong>SharePoint</strong>.</p>



<p>In that case, I may use SharePoint instead of manually maintaining several PDF files.</p>



<p>For example, I can use a <a href="https://www.spguides.com/sharepoint-list-as-knowledge-in-copilot-studio/">SharePoint list as knowledge in Copilot Studio</a> when my information is stored in a structured list.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-dominant-color="afc2d6" data-has-transparency="false" style="--dominant-color: #afc2d6;" loading="lazy" decoding="async" width="1024" height="576" sizes="(max-width: 1024px) 100vw, 1024px" src="https://www.spguides.com/wp-content/uploads/2026/09/Microsoft-Copilot-Studio-Conversation-Starters-1024x576.avif" alt="Microsoft Copilot Studio Conversation Starters" class="wp-image-137415 not-transparent" title="Microsoft Copilot Studio Conversation Starters" srcset="https://www.spguides.com/wp-content/uploads/2026/09/Microsoft-Copilot-Studio-Conversation-Starters-1024x576.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/09/Microsoft-Copilot-Studio-Conversation-Starters-300x169.avif 300w, https://www.spguides.com/wp-content/uploads/2026/09/Microsoft-Copilot-Studio-Conversation-Starters-768x432.avif 768w, https://www.spguides.com/wp-content/uploads/2026/09/Microsoft-Copilot-Studio-Conversation-Starters-1536x864.avif 1536w, https://www.spguides.com/wp-content/uploads/2026/09/Microsoft-Copilot-Studio-Conversation-Starters.avif 1672w" /></figure></div>


<p>I usually choose the source based on where the business already maintains its information.</p>



<p>If HR already updates policies in SharePoint, I do not want them to maintain another copy only for the agent.</p>



<h2 class="wp-block-heading">Why Knowledge Alone Is Not Enough</h2>



<p>This is something I always explain when I show Copilot Studio to beginners.</p>



<p>Suppose I upload the HR Policy Handbook.</p>



<p>Now my agent knows about annual leave, working hours, expenses, and other HR policies.</p>



<p>But the document does not tell my agent:</p>



<ul class="wp-block-list">
<li>How long its answers should be.</li>



<li>Whether answers should be formal or friendly.</li>



<li>What to do when the answer is missing.</li>



<li>Whether it should make assumptions.</li>



<li>How it should answer process-related questions.</li>



<li>What to do when someone asks a non-HR question.</li>
</ul>



<p>That is why I configure <strong>Agent Instructions</strong>.</p>



<p>The HR document contains the business information.</p>



<p>My instructions define the behavior.</p>



<p>Keeping these two things separate also makes the solution easier for me to manage later.</p>



<p>If HR changes the annual leave policy, I update the knowledge source.</p>



<p>I do not need to rewrite the agent&#8217;s behavior rules.</p>



<h2 class="wp-block-heading">How I Configure Agent Instructions in Copilot Studio</h2>



<p>Now I open my agent&#8217;s <strong>Instructions</strong> section.</p>



<p>I may already see some instructions created when I first created the agent.</p>



<p>I review them carefully.</p>



<p>I do not recommend using an instruction like this:</p>



<blockquote class="wp-block-quote has-base-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p>You are an HR assistant. Answer HR questions.</p>
</blockquote>



<p>It is too basic.</p>



<p>It tells the agent what its role is, but it does not tell the agent enough about how I want it to behave.</p>



<p>For my Employee HR Assistant, I use something like this:</p>



<pre class="wp-block-code"><code>You are the Employee HR Assistant.

Your primary responsibility is to help employees understand company HR policies and procedures.

Answer questions related to leave policies, work-from-home policies, company holidays, working hours, expense reimbursement, employee benefits, and general HR procedures.

Use the configured HR knowledge sources when answering company policy-related questions.

Do not invent company policies, leave allowances, reimbursement limits, dates, or HR rules.

If the requested information is not available in the configured knowledge sources, clearly tell the employee that you could not find the information and recommend contacting HR.

Use simple and professional language.

Keep answers concise unless the employee asks for more details.

When explaining a process, provide numbered step-by-step instructions.

If the employee's question is unclear, ask a clarifying question before providing an answer.

Do not provide legal, medical, or financial advice.

Never expose confidential information belonging to another employee.

If the employee asks something unrelated to HR, explain that you are an HR assistant and ask them to provide an HR-related question.
</code></pre>



<p>I like this format because every instruction has a clear purpose.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f7f8f8" data-has-transparency="false" style="--dominant-color: #f7f8f8;" loading="lazy" decoding="async" width="956" height="521" sizes="(max-width: 956px) 100vw, 956px" src="https://www.spguides.com/wp-content/uploads/2026/09/How-to-Configure-Conversation-Starters-in-Copilot-Studio.avif" alt="How to Configure Conversation Starters in Copilot Studio" class="wp-image-137416 not-transparent" title="How to Configure Conversation Starters in Copilot Studio" srcset="https://www.spguides.com/wp-content/uploads/2026/09/How-to-Configure-Conversation-Starters-in-Copilot-Studio.avif 956w, https://www.spguides.com/wp-content/uploads/2026/09/How-to-Configure-Conversation-Starters-in-Copilot-Studio-300x163.avif 300w, https://www.spguides.com/wp-content/uploads/2026/09/How-to-Configure-Conversation-Starters-in-Copilot-Studio-768x419.avif 768w" /></figure></div>


<h2 class="wp-block-heading">How I Structure My Agent Instructions</h2>



<p>When I write instructions, I normally break them into a few simple areas.</p>



<h3 class="wp-block-heading">1. Define the Agent&#8217;s Role</h3>



<p>First, I tell the agent what it is.</p>



<p>For example:</p>



<blockquote class="wp-block-quote has-base-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p>You are the Employee HR Assistant for Contoso.</p>
</blockquote>



<p>This gives the agent a clear identity.</p>



<h3 class="wp-block-heading">2. Define What the Agent Should Answer</h3>



<p>Next, I define its scope.</p>



<p>For example:</p>



<blockquote class="wp-block-quote has-base-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p>Answer questions related to leave, work from home, working hours, company holidays, expenses, benefits, and HR procedures.</p>
</blockquote>



<p>I do this because I do not want my HR assistant answering every possible question.</p>



<p>If somebody asks:</p>



<blockquote class="wp-block-quote has-base-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p>Write JavaScript code for me.</p>
</blockquote>



<p>that request has nothing to do with my HR agent.</p>



<h3 class="wp-block-heading">3. Tell the Agent Which Information to Trust</h3>



<p>I also tell it:</p>



<blockquote class="wp-block-quote has-base-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p>Use the configured HR knowledge sources when answering company policy-related questions.</p>
</blockquote>



<p>This is especially important for company-specific information.</p>



<p>For example, I do not want the agent using a general answer about how many annual leave days employees normally receive.</p>



<p>I want it to use my company&#8217;s policy.</p>



<h3 class="wp-block-heading">4. Tell the Agent Not to Guess</h3>



<p>This is one of the most important instructions I use.</p>



<p>I tell the agent:</p>



<blockquote class="wp-block-quote has-base-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p>Do not invent company policies, leave allowances, reimbursement limits, dates, or HR rules.</p>
</blockquote>



<p>If my HR document does not contain a maternity leave policy, I would rather see:</p>



<blockquote class="wp-block-quote has-base-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p>I could not find this information in the available HR resources. Please contact HR.</p>
</blockquote>



<p>That is much better than showing an employee a made-up company rule.</p>



<h3 class="wp-block-heading">5. Define the Response Style</h3>



<p>I also tell the agent how I want its answers to look.</p>



<p>For example:</p>



<blockquote class="wp-block-quote has-base-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p>Use simple and professional language.</p>
</blockquote>



<p>and:</p>



<blockquote class="wp-block-quote has-base-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p>Keep answers short unless the employee asks for more details.</p>
</blockquote>



<p>I have found this useful because employees normally want quick answers.</p>



<p>They usually do not want five long paragraphs for a simple question like:</p>



<blockquote class="wp-block-quote has-base-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p>What are our working hours?</p>
</blockquote>



<h3 class="wp-block-heading">6. Tell the Agent How to Explain Processes</h3>



<p>For process-related questions, I use:</p>



<blockquote class="wp-block-quote has-base-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p>When explaining a process, provide numbered step-by-step instructions.</p>
</blockquote>



<p>This works well for questions such as:</p>



<blockquote class="wp-block-quote has-base-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p>How do I apply for annual leave?</p>
</blockquote>



<p>or:</p>



<blockquote class="wp-block-quote has-base-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p>How do I submit an expense reimbursement claim?</p>
</blockquote>



<p>Instead of one large paragraph, I want the answer to look more like:</p>



<ol class="wp-block-list">
<li>Open the employee portal.</li>



<li>Select the leave request option.</li>



<li>Choose the leave type.</li>



<li>Enter the start and end dates.</li>



<li>Submit the request.</li>
</ol>



<p>That is much easier for the employee to follow.</p>



<blockquote class="wp-block-quote has-base-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>Pro Tip:</strong> I&#8217;ve found that simple instructions usually work better than a large block of complicated rules. I tell the agent exactly what I expect and keep company policy details inside the knowledge source.</p>
</blockquote>



<h2 class="wp-block-heading">Test Agent Instructions in Copilot Studio</h2>



<p>After configuring the instructions, I always test them.</p>



<p>I do not publish the agent immediately.</p>



<p>I start with a normal HR question.</p>



<h3 class="wp-block-heading">Test 1: Ask a Policy Question</h3>



<p>I ask:</p>



<blockquote class="wp-block-quote has-base-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p>How many annual leave days do I get?</p>
</blockquote>



<p>My agent should find the answer from the HR Policy Handbook.</p>



<p>For my example, the answer may say that eligible employees receive 18 annual leave days.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f6f8f9" data-has-transparency="false" style="--dominant-color: #f6f8f9;" loading="lazy" decoding="async" width="650" height="793" sizes="(max-width: 650px) 100vw, 650px" src="https://www.spguides.com/wp-content/uploads/2026/09/Copilot-Studio-Suggested-Prompts.avif" alt="Copilot Studio Suggested Prompts" class="wp-image-137417 not-transparent" title="Copilot Studio Suggested Prompts" srcset="https://www.spguides.com/wp-content/uploads/2026/09/Copilot-Studio-Suggested-Prompts.avif 650w, https://www.spguides.com/wp-content/uploads/2026/09/Copilot-Studio-Suggested-Prompts-246x300.avif 246w" /></figure></div>


<p>This test tells me whether the agent is using my knowledge correctly.</p>



<h3 class="wp-block-heading">Test 2: Ask for a Process</h3>



<p>Next, I ask:</p>



<blockquote class="wp-block-quote has-base-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p>How do I apply for leave?</p>
</blockquote>



<p>This test is important because I specifically told my agent to provide numbered steps for process questions.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f4f6f7" data-has-transparency="false" style="--dominant-color: #f4f6f7;" loading="lazy" decoding="async" width="625" height="589" sizes="(max-width: 625px) 100vw, 625px" src="https://www.spguides.com/wp-content/uploads/2026/09/How-to-Add-Suggested-Prompts-in-Microsoft-Copilot-Studio.avif" alt="How to Add Suggested Prompts in Microsoft Copilot Studio" class="wp-image-137418 not-transparent" title="How to Add Suggested Prompts in Microsoft Copilot Studio" srcset="https://www.spguides.com/wp-content/uploads/2026/09/How-to-Add-Suggested-Prompts-in-Microsoft-Copilot-Studio.avif 625w, https://www.spguides.com/wp-content/uploads/2026/09/How-to-Add-Suggested-Prompts-in-Microsoft-Copilot-Studio-300x283.avif 300w" /></figure></div>


<p>I check whether the answer is easy to follow.</p>



<p>If the answer is still too long, I can improve my instructions.</p>



<p>For example:</p>



<blockquote class="wp-block-quote has-base-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p>Keep process instructions concise and provide only the steps required to complete the process.</p>
</blockquote>



<h3 class="wp-block-heading">Test 3: Ask About Missing Information</h3>



<p>I also test something that does not exist in my HR Policy Handbook.</p>



<p>For example:</p>



<blockquote class="wp-block-quote has-base-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p>What is our paternity leave allowance?</p>
</blockquote>



<p>If that information is not available, I want the agent to tell me it could not find the policy.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f4f7f8" data-has-transparency="false" style="--dominant-color: #f4f7f8;" loading="lazy" decoding="async" width="628" height="471" sizes="(max-width: 628px) 100vw, 628px" src="https://www.spguides.com/wp-content/uploads/2026/09/Agent-Instructions-and-Conversation-Starters-in-Copilot-Studio.avif" alt="Agent Instructions and Conversation Starters in Copilot Studio" class="wp-image-137419 not-transparent" title="Agent Instructions and Conversation Starters in Copilot Studio" srcset="https://www.spguides.com/wp-content/uploads/2026/09/Agent-Instructions-and-Conversation-Starters-in-Copilot-Studio.avif 628w, https://www.spguides.com/wp-content/uploads/2026/09/Agent-Instructions-and-Conversation-Starters-in-Copilot-Studio-300x225.avif 300w" /></figure></div>


<p>I do not want it to guess.</p>



<h3 class="wp-block-heading">Test 4: Ask an Unrelated Question</h3>



<p>I ask:</p>



<blockquote class="wp-block-quote has-base-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p>Can you write Python code for a calculator?</p>
</blockquote>



<p>My HR assistant should tell me that it focuses on HR-related questions.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f4f7f8" data-has-transparency="false" style="--dominant-color: #f4f7f8;" loading="lazy" decoding="async" width="639" height="399" sizes="(max-width: 639px) 100vw, 639px" src="https://www.spguides.com/wp-content/uploads/2026/09/Microsoft-Copilot-Studio-Agent-Instructions-Tutorial.avif" alt="Microsoft Copilot Studio Agent Instructions Tutorial" class="wp-image-137420 not-transparent" title="Microsoft Copilot Studio Agent Instructions Tutorial" srcset="https://www.spguides.com/wp-content/uploads/2026/09/Microsoft-Copilot-Studio-Agent-Instructions-Tutorial.avif 639w, https://www.spguides.com/wp-content/uploads/2026/09/Microsoft-Copilot-Studio-Agent-Instructions-Tutorial-300x187.avif 300w" /></figure></div>


<p>This tells me whether my scope instruction is working.</p>



<h3 class="wp-block-heading">Test 5: Ask an Unclear Question</h3>



<p>Finally, I ask:</p>



<blockquote class="wp-block-quote has-base-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p>Tell me about leave.</p>
</blockquote>



<p>This question is very broad.</p>



<p>The agent may ask whether I want information about annual leave, sick leave, casual leave, or the leave application process.</p>



<p>That is exactly why I added this instruction:</p>



<blockquote class="wp-block-quote has-base-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p>If the employee&#8217;s question is unclear, ask a clarifying question.</p>
</blockquote>



<h2 class="wp-block-heading">Configure Conversation Starters in Copilot Studio</h2>



<p>Now my agent knows how I want it to behave. But I still need to think about the employee using the agent.</p>



<p>Suppose an employee opens <strong>Employee HR Assistant</strong> for the first time. They may not know what questions the agent can answer.</p>



<p>This is where <strong>Conversation Starters</strong>, also called <strong>Suggested Prompts</strong>, become useful.</p>



<p>I use them to show users examples of useful questions.</p>



<p>For my HR assistant, I can configure prompts like these:</p>



<figure class="wp-block-table is-style-stripes"><table class="has-fixed-layout"><thead><tr><th>Title</th><th>Prompt</th></tr></thead><tbody><tr><td>Leave Policy</td><td>Explain the company&#8217;s leave policy and leave types.</td></tr><tr><td>Apply for Leave</td><td>Give me step-by-step instructions for applying for leave.</td></tr><tr><td>Work From Home</td><td>Explain the company&#8217;s work-from-home policy.</td></tr><tr><td>Expense Claim</td><td>How can I submit an expense reimbursement claim?</td></tr><tr><td>Company Holidays</td><td>Explain the company holiday policy.</td></tr><tr><td>Contact HR</td><td>How can I contact HR if you cannot answer my question?</td></tr></tbody></table></figure>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f8f9f9" data-has-transparency="false" style="--dominant-color: #f8f9f9;" loading="lazy" decoding="async" width="990" height="582" sizes="(max-width: 990px) 100vw, 990px" src="https://www.spguides.com/wp-content/uploads/2026/09/How-to-Write-Agent-Instructions-in-Copilot-Studio.avif" alt="How to Write Agent Instructions in Copilot Studio" class="wp-image-137421 not-transparent" title="How to Write Agent Instructions in Copilot Studio" srcset="https://www.spguides.com/wp-content/uploads/2026/09/How-to-Write-Agent-Instructions-in-Copilot-Studio.avif 990w, https://www.spguides.com/wp-content/uploads/2026/09/How-to-Write-Agent-Instructions-in-Copilot-Studio-300x176.avif 300w, https://www.spguides.com/wp-content/uploads/2026/09/How-to-Write-Agent-Instructions-in-Copilot-Studio-768x451.avif 768w" /></figure></div>


<p>I prefer specific prompts like these.</p>



<p>I avoid conversation starters such as:</p>



<blockquote class="wp-block-quote has-base-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p>Help me</p>
</blockquote>



<p>or:</p>



<blockquote class="wp-block-quote has-base-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p>Ask HR</p>
</blockquote>



<p>Those prompts do not show the employee what the agent can actually do.</p>



<p>If I want to improve the opening experience further, I can also <a href="https://www.spguides.com/customize-welcome-message-in-copilot-studio-chatbot/">customize the welcome message in Copilot Studio</a>.</p>



<h2 class="wp-block-heading">Agent Instructions vs Conversation Starters</h2>



<p>This is one area where beginners sometimes get confused.</p>



<p>The easiest way I explain it is:</p>



<ul class="wp-block-list">
<li><strong>Instructions are for my agent.</strong></li>



<li><strong>Conversation Starters are for my users.</strong></li>
</ul>



<p>For example, this is an instruction:</p>



<blockquote class="wp-block-quote has-base-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p>When explaining a process, use numbered steps.</p>
</blockquote>



<p>The employee does not need to know about that rule.</p>



<p>But this is a conversation starter:</p>



<blockquote class="wp-block-quote has-base-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p>How do I apply for leave?</p>
</blockquote>



<p>The employee can select that prompt and immediately start a useful conversation.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-dominant-color="acbad6" data-has-transparency="false" style="--dominant-color: #acbad6;" loading="lazy" decoding="async" width="1024" height="576" sizes="(max-width: 1024px) 100vw, 1024px" src="https://www.spguides.com/wp-content/uploads/2026/09/Microsoft-Copilot-Studio-Suggested-Prompts-Examples-1024x576.avif" alt="Microsoft Copilot Studio Suggested Prompts Examples" class="wp-image-137422 not-transparent" title="Microsoft Copilot Studio Suggested Prompts Examples" srcset="https://www.spguides.com/wp-content/uploads/2026/09/Microsoft-Copilot-Studio-Suggested-Prompts-Examples-1024x576.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/09/Microsoft-Copilot-Studio-Suggested-Prompts-Examples-300x169.avif 300w, https://www.spguides.com/wp-content/uploads/2026/09/Microsoft-Copilot-Studio-Suggested-Prompts-Examples-768x432.avif 768w, https://www.spguides.com/wp-content/uploads/2026/09/Microsoft-Copilot-Studio-Suggested-Prompts-Examples-1536x864.avif 1536w, https://www.spguides.com/wp-content/uploads/2026/09/Microsoft-Copilot-Studio-Suggested-Prompts-Examples.avif 1672w" /></figure></div>


<p>So I use <strong>Instructions</strong> to control behavior and <strong>Conversation Starters</strong> to improve the user experience.</p>



<h2 class="wp-block-heading">What Happens When Information Changes?</h2>



<p>HR information changes over time.</p>



<p>For example:</p>



<ul class="wp-block-list">
<li>Annual leave entitlement may change.</li>



<li>Work-from-home rules may change.</li>



<li>Reimbursement limits may change.</li>



<li>Company holidays change every year.</li>
</ul>



<p>I do not want to put this changing information directly inside my Agent Instructions.</p>



<p>Instead, I update the HR knowledge source.</p>



<p>For example, if annual leave changes from 18 days to 20 days, I update the source where HR manages that policy.</p>



<p>My behavior instruction can remain:</p>



<blockquote class="wp-block-quote has-base-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p>Use the configured HR knowledge source when answering company policy questions.</p>
</blockquote>



<p>This separation makes the agent much easier for me to maintain.</p>



<h2 class="wp-block-heading">Authentication and Employee Information</h2>



<p>For the example in this article, I mainly use company policy information.</p>



<p>That is very different from employee-specific information.</p>



<p>For example:</p>



<blockquote class="wp-block-quote has-base-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p>What is the company&#8217;s annual leave policy?</p>
</blockquote>



<p>is a general company question.</p>



<p>But:</p>



<blockquote class="wp-block-quote has-base-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p>How many leave days do I personally have remaining?</p>
</blockquote>



<p>is a personal question.</p>



<p>If I want my agent to work with employee-specific information, I need to think carefully about authentication and security.</p>



<p>The agent must know who the employee is before showing personal data.</p>



<p>I should never allow Employee A to ask for Employee B&#8217;s confidential HR information.</p>



<p>If I need more control over user identity, I can review how to <a href="https://www.spguides.com/set-up-manual-authentication-in-microsoft-copilot-studio/">set up manual authentication in Microsoft Copilot Studio</a>.</p>



<p>For my HR assistant, I also include this instruction:</p>



<blockquote class="wp-block-quote has-base-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p>Never expose confidential information belonging to another employee.</p>
</blockquote>



<p>The instruction helps define expected behavior, but I still need proper security on the data source itself.</p>



<h2 class="wp-block-heading">Publish the Employee HR Assistant</h2>



<p>Once I am happy with my testing, I publish the agent.</p>



<p>Before publishing, I check:</p>



<ul class="wp-block-list">
<li>Does the agent use the HR knowledge correctly?</li>



<li>Are responses short and clear?</li>



<li>Does it avoid inventing company policies?</li>



<li>Does it handle missing information properly?</li>



<li>Does it stay within the HR scope?</li>



<li>Are the conversation starters useful?</li>



<li>Does it avoid exposing confidential information?</li>
</ul>



<p>Once those tests pass, I publish the latest version.</p>



<p>I can then make the agent available through the channel my employees will use.</p>



<p>A <strong>channel</strong> is simply where users interact with my agent.</p>



<p>For example, I may want employees to use it inside the Microsoft 365 experience or another supported channel.</p>



<p>If I am building an agent for a public website instead, I can also follow this guide to <a href="https://www.spguides.com/publish-agent-to-live-website-copilot-studio/">publish a Copilot Studio agent to a live website</a>.</p>



<p>For an internal HR assistant, I normally try to place the agent where employees already work. This makes adoption easier because users do not need to learn another system.</p>



<h2 class="wp-block-heading">Some Useful Tips</h2>



<ul class="wp-block-list">
<li><strong>Keep instructions simple.</strong> I avoid writing one huge instruction full of complicated rules. Clear instructions are easier to test and maintain.</li>



<li><strong>Do not copy every policy into Instructions.</strong> I keep policy information in the knowledge source and use instructions only for behavior.</li>



<li><strong>Tell the agent not to guess.</strong> I always define what should happen when company-specific information is unavailable.</li>



<li><strong>Protect employee information.</strong> I never depend only on a prompt instruction for security. I also secure the actual data source.</li>



<li><strong>Test different question types.</strong> I test normal questions, unclear questions, missing information, unrelated questions, and sensitive requests.</li>



<li><strong>Review instructions when policies change.</strong> Usually I update the knowledge source, but I still review the overall agent behavior after major business changes.</li>
</ul>



<h2 class="wp-block-heading">Frequently Asked Questions</h2>


<div id="rank-math-faq" class="rank-math-block">
<div class="rank-math-list ">
<div id="faq-question-1789102503459" class="rank-math-list-item">
<h3 class="rank-math-question ">What are Agent Instructions in Microsoft Copilot Studio?</h3>
<div class="rank-math-answer ">

<p><strong>Agent Instructions</strong> tell my Copilot Studio agent how I want it to behave. I can define its role, scope, response style, boundaries, and what it should do when information is missing.</p>

</div>
</div>
<div id="faq-question-1789102524906" class="rank-math-list-item">
<h3 class="rank-math-question ">What is the difference between Knowledge and Instructions in Copilot Studio?</h3>
<div class="rank-math-answer ">

<p><strong>Knowledge</strong> contains the information my agent can use. <strong>Instructions</strong> tell the agent how I want it to use that information and respond to users.</p>

</div>
</div>
<div id="faq-question-1789102530756" class="rank-math-list-item">
<h3 class="rank-math-question ">What are Conversation Starters in Microsoft Copilot Studio?</h3>
<div class="rank-math-answer ">

<p>Conversation Starters are suggested prompts that help users understand what they can ask. I use them to make the first interaction with my agent easier.</p>

</div>
</div>
<div id="faq-question-1789102547182" class="rank-math-list-item">
<h3 class="rank-math-question ">Can I use a PDF as knowledge in Copilot Studio?</h3>
<div class="rank-math-answer ">

<p>Yes. For my Employee HR Assistant example, I use an HR Policy Handbook PDF as the knowledge source. This works well when policies are already maintained as documents.</p>

</div>
</div>
<div id="faq-question-1789102563292" class="rank-math-list-item">
<h3 class="rank-math-question ">Should I put HR policies inside Agent Instructions?</h3>
<div class="rank-math-answer ">

<p>I normally do not. I keep HR policy content inside the knowledge source and use Agent Instructions for behavior, tone, scope, and response rules.</p>

</div>
</div>
<div id="faq-question-1789102573358" class="rank-math-list-item">
<h3 class="rank-math-question ">Can I create an HR chatbot using Microsoft Copilot Studio?</h3>
<div class="rank-math-answer ">

<p>Yes. I can use Copilot Studio to build an HR assistant for common policy questions, leave information, work-from-home rules, holidays, expenses, benefits, and HR support.</p>

</div>
</div>
</div>
</div>


<p>I configured my <strong>Employee HR Assistant</strong> by combining company knowledge, clear Agent Instructions, and useful Conversation Starters. I have found that keeping knowledge and behavior separate makes the agent much easier to control, test, and maintain.</p>



<p>I hope you found this article helpful.</p>



<p>You may also like</p>



<ul class="wp-block-list">
<li><a href="https://www.spguides.com/create-autonomous-agents-copilot-studio/">Create autonomous agents in Microsoft Copilot Studio</a></li>



<li><a href="https://www.spguides.com/microsoft-365-copilot-vs-copilot-studio/">Microsoft 365 Copilot vs Copilot Studio</a></li>



<li><a href="https://www.spguides.com/create-custom-agent-in-microsoft-365-copilot/">Create a custom agent in Microsoft 365 Copilot</a></li>



<li><a href="https://www.spguides.com/copilot-studio-demo-website-authentication-error/">Copilot Studio demo website authentication error</a></li>



<li><a href="https://www.spguides.com/sharepoint-online/">SharePoint Online overview</a></li>
</ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>50 Power Apps Interview Questions and Answers (2026)</title>
		<link>https://www.spguides.com/power-apps-interview-questions-and-answers/</link>
		
		<dc:creator><![CDATA[Bijay Kumar]]></dc:creator>
		<pubDate>Fri, 11 Sep 2026 07:38:35 +0000</pubDate>
				<category><![CDATA[Power Apps]]></category>
		<category><![CDATA[Power Apps Interview Questions and Answers]]></category>
		<guid isPermaLink="false">https://www.spguides.com/?p=137426</guid>

					<description><![CDATA[A few months back, one of my mentees sat for a Power Apps developer interview at a big consulting firm and came back a little rattled. It wasn&#8217;t the basic Canvas app questions that tripped her up — it was the mix of practical scenarios, Dataverse architecture, and a couple of curveballs on AI Builder ... <a title="50 Power Apps Interview Questions and Answers (2026)" class="read-more" href="https://www.spguides.com/power-apps-interview-questions-and-answers/" aria-label="Read more about 50 Power Apps Interview Questions and Answers (2026)">read more...</a>]]></description>
										<content:encoded><![CDATA[
<p>A few months back, one of my mentees sat for a <a href="https://www.spguides.com/live-training/" target="_blank" rel="noreferrer noopener">Power Apps developer</a> interview at a big consulting firm and came back a little rattled. It wasn&#8217;t the basic <strong>Canvas app</strong> questions that tripped her up — it was the mix of practical scenarios, <strong>Dataverse</strong> architecture, and a couple of curveballs on <strong>AI Builder</strong> and <strong>Copilot Studio</strong>. That&#8217;s the pattern I keep seeing in interviews for 3-5 years of experience these days: panels don&#8217;t just want theory; they want to know how you&#8217;d actually build something under real constraints.</p>



<p>I&#8217;ve sat on both sides of the table for Power Apps interviews — as a candidate early in my career, and later as someone screening developers for <a href="https://www.tsinfotechnologies.com/power-apps-development-services/" target="_blank" rel="noreferrer noopener">client projects</a>. The questions that separate a strong candidate from an average one are rarely the textbook ones. They&#8217;re the &#8220;how would you design this&#8221; and &#8220;why did you choose that approach&#8221; questions that show whether you&#8217;ve actually shipped apps to production.</p>



<p>This article walks you through 50 <strong>Power Apps interview questions and answers</strong>, organized by theme, so you can prepare with a clear structure instead of randomly Googling isolated topics.</p>



<p>If you want to become a Power Apps developer, check out the <a href="https://www.spguides.com/courses/" target="_blank" rel="noreferrer noopener">complete training courses</a> or our <a href="https://www.spguides.com/live-training/" target="_blank" rel="noreferrer noopener">live training</a>.</p>



<h2 class="wp-block-heading">Canvas App Design and UI Questions</h2>



<p>These questions test whether you can architect an app that&#8217;s easy to extend later, not just one that works today.</p>



<h3 class="wp-block-heading">1. You need to build a multi-lingual Canvas app supporting English, Spanish, and French. How would you design it so adding another language later is easy?</h3>



<p>I never hardcode text strings directly on controls. Instead, I build a <strong>collection</strong> of labels with one column per language, loaded on app start using <code>ClearCollect()</code>, and reference it everywhere with a formula like <code>LookUp(colLabels, Key = "Welcome").EN</code>. A cleaner alternative is a <strong>Named Formula</strong> or a <strong>Dataverse table</strong> that stores translations, so adding a new language later means adding a column or a few rows — not touching every screen.</p>



<h3 class="wp-block-heading">2. How would you build a multi-theme Canvas app where users can switch between Light and Dark themes using a Toggle?</h3>



<p>I store theme colors as a collection or a set of global variables (<code>varPrimaryColor</code>, <code>varBackgroundColor</code>, and so on) and bind every control&#8217;s <code>Fill</code> and <code>Color</code> property to those variables instead of hardcoded hex values. The Toggle&#8217;s <code>OnCheck</code> and <code>OnUncheck</code> just update the variables using <code>Set()</code>. You can read a full walkthrough on <a href="https://www.spguides.com/switch-light-or-dark-theme-color-using-power-apps-toggle-control/">switching between light and dark themes with a Power Apps toggle control</a>.</p>



<h3 class="wp-block-heading">3. How can you download Gallery data from a Canvas App into Excel or CSV?</h3>



<p>For a <strong>Data table</strong> control, I usually use the built-in export feature, or generate a CSV string with <code>Concat()</code> and trigger a download through the <code>Download()</code> function. For anything more formatted, I hand it off to <strong>Power Automate</strong> to build the file server-side. I&#8217;ve covered both approaches in detail in <a href="https://www.spguides.com/export-data-from-data-table-to-excel-in-power-apps/">exporting data from a data table to Excel in Power Apps</a>.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>Pro Tip:</strong> I&#8217;ve found that interviewers love this question because it exposes whether you understand governance too. I always mention that exporting sensitive data needs a review of the app&#8217;s <strong>Data Loss Prevention (DLP)</strong> policy before anyone assumes it&#8217;s a simple feature request.</p>
</blockquote>



<h3 class="wp-block-heading">4. How do you plan screen navigation in a large Canvas app with many screens?</h3>



<p>I avoid a flat list of screens with individual <code>Navigate()</code> calls scattered everywhere. Instead, I build a reusable navigation <strong>Component</strong> with a <strong>collection</strong>-driven menu, so adding a new screen means adding one row, not editing ten buttons. I&#8217;ve built exactly this kind of setup in <a href="https://www.spguides.com/power-apps-create-responsive-navigation-menu/">creating a responsive navigation menu in Power Apps</a> and a <a href="https://www.spguides.com/power-apps-left-navigation-component/">left navigation component</a>.</p>



<h3 class="wp-block-heading">5. What are Components in Power Apps and why should you use them?</h3>



<p>A <strong>Component</strong> is a reusable group of controls with its own custom properties, similar to a template you drop onto multiple screens. I use them for headers, footers, and navigation menus so a design change happens in one place instead of every screen. Here&#8217;s a practical example: <a href="https://www.spguides.com/create-a-header-component-in-power-apps/">creating a header component in Power Apps</a>.</p>



<h3 class="wp-block-heading">6. How do you build a horizontal navigation menu with submenus in Power Apps?</h3>



<p>I nest a horizontal Gallery for top-level menu items and toggle the visibility of a second Gallery for submenu items based on the selected parent item, usually tracked with a context variable. I walk through a complete build in <a href="https://www.spguides.com/power-apps-horizontal-navigation-menu-component-with-submenu/">creating a horizontal navigation menu with submenus in Power Apps</a>.</p>



<h3 class="wp-block-heading">7. How do you make a Canvas app responsive across tablet and mobile screen sizes?</h3>



<p>I set container layouts to fill available space, use <code>Parent.Width</code> and <code>Parent.Height</code> instead of fixed pixel values, and test on both orientations early — not after the app is &#8220;done.&#8221; Getting this wrong is one of the biggest rework causes I&#8217;ve seen on client projects.</p>



<h2 class="wp-block-heading">Power Fx and Formula Questions</h2>



<p>This is where interviewers separate people who&#8217;ve memorized function names from people who understand <strong>Power Fx</strong>, the formula language behind Power Apps.</p>



<h3 class="wp-block-heading">8. What is delegation in Power Apps and why is it important when working with large datasets?</h3>



<p><strong>Delegation</strong> means the data source (like SharePoint or Dataverse) processes a query itself instead of Power Apps pulling all records down first. Non-delegable formulas only process the first 500 or 2,000 records locally, so a <code>Filter()</code> that isn&#8217;t delegable will silently return incomplete results on a large list. I always check the delegation warning icon in the formula bar and rework the formula, or move to Dataverse, when a list starts growing.</p>



<h3 class="wp-block-heading">9. What is the difference between Patch() and SubmitForm() in Canvas Apps?</h3>



<p><code>SubmitForm()</code> saves whatever is bound to a <strong>Form</strong> control&#8217;s fields, using the form&#8217;s own validation rules. <code>Patch()</code> gives you direct, field-level control to insert or update a record without needing a Form control at all, which is useful for Gallery-based editing or saving several records at once. I explain both patterns with examples in <a href="https://www.spguides.com/update-and-updateif-in-power-apps/">Update and UpdateIf in Power Apps</a> and <a href="https://www.spguides.com/patch-gallery-items-in-power-apps/">patching Gallery items in Power Apps</a>.</p>



<h3 class="wp-block-heading">10. What are Named Formulas in Power Apps and how are they different from using Set()?</h3>



<p>A <strong>Named Formula</strong> is a reusable, auto-recalculating value defined once at the app level — similar to a calculated column that always stays current. <code>Set()</code> creates a <strong>global variable</strong> that only changes when you explicitly run that line of code again. I reach for Named Formulas for values that should always reflect current state, like &#8220;is the current user an admin,&#8221; and I use <code>Set()</code> for things that genuinely represent one-time user actions, like a button click flag.</p>



<h3 class="wp-block-heading">11. What is the difference between Collect() and ClearCollect()?</h3>



<p><code>Collect()</code> adds records to an existing <strong>collection</strong> without removing what&#8217;s already there, so calling it repeatedly keeps growing the collection. <code>ClearCollect()</code> empties the collection first and then adds the new records, which is what you want when refreshing data on screen load. I&#8217;ve written more on building these from scratch in <a href="https://www.spguides.com/create-empty-collection-in-power-apps/">creating an empty collection in Power Apps</a> and <a href="https://www.spguides.com/how-to-add-gallery-data-to-a-collection-in-power-apps/">adding Gallery data to a collection</a>.</p>



<h3 class="wp-block-heading">12. How does UpdateContext() differ from Set()?</h3>



<p><code>UpdateContext()</code> creates a <strong>context variable</strong> that only exists on the current screen, while <code>Set()</code> creates a global variable available across the whole app. I use context variables for screen-only UI state, like whether a panel is expanded, and global variables for anything the rest of the app needs to see, like the logged-in user&#8217;s role.</p>



<h3 class="wp-block-heading">13. What does the Concurrent() function do and when would you use it?</h3>



<p><code>Concurrent()</code> runs multiple formulas at the same time instead of one after another, which noticeably speeds up app load when you&#8217;re pulling from several unrelated data sources. I typically wrap my initial <code>ClearCollect()</code> calls for dropdown lookups inside <code>Concurrent()</code> on the app&#8217;s <code>OnStart</code> property.</p>



<h3 class="wp-block-heading">14. How do you handle errors gracefully in a Canvas app?</h3>



<p>I wrap risky operations in <code>IfError()</code> and show a friendly <code>Notify()</code> message instead of letting the app throw a raw error banner at the user. Two errors I run into often are covered in <a href="https://www.spguides.com/does-not-match-the-expected-type-record-in-power-apps/">the &#8220;does not match the expected type record&#8221; error</a> and <a href="https://www.spguides.com/invalid-operation-division-by-zero-in-power-apps/">invalid operation: division by zero in Power Apps</a>.</p>



<h3 class="wp-block-heading">15. What&#8217;s the difference between LookUp() and Filter()?</h3>



<p><code>LookUp()</code> returns a single record — the first match it finds — while <code>Filter()</code> returns a table of every matching record. Using <code>LookUp()</code> when you actually need multiple rows, or using <code>Filter()</code> and then grabbing <code>First()</code> when you only need one record, are both common mistakes I&#8217;ve had to fix in code reviews.</p>



<h3 class="wp-block-heading">16. How do you use the With() function, and why is it useful?</h3>



<p><code>With()</code> lets you define temporary named values inside a single formula, so you don&#8217;t have to repeat the same sub-expression multiple times. It also makes formulas easier to read and slightly more efficient, since the sub-expression only gets evaluated once instead of on every reference.</p>



<h3 class="wp-block-heading">17. How do you optimize a formula-heavy Canvas app for performance?</h3>



<p>I move repeated lookups into variables set once on screen load instead of recalculating them on every control. I also avoid nested <code>Filter()</code> calls inside Gallery items, use <strong>Named Formulas</strong> where the value truly needs to stay live, and check the <a href="https://www.spguides.com/power-apps-refresh-function/">Power Apps Refresh function</a> usage — refreshing a data source too often is a silent performance killer.</p>



<h2 class="wp-block-heading">SharePoint and Dataverse Questions</h2>



<p>Almost every mid-to-senior interview includes at least a few questions comparing these two data sources.</p>



<h3 class="wp-block-heading">18. SharePoint list or Dataverse table — how do you decide which one to use?</h3>



<p><strong>Dataverse</strong> is a proper relational database with rich data types, real relationships, business rules, and row-level security, which makes it the right choice for complex, multi-table business apps. A <strong>SharePoint list</strong> is faster to spin up and fine for simpler apps or teams without Dataverse licensing. I break this decision down fully in <a href="https://www.spguides.com/power-apps-dataverse-vs-sharepoint-list/">Power Apps Dataverse vs SharePoint list</a>.</p>



<h3 class="wp-block-heading">19. What is a Dataverse Choice column and how is it different from a SharePoint Choice column?</h3>



<p>Both store a fixed set of options, but Dataverse <strong>Choice</strong> columns (formerly called option sets) can be shared across multiple tables and environments, while a SharePoint Choice column lives only inside that one list. Filtering a Dataverse choice column also needs a slightly different formula pattern, which I cover in <a href="https://www.spguides.com/filter-dataverse-choice-column/">filtering a Dataverse Choice column</a>.</p>



<h3 class="wp-block-heading">20. How do you migrate a SharePoint list to Dataverse?</h3>



<p>I map columns first, since data types don&#8217;t line up one-to-one, then use the built-in migration tooling or Power Automate to move records over in batches, and finally repoint the Canvas app&#8217;s data source and retest every formula that referenced SharePoint-specific behavior. The full process is in <a href="https://www.spguides.com/migrate-sharepoint-online-list-to-dataverse/">migrating a SharePoint Online list to Dataverse</a>.</p>



<h3 class="wp-block-heading">21. What is a Dataverse formula column?</h3>



<p>A <strong>formula column</strong> calculates its value automatically from other columns in the same row, similar to a calculated column, but it recalculates in real time and can be used in views and reports. I use these instead of doing the same math repeatedly inside the Canvas app. See <a href="https://www.spguides.com/dataverse-formula-column/">Dataverse formula column</a> for a working example.</p>



<h3 class="wp-block-heading">22. How do table relationships work in Dataverse?</h3>



<p>Dataverse supports one-to-many and many-to-many relationships between tables, similar to foreign keys in SQL, which lets you build proper parent-child structures like Orders and Order Items. I&#8217;ve documented creating tables and relationships from scratch in <a href="https://www.spguides.com/dataverse-create-table/">creating a Dataverse table</a> and <a href="https://www.spguides.com/dataverse-create-table-from-sharepoint-list/">creating a Dataverse table from a SharePoint list</a>.</p>



<h3 class="wp-block-heading">23. How do you deal with SharePoint&#8217;s 2,000-item delegation limit on a growing list?</h3>



<p>I push as much filtering to the server as possible using delegable functions, add indexed columns for anything used in filters, and design the UI to force users to search or filter before loading a full list rather than showing everything by default. For date-based filtering, I lean on patterns like <a href="https://www.spguides.com/power-apps-filter-gallery-by-quarter/">filtering a gallery by quarter</a> and <a href="https://www.spguides.com/power-apps-filter-gallery-by-year/">filtering a gallery by year</a>, which stay delegable.</p>



<h3 class="wp-block-heading">24. How do you create a folder in SharePoint directly from a Power Apps app?</h3>



<p>I use the SharePoint connector&#8217;s <code>SPCreateFolder</code> action, usually triggered from a button after a record is created, so each record gets its own document folder automatically. The full steps are in <a href="https://www.spguides.com/create-a-folder-in-sharepoint-from-power-apps/">creating a folder in SharePoint from Power Apps</a>.</p>



<h3 class="wp-block-heading">25. What is a Dataverse solution and why does it matter?</h3>



<p>A <strong>solution</strong> is a container that packages your app, flows, tables, and other components together so they can be moved cleanly between environments — dev, test, and production. Without solutions, you end up manually recreating components in each environment, which is error-prone and impossible to version properly. More detail in <a href="https://www.spguides.com/dataverse-solution/">Dataverse solution</a>.</p>



<h2 class="wp-block-heading">AI Builder Questions</h2>



<p><strong>AI Builder</strong> questions have become common as more clients ask for document automation inside their Power Apps projects.</p>



<h3 class="wp-block-heading">26. What is AI Builder in Power Platform?</h3>



<p><strong>AI Builder</strong> is a set of pre-built and custom AI models — like form processing, object detection, and prediction — that you can plug directly into Power Apps and Power Automate without writing any machine learning code. It&#8217;s the easiest way to add &#8220;smart&#8221; features to a business app without bringing in a data science team.</p>



<h3 class="wp-block-heading">27. What AI Builder capabilities have you actually worked with?</h3>



<p>I&#8217;ve mostly used the <strong>Form Processor</strong> and <strong>Document Processing</strong> models to pull structured fields out of invoices, and I&#8217;ve worked with prediction models for simple yes/no classification tasks. A real example is detecting and extracting text inside Dataverse records, which I cover in <a href="https://www.spguides.com/detect-text-in-dataverse-using-ai-builder/">detecting text in Dataverse using AI Builder</a>.</p>



<h3 class="wp-block-heading">28. How do you extract invoice data using AI Builder inside a Power Apps flow?</h3>



<p>I train a Form Processor model on a handful of sample invoices, tag the fields I need — vendor name, invoice number, total — and then call that model from Power Automate whenever a new invoice lands in a SharePoint library. The complete build is in <a href="https://www.spguides.com/extract-invoice-data-from-pdf-or-image-using-power-apps/">extracting invoice data from a PDF or image using Power Apps</a> and <a href="https://www.spguides.com/extract-invoice-details-from-sharepoint-power-automate-ai-builder/">extracting invoice details with AI Builder in Power Automate</a>.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>Pro Tip:</strong> In my experience, AI Builder&#8217;s accuracy really depends on training with realistic samples — messy scans, different vendor formats, low-resolution photos. Training on ten clean PDFs and expecting production-grade accuracy is the mistake I see most junior developers make.</p>
</blockquote>



<h3 class="wp-block-heading">29. What licensing considerations come with AI Builder?</h3>



<p>AI Builder consumes <strong>credits</strong>, which are separate from standard Power Apps or Power Automate licensing, and every prediction or document processed draws from that pool. I always flag this to clients early, since heavy document processing volumes can burn through credits faster than expected.</p>



<h3 class="wp-block-heading">30. How does AI Builder fit together with Power Automate and Power Apps in a real solution?</h3>



<p>Typically, the Canvas app captures or displays the document, a Power Automate flow calls the AI Builder model to extract or classify data, and the result gets written back to Dataverse or SharePoint for the app to display. Keeping the AI logic in the flow, not the app, keeps the Canvas app fast and easy to maintain.</p>



<h2 class="wp-block-heading">Copilot Studio Questions</h2>



<p>Almost every recent interview I&#8217;ve heard about now includes at least two or three <strong>Copilot Studio</strong> questions.</p>



<h3 class="wp-block-heading">31. What is Copilot Studio and how is it different from a traditional chatbot?</h3>



<p><strong>Copilot Studio</strong> is Microsoft&#8217;s low-code platform for building AI-powered conversational agents. Unlike a traditional rule-based chatbot that only follows scripted decision trees, Copilot Studio agents can use generative AI to understand natural language, pull from knowledge sources, and still fall back to structured <strong>Topics</strong> for precise, controlled flows. I cover the setup in <a href="https://www.spguides.com/create-agent-using-microsoft-copilot-studio/">creating an agent using Microsoft Copilot Studio</a>.</p>



<h3 class="wp-block-heading">32. What are Topics, Entities, and Variables in Copilot Studio?</h3>



<p>A <strong>Topic</strong> is a defined conversation flow triggered by specific phrases, like &#8220;reset my password.&#8221; An <strong>Entity</strong> is a piece of information the agent extracts from user input, like a date or an employee ID. A <strong>Variable</strong> stores that captured information so it can be reused later in the conversation, such as passing an order number into a follow-up action.</p>



<h3 class="wp-block-heading">33. How do you build a custom Topic in Copilot Studio?</h3>



<p>I define trigger phrases the user might type, build the conversation flow with question nodes to capture entities, and add condition branches for different responses. The full walkthrough is in <a href="https://www.spguides.com/create-custom-topic-in-microsoft-copilot-studio/">creating a custom Topic in Microsoft Copilot Studio</a>.</p>



<h3 class="wp-block-heading">34. How do event triggers work in Copilot Studio?</h3>



<p><strong>Event triggers</strong> let a Topic fire automatically based on a system event, like a conversation starting, rather than only reacting to what the user types. I&#8217;ve used these to show a welcome message or run a setup check the moment a chat session opens, detailed in <a href="https://www.spguides.com/add-event-triggers-in-microsoft-copilot-studio/">adding event triggers in Microsoft Copilot Studio</a>.</p>



<h3 class="wp-block-heading">35. What are agent flows in Copilot Studio?</h3>



<p><strong>Agent flows</strong> are Power Automate-style flows built specifically to run inside a Copilot Studio agent&#8217;s conversation, letting the agent take real actions — create a record, send an approval, call an API — instead of just answering questions. I walk through building one in <a href="https://www.spguides.com/create-agent-flow-copilot-studio/">creating an agent flow in Copilot Studio</a>.</p>



<h3 class="wp-block-heading">36. How do you connect a SharePoint list to a Copilot Studio agent?</h3>



<p>I add the SharePoint list either as a <strong>knowledge source</strong>, so the agent can answer questions from it directly, or wire it up through an agent flow to create and update list items conversationally. Both patterns are covered in <a href="https://www.spguides.com/sharepoint-list-as-knowledge-in-copilot-studio/">using a SharePoint list as knowledge in Copilot Studio</a> and <a href="https://www.spguides.com/create-sharepoint-list-item-using-copilot-studio/">creating a SharePoint list item using Copilot Studio</a>.</p>



<h3 class="wp-block-heading">37. How do you publish a Copilot Studio agent to a live website?</h3>



<p>Once the agent is tested in the test canvas, I publish it and grab the embed code or use the Power Pages / custom website channel to drop it onto a live page. The full steps are in <a href="https://www.spguides.com/publish-agent-to-live-website-copilot-studio/">publishing an agent to a live website in Copilot Studio</a>.</p>



<h3 class="wp-block-heading">38. What&#8217;s the difference between Microsoft 365 Copilot and Copilot Studio?</h3>



<p><strong>Microsoft 365 Copilot</strong> is the AI assistant built into apps like Word, Teams, and Outlook to help with everyday productivity tasks. <strong>Copilot Studio</strong> is the platform you use to build your own custom agents from scratch for specific business processes. I break this distinction down in <a href="https://www.spguides.com/microsoft-365-copilot-vs-copilot-studio/">Microsoft 365 Copilot vs Copilot Studio</a>.</p>



<h2 class="wp-block-heading">Governance, Performance, and ALM Questions</h2>



<p>For 3-5 years of experience, interviewers expect you to think beyond &#8220;does it work&#8221; and into &#8220;does it scale and stay maintainable.&#8221;</p>



<h3 class="wp-block-heading">39. How do Standard and Premium connectors differ, and why does it matter for project planning?</h3>



<p><strong>Standard connectors</strong> are included in most Power Apps licenses, while <strong>Premium connectors</strong> — like SQL Server, Dataverse, or many third-party APIs — need a premium license per user or per app. I always check this during the design phase, because discovering a premium connector requirement after go-live is a budget conversation nobody enjoys. Full comparison here: <a href="https://www.spguides.com/standard-vs-premium-connectors-in-power-apps/">Standard vs Premium connectors in Power Apps</a>.</p>



<h3 class="wp-block-heading">40. How do you troubleshoot a slow-loading Power Apps app?</h3>



<p>I check <code>OnStart</code> first, since loading too much data upfront is the most common cause, then look for non-delegable filters, too many controls on one screen, and unnecessary image loading. Adding a loading indicator also improves perceived performance while the real fixes go in — see <a href="https://www.spguides.com/power-apps-loading-spinner/">Power Apps loading spinner</a>.</p>



<h3 class="wp-block-heading">41. What is the Center of Excellence (CoE) Starter Kit and why do organizations use it?</h3>



<p>The <strong>CoE Starter Kit</strong> is a set of Power Platform components that helps admins govern, monitor, and support citizen developers across an organization — tracking app usage, licensing, and adoption at scale. I&#8217;ve seen import issues with it firsthand, which I documented in <a href="https://www.spguides.com/import-failed-due-to-missing-dependencies-for-center-of-excellence-power-apps/">the CoE Starter Kit import failure due to missing dependencies</a>.</p>



<h3 class="wp-block-heading">42. How do you secure a Canvas app before sharing it with a wider group?</h3>



<p>I share the app with a <strong>Microsoft 365 group</strong> instead of individual users so access stays manageable, review the connections the app uses for over-permissioned service accounts, and check the tenant&#8217;s <strong>Data Loss Prevention (DLP)</strong> policies before publishing. Sharing steps are in <a href="https://www.spguides.com/share-canvas-app-with-a-microsoft-365-group-in-power-apps/">sharing a Canvas app with a Microsoft 365 group in Power Apps</a>.</p>



<h3 class="wp-block-heading">43. What naming conventions do you follow for controls and variables?</h3>



<p>I prefix controls by type — <code>btn</code> for buttons, <code>gal</code> for galleries, <code>txt</code> for text inputs — and prefix variables by scope, like <code>gbl</code> for global and <code>loc</code> for context variables. Consistent naming makes formulas far easier to read months later, or for another developer picking up your app. I go deeper on this in <a href="https://www.spguides.com/power-apps-naming-conventions/">Power Apps naming conventions</a>.</p>



<h3 class="wp-block-heading">44. How do you approach testing before deploying an app to production?</h3>



<p>I test with real data volumes, not just a handful of sample rows, check delegation warnings again at scale, and walk through the app as each different user role or security group to confirm permissions behave as expected. I also test on the actual device type end users will use — a form that looks fine on a laptop can break badly on a phone.</p>



<h3 class="wp-block-heading">45. What are Environment variables in Power Platform and why use them?</h3>



<p><strong>Environment variables</strong> store configuration values — like a SharePoint site URL or an API endpoint — outside the app itself, so moving a solution from dev to production only means updating the variable&#8217;s value, not editing formulas inside every app and flow. This is one of the first things I set up on any multi-environment project.</p>



<h2 class="wp-block-heading">Model-Driven Apps and Broader Platform Questions</h2>



<h3 class="wp-block-heading">46. What is a Model-driven app and how is it different from a Canvas app?</h3>



<p>A <strong>Model-driven app</strong> is generated automatically from your Dataverse data model — tables, forms, views, and business rules — giving you a consistent, responsive UI with far less manual screen design. A <strong>Canvas app</strong> gives you full pixel-level control over layout but requires you to build every screen yourself. I compare both in detail in <a href="https://www.spguides.com/model-driven-apps-in-power-apps/">Model-driven apps in Power Apps</a>.</p>



<h3 class="wp-block-heading">47. What is Microsoft Dataverse in one sentence, and why does it matter for enterprise apps?</h3>



<p><strong>Dataverse</strong> is Microsoft&#8217;s cloud-based, secure business database that underpins Power Apps, Power Automate, Dynamics 365, and Copilot Studio, giving all of them a shared, governed data layer. For anything beyond a simple departmental app, I recommend clients build on Dataverse from day one. More background in <a href="https://www.spguides.com/what-is-microsoft-dataverse/">What is Microsoft Dataverse</a>.</p>



<h3 class="wp-block-heading">48. How would you explain Power Apps&#8217; advantage over traditional custom development to a stakeholder?</h3>



<p><strong>Low-code</strong> development in Power Apps lets you build and ship business apps in weeks instead of months, without a dedicated development team for every small internal tool, while still allowing custom code and connectors for complex needs. I lay out the tradeoffs honestly in <a href="https://www.spguides.com/power-apps-vs-traditional-development/">Power Apps vs traditional development</a>.</p>



<h3 class="wp-block-heading">49. How do you decide whether a client project should be Canvas, Model-driven, or a hybrid?</h3>



<p>I look at how structured the data already is — heavily relational data with many tables usually points to Model-driven, while a highly custom, visual, or mobile-first experience points to Canvas. For larger enterprise solutions, I often recommend a hybrid: Model-driven for back-office data management and Canvas embedded inside it for a tailored front-end experience.</p>



<h3 class="wp-block-heading">50. What&#8217;s one mistake you&#8217;ve made on a Power Apps project that taught you something important?</h3>



<p>I once built an app entirely against a SharePoint list because it was &#8220;quick to start,&#8221; only to hit delegation limits and performance issues six months later when the list grew past 5,000 items. Migrating to Dataverse mid-project cost far more time than planning for scale upfront would have. Now I always ask about expected data volume before choosing a data source, not after.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-dominant-color="5f5078" data-has-transparency="false" style="--dominant-color: #5f5078;" loading="lazy" decoding="async" width="1024" height="576" sizes="(max-width: 1024px) 100vw, 1024px" src="https://www.spguides.com/wp-content/uploads/2026/09/Power-Apps-Interview-Questions-and-Answers-1024x576.avif" alt="Power Apps Interview Questions and Answers" class="wp-image-137427 not-transparent" title="Power Apps Interview Questions and Answers" srcset="https://www.spguides.com/wp-content/uploads/2026/09/Power-Apps-Interview-Questions-and-Answers-1024x576.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/09/Power-Apps-Interview-Questions-and-Answers-300x169.avif 300w, https://www.spguides.com/wp-content/uploads/2026/09/Power-Apps-Interview-Questions-and-Answers-768x432.avif 768w, https://www.spguides.com/wp-content/uploads/2026/09/Power-Apps-Interview-Questions-and-Answers.avif 1280w" /></figure></div>


<h2 class="wp-block-heading">Tips to Keep in Mind Before Your Interview</h2>



<ul class="wp-block-list">
<li><strong>Practice explaining your reasoning, not just the answer.</strong> Interviewers care more about why you&#8217;d choose Patch over SubmitForm in a given scenario than whether you can recite the syntax.</li>



<li><strong>Know the current data source debate cold.</strong> SharePoint vs Dataverse questions come up in almost every interview at this experience level, so be ready to justify your choice with real tradeoffs.</li>



<li><strong>Don&#8217;t skip AI Builder and Copilot Studio.</strong> These used to be &#8220;nice to know&#8221; a couple of years ago — now they show up as core rounds, as the interview experience I reviewed for this article confirms.</li>



<li><strong>Have one or two real project stories ready.</strong> A specific example, like solving a delegation issue on a production app, lands far better than a generic textbook answer.</li>



<li><strong>Brush up on governance basics.</strong> Questions about DLP policies, sharing, and the CoE Starter Kit show you can be trusted with production apps, not just build demos.</li>



<li><strong>Test your formulas out loud before the interview.</strong> Practicing how you&#8217;d explain <code>Patch()</code>, <code>Filter()</code>, and <code>Collect()</code> in plain English helps you avoid freezing up when asked live.</li>
</ul>



<h2 class="wp-block-heading">Frequently Asked Questions</h2>


<div id="rank-math-faq" class="rank-math-block">
<div class="rank-math-list ">
<div id="faq-question-1789111866381" class="rank-math-list-item">
<h3 class="rank-math-question ">How do I prepare for a Power Apps interview with 3-5 years of experience?</h3>
<div class="rank-math-answer ">

<p>Focus on scenario-based questions rather than pure definitions — practice explaining how you&#8217;d design an app under real constraints like delegation limits or multi-language support. Review your own past projects and be ready to walk through one or two in detail, including mistakes you fixed along the way.</p>

</div>
</div>
<div id="faq-question-1789111876118" class="rank-math-list-item">
<h3 class="rank-math-question ">Are Power Apps interviews at this level mostly theory or hands-on coding?</h3>
<div class="rank-math-answer ">

<p>Most interviews at 3-5 years mix conceptual questions with scenario-based design questions rather than live coding, though some panels do ask you to write a Power Fx formula on the spot. Expect a good portion of the interview to focus on &#8220;how would you build this&#8221; rather than pure definitions.</p>

</div>
</div>
<div id="faq-question-1789111891000" class="rank-math-list-item">
<h3 class="rank-math-question ">What Dataverse topics should I focus on most for an interview?</h3>
<div class="rank-math-answer ">

<p>Prioritize the differences between SharePoint and Dataverse, Choice columns, formula columns, relationships, and security roles, since these come up repeatedly across interviews. Understanding when to recommend Dataverse over SharePoint for a client project is often more important than memorizing every table property.</p>

</div>
</div>
<div id="faq-question-1789111901326" class="rank-math-list-item">
<h3 class="rank-math-question ">How important is Copilot Studio knowledge for a Power Apps developer role now?</h3>
<div class="rank-math-answer ">

<p>It&#8217;s become genuinely important, not optional, especially for consulting roles where clients are actively asking for AI-powered assistants alongside their business apps. Even a working knowledge of Topics, Entities, and agent flows puts you ahead of candidates who&#8217;ve only worked with Canvas and Model-driven apps.</p>

</div>
</div>
<div id="faq-question-1789111910710" class="rank-math-list-item">
<h3 class="rank-math-question ">Do I need to know AI Builder in depth, or just the basics?</h3>
<div class="rank-math-answer ">

<p>You don&#8217;t need to be a data scientist, but you should understand what AI Builder does, which models are commonly used like Form Processor, and how it connects to Power Automate and Power Apps in a real solution. Interviewers usually want to confirm you understand the concept and can apply it, not that you&#8217;ve built a custom model from scratch.</p>

</div>
</div>
<div id="faq-question-1789111921272" class="rank-math-list-item">
<h3 class="rank-math-question ">What&#8217;s the best way to practice Power Fx formulas before an interview?</h3>
<div class="rank-math-answer ">

<p>Build a small real app — a leave request tracker or an issue log — and deliberately practice <code>Patch()</code>, <code>Filter()</code>, <code>Collect()</code>, and error handling inside it rather than just reading about the functions. Explaining your formulas out loud as you write them mirrors exactly what an interview conversation feels like.</p>

</div>
</div>
</div>
</div>


<p>You may also like the following tutorials:</p>



<ul class="wp-block-list">
<li><a href="https://www.spguides.com/power-apps-interview-questions/">Power Apps Interview Questions and Answers</a></li>



<li><a href="https://www.spguides.com/power-apps-interview-questions-and-answers-for-experienced-developers/">Power Apps Interview Questions and Answers for Experienced Developers</a></li>



<li><a href="https://www.spguides.com/model-driven-apps-in-power-apps/">Model-Driven Apps in Power Apps</a></li>



<li><a href="https://www.spguides.com/what-is-microsoft-dataverse/">What is Microsoft Dataverse</a></li>



<li><a href="https://www.spguides.com/power-apps-vs-traditional-development/">Power Apps vs Traditional Development</a></li>
</ul>



<p>This list covers the questions I&#8217;ve personally seen come up most often for Power Apps roles at the 3-5 year mark, from Canvas app fundamentals through Dataverse, AI Builder, and Copilot Studio. The best preparation is still building a small real app yourself and walking through every design decision out loud, since that&#8217;s exactly what a good interviewer will ask you to do. Good luck with your interview.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Power BI DAX FILTER: A Practical Guide With Examples</title>
		<link>https://www.spguides.com/power-bi-dax-filter/</link>
		
		<dc:creator><![CDATA[Bijay Kumar]]></dc:creator>
		<pubDate>Mon, 07 Sep 2026 11:21:03 +0000</pubDate>
				<category><![CDATA[Power BI]]></category>
		<category><![CDATA[Power BI Dax Filter]]></category>
		<guid isPermaLink="false">https://www.spguides.com/?p=50807</guid>

					<description><![CDATA[A sales manager opens a monthly dashboard and notices something confusing. Total sales look right, but the “High-Value Orders” card shows every order, not only orders above the agreed threshold. I have seen this often when teams move from Excel reports into Power BI Desktop. The report already has clean data from Excel, SharePoint, or ... <a title="Power BI DAX FILTER: A Practical Guide With Examples" class="read-more" href="https://www.spguides.com/power-bi-dax-filter/" aria-label="Read more about Power BI DAX FILTER: A Practical Guide With Examples">read more...</a>]]></description>
										<content:encoded><![CDATA[
<p>A sales manager opens a monthly dashboard and notices something confusing. Total sales look right, but the “High-Value Orders” card shows every order, not only orders above the agreed threshold. I have seen this often when teams move from Excel reports into <strong>Power BI Desktop</strong>.</p>



<p>The report already has clean data from Excel, <a href="https://www.spguides.com/live-training/">SharePoint</a>, or SQL Server. The missing piece is a way to tell a calculation exactly which rows matter. That is where <strong>Power BI DAX FILTER</strong> becomes useful.</p>



<p>This guide walks through how to use the <strong>FILTER</strong> function in a practical retail sales dashboard, including common patterns, measures, visuals, and mistakes to avoid.</p>



<h2 class="wp-block-heading">What Is Power BI DAX FILTER?</h2>



<p><strong>FILTER</strong> is a DAX function that returns a table containing only the rows that meet a condition. DAX stands for Data Analysis Expressions, the formula language that powers calculations in <strong>Power BI</strong>.</p>



<p>Unlike a visual-level filter or a slicer, <strong>FILTER</strong> works inside a formula. It lets you define custom logic for a <strong>Measure</strong>, which is a calculation that changes based on the current filters in your report.</p>



<p>The syntax looks like this:</p>



<pre class="wp-block-code"><code>FILTER(&lt;table&gt;, &lt;filter_expression&gt;)</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="e7eaed" data-has-transparency="false" style="--dominant-color: #e7eaed;" loading="lazy" decoding="async" width="420" height="136" sizes="(max-width: 420px) 100vw, 420px" src="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-function-with-examples.avif" alt="Power BI DAX FILTER function with examples" class="wp-image-137352 not-transparent" title="Power BI DAX FILTER function with" srcset="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-function-with-examples.avif 420w, https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-function-with-examples-300x97.avif 300w" /></figure></div>


<ul class="wp-block-list">
<li>&lt;table&gt; is the table that you want to examine.</li>



<li>&lt;filter_expression&gt; is the condition that each row must satisfy.</li>
</ul>



<p>For example, imagine a Sales table with these columns:</p>



<ul class="wp-block-list">
<li>Order Date</li>



<li>Product</li>



<li>Region</li>



<li>Salesperson</li>



<li>Amount</li>



<li>Quantity</li>
</ul>



<p>This formula returns sales rows where the order amount exceeds 1,000:</p>



<pre class="wp-block-code"><code>FILTER(
    Sales,
    Sales&#91;Amount] &gt; 1000
)</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f4f4f4" data-has-transparency="false" style="--dominant-color: #f4f4f4;" loading="lazy" decoding="async" width="982" height="803" sizes="(max-width: 982px) 100vw, 982px" src="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-function-explained-with-examples.avif" alt="Power BI DAX FILTER function explained with examples" class="wp-image-137353 not-transparent" title="Power BI DAX FILTER function explained with" srcset="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-function-explained-with-examples.avif 982w, https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-function-explained-with-examples-300x245.avif 300w, https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-function-explained-with-examples-768x628.avif 768w" /></figure></div>


<p>By itself, <strong>FILTER</strong> returns a table, not a single number. That is why you will usually use it inside another DAX function, especially <strong>CALCULATE</strong>.</p>



<p>For a wider introduction to DAX concepts before building more advanced formulas, read this guide on <a href="https://www.spguides.com/power-bi-dax/">Power BI DAX</a>.</p>



<h2 class="wp-block-heading">Why Use Power BI DAX FILTER?</h2>



<p>A standard report filter works well when every visual needs the same rule. However, real dashboards often need one visual to follow a special business rule while other visuals keep showing all data.</p>



<p>For example, a retail company may want these KPIs on one sales dashboard:</p>



<ul class="wp-block-list">
<li>Total sales from every completed order.</li>



<li>Sales from the West region only.</li>



<li>Revenue from orders above 1,000.</li>



<li>Revenue from orders created in the last 30 days.</li>



<li>Sales from products with a margin greater than 25 percent.</li>
</ul>



<p>You could create separate pages or apply visual filters. But that approach becomes difficult to maintain when users need several calculations together in a single <strong>interactive report</strong>.</p>



<p><strong>Power BI DAX FILTER</strong> gives each measure its own rule. Your total sales card can show all sales, while a second card shows only high-value sales. Both cards can still respond to the same Year, Region, and Product <strong>Slicers</strong>.</p>



<blockquote class="wp-block-quote has-contrast-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p class="has-base-2-background-color has-background"><strong>Pro Tip:</strong> I have found that measures with clear business names save a lot of support time later. Name a measure <code>High Value Sales</code> instead of <code>Measure 7</code>, and document the threshold in the formula or model description.</p>
</blockquote>



<h2 class="wp-block-heading">Build the Sales Data Model First</h2>



<p>Before writing DAX, set up a clean <strong>Data model</strong>. In client projects, I usually start with a simple star schema. A star schema keeps one large transaction table in the center and connects it to smaller lookup tables.</p>



<p>For this example, use these tables:</p>



<ul class="wp-block-list">
<li><strong>Sales</strong>: Fact table with one row per sales order line.</li>



<li><strong>Date</strong>: Calendar table with one row per date.</li>



<li><strong>Products</strong>: Product name, category, and brand.</li>



<li><strong>Customers</strong>: Customer name, segment, and city.</li>



<li><strong>Regions</strong>: Region and sales territory details.</li>
</ul>



<p>The <strong>Sales</strong> table sits in the center. Create one-to-many relationships from the lookup tables to <code>Sales</code>:</p>



<ul class="wp-block-list">
<li>Date[Date] to Sales[Order Date]</li>



<li>Products[Product ID] to Sales[Product ID]</li>



<li>Customers[Customer ID] to Sales[Customer ID]</li>



<li>Regions[Region ID] to Sales[Region ID]</li>
</ul>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-dominant-color="eceded" data-has-transparency="false" style="--dominant-color: #eceded;" loading="lazy" decoding="async" width="1024" height="477" sizes="(max-width: 1024px) 100vw, 1024px" src="https://www.spguides.com/wp-content/uploads/2026/09/How-to-use-FILTER-function-in-Power-BI-DAX-1024x477.avif" alt="How to use FILTER function in Power BI DAX" class="wp-image-137354 not-transparent" title="How to use FILTER function in Power BI DAX" srcset="https://www.spguides.com/wp-content/uploads/2026/09/How-to-use-FILTER-function-in-Power-BI-DAX-1024x477.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/09/How-to-use-FILTER-function-in-Power-BI-DAX-300x140.avif 300w, https://www.spguides.com/wp-content/uploads/2026/09/How-to-use-FILTER-function-in-Power-BI-DAX-768x358.avif 768w, https://www.spguides.com/wp-content/uploads/2026/09/How-to-use-FILTER-function-in-Power-BI-DAX.avif 1339w" /></figure></div>


<p>Use a single filter direction from each lookup table to the <strong>Sales</strong> table. This design makes DAX measures easier to understand and helps prevent incorrect totals.</p>



<p>You can also learn how to work with relationships when unique values are unavailable in this guide about <a href="https://www.spguides.com/create-a-relationship-in-power-bi-without-unique-values/">creating a relationship in Power BI without unique values</a>.</p>



<h3 class="wp-block-heading">Create and Mark a Date Table</h3>



<p>A separate <strong>Date table</strong> gives your report a reliable calendar for monthly, quarterly, and yearly analysis. It also supports time-intelligence calculations.</p>



<p>In <strong>Power BI Desktop</strong>, select <strong>Modeling</strong> &gt; <strong>New table</strong>, then enter:</p>



<pre class="wp-block-code"><code>Date =
CALENDAR(
    DATE(2023, 1, 1),
    DATE(2026, 12, 31)
)</code></pre>



<p>Add supporting columns:</p>



<pre class="wp-block-code"><code>Year = YEAR('Date'&#91;Date])</code></pre>



<pre class="wp-block-code"><code>Month Number = MONTH('Date'&#91;Date])</code></pre>



<pre class="wp-block-code"><code>Month Name = FORMAT('Date'&#91;Date], "MMMM")</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f2f2f2" data-has-transparency="false" style="--dominant-color: #f2f2f2;" loading="lazy" decoding="async" width="540" height="736" sizes="(max-width: 540px) 100vw, 540px" src="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-with-CALCULATE-examples.avif" alt="Power BI DAX FILTER with CALCULATE examples" class="wp-image-137355 not-transparent" title="Power BI DAX FILTER with CALCULATE" srcset="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-with-CALCULATE-examples.avif 540w, https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-with-CALCULATE-examples-220x300.avif 220w" /></figure></div>


<p>Next, select the <code>Date</code> table, choose <strong>Table tools</strong> &gt; <strong>Mark as date table</strong>, and select the <code>Date</code> column. Then sort Month Name by Month Number so months appear in calendar order instead of alphabetical order.</p>



<p>If you need a user-friendly date selection control, this article explains how to create a <a href="https://www.spguides.com/power-bi-date-slicer/">Power BI date slicer</a>.</p>



<h2 class="wp-block-heading">Create the Base Measure</h2>



<p>Always create a base measure before adding complex filters. In this sales dashboard, the basic measure is total sales:</p>



<pre class="wp-block-code"><code>Total Sales =
SUM(Sales&#91;Amount])</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f4f5f5" data-has-transparency="false" style="--dominant-color: #f4f5f5;" loading="lazy" decoding="async" width="595" height="222" sizes="(max-width: 595px) 100vw, 595px" src="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-FILTER-function-with-multiple-conditions.avif" alt="Power BI FILTER function with multiple conditions" class="wp-image-137356 not-transparent" title="Power BI FILTER function with multiple conditions" srcset="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-FILTER-function-with-multiple-conditions.avif 595w, https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-FILTER-function-with-multiple-conditions-300x112.avif 300w" /></figure></div>


<p>This <strong>Measure</strong> adds the values in Sales[Amount]. It automatically responds to filters from visuals, slicers, the report page, and related tables.</p>



<p>Add Total Sales to a <strong>Card</strong> visual. Then add a clustered column chart with Products[Category] on the X-axis and Total Sales on the Y-axis. Add a line chart with Date[Month Name] and Total Sales to show the sales trend.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-dominant-color="ecf4fb" data-has-transparency="false" style="--dominant-color: #ecf4fb;" loading="lazy" decoding="async" width="1024" height="442" sizes="(max-width: 1024px) 100vw, 1024px" src="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-multiple-conditions-using-AND-1024x442.avif" alt="Power BI DAX FILTER multiple conditions using AND" class="wp-image-137357 not-transparent" title="Power BI DAX FILTER multiple conditions using AND" srcset="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-multiple-conditions-using-AND-1024x442.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-multiple-conditions-using-AND-300x130.avif 300w, https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-multiple-conditions-using-AND-768x332.avif 768w, https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-multiple-conditions-using-AND.avif 1318w" /></figure></div>


<p>A well-designed measure makes future formulas much shorter. Instead of repeating SUM(Sales[Amount]) in every formula, you reuse [Total Sales].</p>



<p>This is also why a <strong>Measure</strong> differs from a calculated column. A calculated column stores a value for every row during refresh, while a measure calculates results only when users view the report. See this detailed explanation of the <a href="https://www.spguides.com/what-is-the-difference-between-calculated-column-and-measure-in-power-bi/">difference between a calculated column and measure in Power BI</a>.</p>



<h2 class="wp-block-heading">Power BI DAX FILTER With CALCULATE</h2>



<p><strong>CALCULATE</strong> changes the filter context of a calculation. Filter context means the set of rows that Power BI considers when it calculates a measure.</p>



<p>The structure is:</p>



<pre class="wp-block-code"><code>CALCULATE(
    &lt;expression&gt;,
    &lt;filter&gt;
)</code></pre>



<p>To calculate sales for orders above 1,000, create this measure:</p>



<pre class="wp-block-code"><code>High Value Sales =
CALCULATE(
    &#91;Total Sales],
    FILTER(
        Sales,
        Sales&#91;Amount] &gt; 1000
    )
)</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f6f6f6" data-has-transparency="false" style="--dominant-color: #f6f6f6;" loading="lazy" decoding="async" width="594" height="372" sizes="(max-width: 594px) 100vw, 594px" src="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-with-RELATED-function.avif" alt="Power BI DAX FILTER with RELATED function" class="wp-image-137358 not-transparent" title="Power BI DAX FILTER with RELATED function" srcset="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-with-RELATED-function.avif 594w, https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-with-RELATED-function-300x188.avif 300w" /></figure></div>


<p>Here is what happens:</p>



<ol class="wp-block-list">
<li>[Total Sales] tells Power BI what to calculate.</li>



<li>FILTER(Sales, Sales[Amount] &gt; 1000) keeps only rows where the amount is greater than 1,000.</li>



<li><code>CALCULATE</code> evaluates <code>Total Sales</code> using those filtered rows.</li>
</ol>



<p>Add this measure to a second <strong>Card</strong> visual beside Total Sales. A manager can now compare overall revenue with revenue from high-value orders.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="eff0f0" data-has-transparency="false" style="--dominant-color: #eff0f0;" loading="lazy" decoding="async" width="521" height="302" sizes="(max-width: 521px) 100vw, 521px" src="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-sales-above-1000.avif" alt="Power BI DAX FILTER sales above 1000" class="wp-image-137359 not-transparent" title="Power BI DAX FILTER sales above 1000" srcset="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-sales-above-1000.avif 521w, https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-sales-above-1000-300x174.avif 300w" /></figure></div>


<p>The formula remains interactive. If the manager selects the South region in a <strong>Slicer</strong>, both the card and the formula only consider South region sales. Then the <code>FILTER</code> condition further keeps orders above 1,000.</p>



<p>For additional examples with multiple conditions, review this guide on <a href="https://www.spguides.com/power-bi-dax-count-data-with-multiple-filter-conditions/">counting data with multiple filter conditions in Power BI DAX</a>.</p>



<h3 class="wp-block-heading">Filter Rows With Multiple Conditions in Power BI</h3>



<p>You can combine several conditions with the <code>&amp;&amp;</code> operator, which means “and.” Suppose your sales director wants revenue from high-value Technology orders:</p>



<pre class="wp-block-code"><code>High Value Technology Sales =
CALCULATE(
    &#91;Total Sales],
    FILTER(
        Sales,
        Sales&#91;Amount] &gt; 1000 &amp;&amp;
        RELATED(Products&#91;Category]) = "Technology"
    )
)</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f6f6f7" data-has-transparency="false" style="--dominant-color: #f6f6f7;" loading="lazy" decoding="async" width="609" height="218" sizes="(max-width: 609px) 100vw, 609px" src="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-high-value-sales-example.avif" alt="Power BI DAX FILTER high value sales example" class="wp-image-137360 not-transparent" title="Power BI DAX FILTER high value sales" srcset="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-high-value-sales-example.avif 609w, https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-high-value-sales-example-300x107.avif 300w" /></figure></div>


<p>This formula checks each row in Sales. It keeps the row only when the amount exceeds 1,000 and the related product category equals Technology.</p>



<p><strong>RELATED</strong> retrieves a value from a related lookup table. It works well here because <code>Products</code> has a relationship with Sales.</p>



<p>If your condition uses columns from only one table, a simpler Boolean filter often works:</p>



<pre class="wp-block-code"><code>West Region Sales =
CALCULATE(
    &#91;Total Sales],
    Regions&#91;Region Name] = "West"
)</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f6f6f6" data-has-transparency="false" style="--dominant-color: #f6f6f6;" loading="lazy" decoding="async" width="585" height="256" sizes="(max-width: 585px) 100vw, 585px" src="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-by-date-examples.avif" alt="Power BI DAX FILTER by date examples" class="wp-image-137361 not-transparent" title="Power BI DAX FILTER by date" srcset="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-by-date-examples.avif 585w, https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-by-date-examples-300x131.avif 300w" /></figure></div>


<p>Use the simpler filter when possible. Use <strong>FILTER</strong> when your logic needs row-by-row evaluation, involves a measure, uses more complex comparisons, or needs functions such as <code>RELATED</code>.</p>



<h2 class="wp-block-heading">Filter Sales by Date in Power BI</h2>



<p>Date-based filters are among the most common uses of <strong>Power BI DAX FILTER</strong>. A sales manager often asks, “How much did we sell in the last 30 days?” or “What did this year’s revenue look like?”</p>



<p>Create a measure for sales in the last 30 days:</p>



<pre class="wp-block-code"><code>Sales Last 30 Days =
CALCULATE(
    &#91;Total Sales],
    FILTER(
        ALL('Date'&#91;Date]),
        'Date'&#91;Date] &gt; TODAY() - 30 &amp;&amp;
        'Date'&#91;Date] &lt;= TODAY()
    )
)</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f6f6f7" data-has-transparency="false" style="--dominant-color: #f6f6f7;" loading="lazy" decoding="async" width="589" height="335" sizes="(max-width: 589px) 100vw, 589px" src="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-last-30-days-example.avif" alt="Power BI DAX FILTER last 30 days example" class="wp-image-137362 not-transparent" title="Power BI DAX FILTER last 30 days" srcset="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-last-30-days-example.avif 589w, https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-last-30-days-example-300x171.avif 300w" /></figure></div>


<p>ALL(&#8216;Date'[Date]) removes the existing filter from the Date column before the formula applies its own 30-day date range. Without it, a Month slicer might limit the available dates and create unexpected results.</p>



<p>Then FILTER keeps dates from the past 30 days through today. Power BI sends that date filter through the relationship to the Sales table.</p>



<p>Use this measure in a card, and place it next to a card for monthly sales. It gives leaders a rolling view that does not reset when a new month begins.</p>



<p>If date logic does not behave as expected, start with this guide on fixing a <a href="https://www.spguides.com/power-bi-dax-date-filter-not-working/">Power BI DAX date filter that is not working</a>. For other rolling-period examples, see how to <a href="https://www.spguides.com/filter-last-n-days-data-using-power-bi-dax/">filter last N days of data using Power BI DAX</a>.</p>



<h3 class="wp-block-heading">Compare Current Sales to Last Year in Power BI</h3>



<p>A proper Date table also makes year-over-year comparisons simple:</p>



<pre class="wp-block-code"><code>Sales LY =
CALCULATE(
    &#91;Total Sales],
    SAMEPERIODLASTYEAR('Date'&#91;Date])
)</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f7f7f7" data-has-transparency="false" style="--dominant-color: #f7f7f7;" loading="lazy" decoding="async" width="580" height="246" sizes="(max-width: 580px) 100vw, 580px" src="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-with-ALL-function.avif" alt="Power BI DAX FILTER with ALL function" class="wp-image-137363 not-transparent" title="Power BI DAX FILTER with ALL function" srcset="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-with-ALL-function.avif 580w, https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-with-ALL-function-300x127.avif 300w" /></figure></div>


<p>SAMEPERIODLASTYEAR shifts the current date selection back one year. If a user selects March 2026, the measure returns sales for March 2025.</p>



<p>Create a variance measure:</p>



<pre class="wp-block-code"><code>Sales Variance =
&#91;Total Sales] - &#91;Sales LY]</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f4f4f5" data-has-transparency="false" style="--dominant-color: #f4f4f5;" loading="lazy" decoding="async" width="588" height="228" sizes="(max-width: 588px) 100vw, 588px" src="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-remove-existing-filters.avif" alt="Power BI DAX FILTER remove existing filters" class="wp-image-137364 not-transparent" title="Power BI DAX FILTER remove existing filters" srcset="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-remove-existing-filters.avif 588w, https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-remove-existing-filters-300x116.avif 300w" /></figure></div>


<p>Create a percentage measure:</p>



<pre class="wp-block-code"><code>Sales Growth % =
DIVIDE(
    &#91;Sales Variance],
    &#91;Sales LY],
    0
)</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f5f6f6" data-has-transparency="false" style="--dominant-color: #f5f6f6;" loading="lazy" decoding="async" width="593" height="325" sizes="(max-width: 593px) 100vw, 593px" src="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-with-REMOVEFILTERS.avif" alt="Power BI DAX FILTER with REMOVEFILTERS" class="wp-image-137365 not-transparent" title="Power BI DAX FILTER with REMOVEFILTERS" srcset="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-with-REMOVEFILTERS.avif 593w, https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-with-REMOVEFILTERS-300x164.avif 300w" /></figure></div>


<p>Format Sales Growth % as a percentage. Add Total Sales, Sales LY, and Sales Growth % to cards. Then use conditional formatting to show positive growth in green and negative growth in red.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="fafafa" data-has-transparency="false" style="--dominant-color: #fafafa;" loading="lazy" decoding="async" width="962" height="199" sizes="(max-width: 962px) 100vw, 962px" src="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-ignore-slicer-selection.avif" alt="Power BI DAX FILTER ignore slicer selection" class="wp-image-137366 not-transparent" title="Power BI DAX FILTER ignore slicer selection" srcset="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-ignore-slicer-selection.avif 962w, https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-ignore-slicer-selection-300x62.avif 300w, https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-ignore-slicer-selection-768x159.avif 768w" /></figure></div>


<p>For a separate practical example, explore <a href="https://www.spguides.com/power-bi-month-over-month-change/">month-over-month change in Power BI</a>.</p>



<h2 class="wp-block-heading">Use FILTER to Ignore a Visual Selection</h2>



<p>Sometimes a measure must keep one filter but ignore another. For example, a category bar chart should show each category’s sales as a percentage of the selected region total.</p>



<p>Start with this measure:</p>



<pre class="wp-block-code"><code>Category Sales % of Region =
DIVIDE(
    &#91;Total Sales],
    CALCULATE(
        &#91;Total Sales],
        ALL(Products&#91;Category])
    ),
    0
)</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f5f6f6" data-has-transparency="false" style="--dominant-color: #f5f6f6;" loading="lazy" decoding="async" width="587" height="377" sizes="(max-width: 587px) 100vw, 587px" src="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-ignore-product-selection.avif" alt="Power BI DAX FILTER ignore product selection" class="wp-image-137367 not-transparent" title="Power BI DAX FILTER ignore product selection" srcset="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-ignore-product-selection.avif 587w, https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-ignore-product-selection-300x193.avif 300w" /></figure></div>


<p>ALL(Products[Category]) removes the category filter only. It does not remove filters for Region, Year, or Customer Segment. This makes the denominator the total sales for the current region and date selection.</p>



<p>Now consider a stricter example. Your sales team may need sales that ignore the Product slicer but still respect the Region slicer:</p>



<pre class="wp-block-code"><code>Sales Ignoring Product Selection =
CALCULATE(
    &#91;Total Sales],
    REMOVEFILTERS(Products)
)</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f4f4f5" data-has-transparency="false" style="--dominant-color: #f4f4f5;" loading="lazy" decoding="async" width="591" height="295" sizes="(max-width: 591px) 100vw, 591px" src="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-sales-by-region-example.avif" alt="Power BI DAX FILTER sales by region example" class="wp-image-137368 not-transparent" title="Power BI DAX FILTER sales by region" srcset="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-sales-by-region-example.avif 591w, https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-sales-by-region-example-300x150.avif 300w" /></figure></div>


<p><strong>REMOVEFILTERS</strong> clearly states that you want to remove filters from a table or column. It often reads better than ALL when the goal is filter removal.</p>



<p>Choose these patterns carefully. Removing too many filters can produce a number that users cannot explain. Use a clear visual title such as “Share of Selected Region Sales” so people understand what the KPI represents.</p>



<p>For more examples, see this guide on how to <a href="https://www.spguides.com/remove-filters-in-power-bi-dax/">remove filters in Power BI DAX</a>.</p>



<h2 class="wp-block-heading">Build the Interactive Report in Power BI</h2>



<p>Now turn the measures into a useful <strong>sales dashboard</strong>. I usually create one executive summary page first, rather than placing every available visual on the canvas.</p>



<p>Use this layout:</p>



<ul class="wp-block-list">
<li>Add three <strong>Card</strong> visuals for Total Sales, High Value Sales, and <code>Sales Growth %</code>.</li>



<li>Add a line chart showing Total Sales by month.</li>



<li>Add a clustered bar chart showing Total Sales by product category.</li>



<li>Add a matrix with Region, Salesperson, Total Sales, High Value Sales.</li>



<li>Add slicers for Year, Region, Product Category, and Customer Segment.</li>
</ul>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-dominant-color="eff5fa" data-has-transparency="false" style="--dominant-color: #eff5fa;" loading="lazy" decoding="async" width="1024" height="573" sizes="(max-width: 1024px) 100vw, 1024px" src="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-A-Practical-Guide-With-Examples-1024x573.avif" alt="Power BI DAX FILTER A Practical Guide With Examples" class="wp-image-137369 not-transparent" title="Power BI DAX FILTER A Practical Guide With Examples" srcset="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-A-Practical-Guide-With-Examples-1024x573.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-A-Practical-Guide-With-Examples-300x168.avif 300w, https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-A-Practical-Guide-With-Examples-768x430.avif 768w, https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-DAX-FILTER-A-Practical-Guide-With-Examples.avif 1115w" /></figure></div>


<p>Keep the cards at the top because managers often need answers within seconds. Use the line chart to show direction over time, then use the category chart and matrix to explain the reason behind the trend.</p>



<p>When the report looks correct, publish it to a <strong>Power BI Service</strong> workspace. A workspace is a shared online area where a team can manage reports, dashboards, datasets, and access.</p>



<p>Before sharing, test each slicer with your DAX measures. Select a single region, multiple categories, and a date range. Check whether the business result matches the intended definition.</p>



<blockquote class="wp-block-quote has-contrast-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p class="has-base-2-background-color has-background"><strong>Pro Tip:</strong> In my experience, a measure is only complete after someone tests it with unexpected slicer combinations. Most DAX issues come from filter context, not from spelling errors in the formula.</p>
</blockquote>



<h2 class="wp-block-heading">Key Points</h2>



<ul class="wp-block-list">
<li><strong>Use a star schema:</strong> Keep dimension tables such as Date, Products, and Regions separate from the Sales fact table. Clear relationships make filter flow predictable.</li>



<li><strong>Create base measures first:</strong> Reuse measures such as <code>[Total Sales]</code> inside other DAX measures. This keeps formulas shorter and easier to maintain.</li>



<li><strong>Avoid unnecessary FILTER calls:</strong> A direct filter inside <code>CALCULATE</code> usually performs better when the condition only compares a column to a fixed value.</li>



<li><strong>Check blank values:</strong> Blank dates, missing region IDs, and blank amounts can change results. Review data quality in <strong>Power Query</strong> before blaming the DAX formula.</li>



<li><strong>Use Row-Level Security carefully:</strong> <strong>Row-Level Security</strong> restricts data for specific users. Test DAX measures under every security role before publishing sensitive sales data.</li>



<li><strong>Make business rules visible:</strong> Include the threshold in a visual title or tooltip. Users should know whether “High Value” means orders above 1,000 or another amount.</li>
</ul>



<h2 class="wp-block-heading">Frequently Asked Questions</h2>


<div id="rank-math-faq" class="rank-math-block">
<div class="rank-math-list ">
<div id="faq-question-1788759278162" class="rank-math-list-item">
<h3 class="rank-math-question ">What does FILTER do in Power BI DAX?</h3>
<div class="rank-math-answer ">

<p><strong>FILTER</strong> returns a table that contains rows meeting a condition. You commonly place it inside <strong>CALCULATE</strong> to apply custom filtering to a measure, such as sales above a certain order value.</p>

</div>
</div>
<div id="faq-question-1788759288693" class="rank-math-list-item">
<h3 class="rank-math-question ">What is the difference between FILTER and CALCULATE in Power BI?</h3>
<div class="rank-math-answer ">

<p><strong>FILTER</strong> creates a filtered table based on row-level logic. <strong>CALCULATE</strong> evaluates a measure after changing its filter context. They often work together because CALCULATE needs a filter argument, while FILTER can provide a complex filtered table.</p>

</div>
</div>
<div id="faq-question-1788759300701" class="rank-math-list-item">
<h3 class="rank-math-question ">When should I use FILTER in a DAX measure?</h3>
<div class="rank-math-answer ">

<p>Use <strong>FILTER</strong> when a direct condition does not handle your business rule. Good examples include comparing rows to a measure, filtering based on related-table values, or applying several calculated conditions together.</p>

</div>
</div>
<div id="faq-question-1788759325130" class="rank-math-list-item">
<h3 class="rank-math-question ">Why is my Power BI DAX FILTER measure returning the wrong total?</h3>
<div class="rank-math-answer ">

<p>The most common cause is an unexpected filter context from a slicer, relationship, or visual. Check relationships first, then test the measure in a table visual with the columns used in the formula.</p>

</div>
</div>
<div id="faq-question-1788759348913" class="rank-math-list-item">
<h3 class="rank-math-question ">Can I use FILTER with multiple conditions in Power BI?</h3>
<div class="rank-math-answer ">

<p>Yes. Use &amp;&amp; when every condition must be true and || when at least one condition must be true. Keep the formula readable by placing each condition on a separate line.</p>

</div>
</div>
<div id="faq-question-1788759359898" class="rank-math-list-item">
<h3 class="rank-math-question ">Does FILTER remove existing filters in Power BI?</h3>
<div class="rank-math-answer ">

<p>No. <strong>FILTER</strong> usually adds to the filters already applied by the report. Use <code>ALL</code> or <code>REMOVEFILTERS</code> inside <code>CALCULATE</code> when you need to clear filters from a specific table or column.</p>

</div>
</div>
</div>
</div>


<p><strong>Power BI DAX FILTER</strong> lets you create focused, reliable measures for high-value orders, rolling sales periods, and custom KPI logic. Start with a clean data model and reusable base measures, then use <strong>CALCULATE</strong> and <strong>FILTER</strong> only where the business rule truly needs them. I hope you found this article helpful.</p>



<p>You may also like</p>



<ul class="wp-block-list">
<li><a href="https://www.spguides.com/power-bi-dax-filter-with-selectedvalue/">SELECTEDVALUE with Power BI DAX FILTER</a></li>



<li><a href="https://www.spguides.com/filter-blank-value-in-power-bi/">Filter blank values in Power BI</a></li>



<li><a href="https://www.spguides.com/add-a-dropdown-slicer-in-power-bi/">Create a dropdown slicer in Power BI</a></li>



<li><a href="https://www.spguides.com/power-bi-row-level-security/">Set up Row-Level Security in Power BI</a></li>



<li><a href="https://www.spguides.com/create-a-report-in-power-bi-desktop/">Create a report in Power BI Desktop</a></li>
</ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Power BI Date Slicer by Month: How to Use It</title>
		<link>https://www.spguides.com/power-bi-date-slicer-by-month/</link>
		
		<dc:creator><![CDATA[Bijay Kumar]]></dc:creator>
		<pubDate>Tue, 01 Sep 2026 11:22:59 +0000</pubDate>
				<category><![CDATA[Power BI]]></category>
		<category><![CDATA[Power BI Date Slicer By Month]]></category>
		<guid isPermaLink="false">https://www.spguides.com/?p=60718</guid>

					<description><![CDATA[A sales manager opens a monthly dashboard and wants a quick answer: “How did March perform compared with February?” In many early Power BI reports, the date filter shows every single day. That creates a long, messy list that users avoid. I have seen this issue often when teams move from Excel reports to a ... <a title="Power BI Date Slicer by Month: How to Use It" class="read-more" href="https://www.spguides.com/power-bi-date-slicer-by-month/" aria-label="Read more about Power BI Date Slicer by Month: How to Use It">read more...</a>]]></description>
										<content:encoded><![CDATA[
<p>A sales manager opens a monthly dashboard and wants a quick answer: “How did March perform compared with February?” In many early <a href="https://www.spguides.com/power-bi-report-export-to-pdf/">Power BI reports</a>, the date filter shows every single day. That creates a long, messy list that users avoid.</p>



<p>I have seen this issue often when teams move from Excel reports to a proper <strong>Power BI sales dashboard</strong>. A clean month slicer gives users a familiar way to explore revenue, orders, profit, and KPIs without touching the underlying data.</p>



<p>This practical <strong>Power BI tutorial</strong> shows how to build a reliable <strong>Power BI date slicer by month</strong>, sort it correctly, connect it to visuals, and avoid common date-model mistakes.</p>



<h2 class="wp-block-heading">Why Use a Power BI Date Slicer by Month?</h2>



<p>A <strong>Slicer</strong> is a visual filter that lets report users choose values directly on the report canvas. When users select a month, Power BI filters connected visuals such as cards, bar charts, tables, and line charts.</p>



<p>For a retail sales dashboard, a monthly slicer helps a regional manager focus on one period without creating separate report pages. They can select January to review the new-year campaign, then switch to December to check holiday performance.</p>



<p>A month slicer works best when users ask questions like these:</p>



<ul class="wp-block-list">
<li>What were total sales in April?</li>



<li>Which product category performed best last month?</li>



<li>Did the South region meet its monthly target?</li>



<li>How do current-month sales compare with the same month last year?</li>



<li>Which month had the highest profit?</li>
</ul>



<p>The most important point is this: do not build the slicer directly from a random date column if the report will grow. Create a dedicated <a href="https://www.spguides.com/power-bi-sort-table-by-date/">Date table</a> first. A Date table is a separate table that contains one row per calendar date and supports consistent time-based analysis.</p>



<h2 class="wp-block-heading">Power BI Date Slicer by Month: Example Setup</h2>



<p>For this guide, I will use a sales dashboard for a mid-sized retail company. The company stores transaction data in an Excel file, but the same approach works with <a href="https://www.spguides.com/sharepoint-tutorials/">SharePoint Online</a>, SQL Server, Dataverse, and other sources.</p>



<p>The main table is called <strong>Sales</strong> and includes these columns:</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>Column</th><th>Example value</th><th>Purpose</th></tr></thead><tbody><tr><td>OrderDate</td><td>2026-03-15</td><td>Date when the sale happened</td></tr><tr><td>SalesAmount</td><td>1250</td><td>Revenue from the transaction</td></tr><tr><td>ProductCategory</td><td>Electronics</td><td>Product grouping</td></tr><tr><td>Region</td><td>East</td><td>Sales territory</td></tr><tr><td>OrderID</td><td>SO-10245</td><td>Unique order reference</td></tr></tbody></table></figure>



<p>Start by loading the table into <strong>Power BI Desktop</strong>.</p>



<ol class="wp-block-list">
<li>Open <strong>Power BI Desktop</strong>.</li>



<li>Select <strong>Get data</strong> from the Home ribbon.</li>



<li>Choose <strong>Excel workbook</strong> if your data comes from an <a href="https://www.spguides.com/wp-content/uploads/2026/09/PowerBI_Month_Slicer_Sales_Data.xlsx">Excel file</a>.</li>



<li>Select the worksheet or table that contains the sales data.</li>



<li>Click <strong>Load</strong>.</li>
</ol>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-dominant-color="f9f9f9" data-has-transparency="false" style="--dominant-color: #f9f9f9;" loading="lazy" decoding="async" width="1024" height="388" sizes="(max-width: 1024px) 100vw, 1024px" src="https://www.spguides.com/wp-content/uploads/2026/09/How-to-Create-a-Power-BI-Date-Slicer-by-Month-and-Year-1024x388.avif" alt="How to Create a Power BI Date Slicer by Month and Year" class="wp-image-137234 not-transparent" title="How to Create a Power BI Date Slicer by Month and Year" srcset="https://www.spguides.com/wp-content/uploads/2026/09/How-to-Create-a-Power-BI-Date-Slicer-by-Month-and-Year-1024x388.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/09/How-to-Create-a-Power-BI-Date-Slicer-by-Month-and-Year-300x114.avif 300w, https://www.spguides.com/wp-content/uploads/2026/09/How-to-Create-a-Power-BI-Date-Slicer-by-Month-and-Year-768x291.avif 768w, https://www.spguides.com/wp-content/uploads/2026/09/How-to-Create-a-Power-BI-Date-Slicer-by-Month-and-Year-1536x583.avif 1536w, https://www.spguides.com/wp-content/uploads/2026/09/How-to-Create-a-Power-BI-Date-Slicer-by-Month-and-Year-2048x777.avif 2048w" /></figure></div>


<p>Before you add a slicer, check the <strong>OrderDate</strong> column. Select the column in Data view, open <strong>Column tools</strong>, and confirm that its data type is <strong>Date</strong> or <strong>Date/Time</strong>.</p>



<p>A date stored as text creates many problems. Power BI may display it in the wrong format, sort it incorrectly, or fail to calculate year-over-year measures.</p>



<h2 class="wp-block-heading">Create a Proper Date Table in Power BI</h2>



<p>A dedicated <strong>Date table</strong> gives every report visual a single, consistent calendar. It also lets you add useful fields such as Month Name, Month Number, Quarter, and Year-Month.</p>



<p>In <strong>Modeling</strong>, select <strong>New table</strong> and enter this DAX formula:</p>



<pre class="wp-block-code"><code>Date =
ADDCOLUMNS(
    CALENDAR(
        MIN(Sales&#91;OrderDate]),
        MAX(Sales&#91;OrderDate])
    ),
    "Year", YEAR(&#91;Date]),
    "Month Number", MONTH(&#91;Date]),
    "Month Name", FORMAT(&#91;Date], "MMMM"),
    "Month Short Name", FORMAT(&#91;Date], "MMM"),
    "Year Month", FORMAT(&#91;Date], "YYYY-MM"),
    "Year Month Sort", YEAR(&#91;Date]) * 100 + MONTH(&#91;Date]),
    "Quarter", "Q" &amp; FORMAT(&#91;Date], "Q")
)</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f4f4f5" data-has-transparency="false" style="--dominant-color: #f4f4f5;" loading="lazy" decoding="async" width="721" height="489" sizes="(max-width: 721px) 100vw, 721px" src="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-Date-Slicer-by-Month-Step-by-Step-Guide.avif" alt="Power BI Date Slicer by Month Step-by-Step Guide" class="wp-image-137235 not-transparent" title="Power BI Date Slicer by Month Step by Step Guide" srcset="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-Date-Slicer-by-Month-Step-by-Step-Guide.avif 721w, https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-Date-Slicer-by-Month-Step-by-Step-Guide-300x203.avif 300w" /></figure></div>


<p>This formula creates one date row for every day between the earliest and latest order dates in the <strong>Sales</strong> table.</p>



<p>The added columns make the table useful for reporting:</p>



<ul class="wp-block-list">
<li><strong>Year</strong> returns the calendar year, such as 2026.</li>



<li><strong>Month Number</strong> returns values from 1 to 12.</li>



<li><strong>Month Name</strong> returns full names such as January and February.</li>



<li><strong>Month Short Name</strong> returns shorter labels such as Jan and Feb.</li>



<li><strong>Year Month</strong> creates labels such as 2026-03.</li>



<li><strong>Year Month Sort</strong> creates a numeric sorting value such as 202603.</li>



<li><strong>Quarter</strong> creates labels such as Q1 and Q2.</li>
</ul>



<p>Next, mark it as a <strong>Date table</strong>.</p>



<ol class="wp-block-list">
<li>Select the <strong>Date</strong> table in the Fields pane.</li>



<li>Open <strong>Table tools</strong>.</li>



<li>Select <strong>Mark as date table</strong>.</li>



<li>Choose the <strong>Date[Date]</strong> column.</li>



<li>Click <strong>OK</strong>.</li>
</ol>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-dominant-color="fafafa" data-has-transparency="false" style="--dominant-color: #fafafa;" loading="lazy" decoding="async" width="1024" height="281" sizes="(max-width: 1024px) 100vw, 1024px" src="https://www.spguides.com/wp-content/uploads/2026/09/How-to-Filter-Power-BI-Reports-by-Month-and-Year-1024x281.avif" alt="How to Filter Power BI Reports by Month and Year" class="wp-image-137236 not-transparent" title="How to Filter Power BI Reports by Month and Year" srcset="https://www.spguides.com/wp-content/uploads/2026/09/How-to-Filter-Power-BI-Reports-by-Month-and-Year-1024x281.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/09/How-to-Filter-Power-BI-Reports-by-Month-and-Year-300x82.avif 300w, https://www.spguides.com/wp-content/uploads/2026/09/How-to-Filter-Power-BI-Reports-by-Month-and-Year-768x211.avif 768w, https://www.spguides.com/wp-content/uploads/2026/09/How-to-Filter-Power-BI-Reports-by-Month-and-Year-1536x422.avif 1536w, https://www.spguides.com/wp-content/uploads/2026/09/How-to-Filter-Power-BI-Reports-by-Month-and-Year-2048x562.avif 2048w" /></figure></div>


<p>This tells Power BI that the table is your official reporting calendar. It matters when you use time-intelligence DAX measures such as year-to-date sales or same-period-last-year sales.</p>



<blockquote class="wp-block-quote has-contrast-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p class="has-base-2-background-color has-background"><strong>Pro Tip:</strong> I always create a Date table even for small reports. A report may start with one chart and one slicer, but it often grows into a multi-page dashboard. A clean date dimension prevents rework later.</p>
</blockquote>



<h2 class="wp-block-heading">Create the Relationship in Power BI</h2>



<p>A <a href="https://www.spguides.com/create-a-relationship-in-power-bi-without-unique-values/">relationship</a> connects tables in the <strong>Data model</strong>. In this example, the Date table must filter the Sales table through the order date.</p>



<ol class="wp-block-list">
<li>Open <strong>Model view</strong> from the left navigation.</li>



<li>Drag <strong>Date[Date]</strong> onto <strong>Sales[OrderDate]</strong>.</li>



<li>Confirm that the relationship has these settings:
<ul class="wp-block-list">
<li>Cardinality: <strong>One to many (1:<em>)</em></strong></li>



<li>Cross-filter direction: <strong>Single</strong></li>



<li>Relationship active: Enabled</li>
</ul>
</li>



<li>Select <strong>OK</strong>.</li>
</ol>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f7f7f7" data-has-transparency="false" style="--dominant-color: #f7f7f7;" loading="lazy" decoding="async" width="861" height="986" sizes="(max-width: 861px) 100vw, 861px" src="https://www.spguides.com/wp-content/uploads/2026/09/How-to-Add-a-Month-and-Year-Slicer-in-Power-BI.avif" alt="How to Add a Month and Year Slicer in Power BI" class="wp-image-137237 not-transparent" title="How to Add a Month and Year Slicer in Power BI" srcset="https://www.spguides.com/wp-content/uploads/2026/09/How-to-Add-a-Month-and-Year-Slicer-in-Power-BI.avif 861w, https://www.spguides.com/wp-content/uploads/2026/09/How-to-Add-a-Month-and-Year-Slicer-in-Power-BI-262x300.avif 262w, https://www.spguides.com/wp-content/uploads/2026/09/How-to-Add-a-Month-and-Year-Slicer-in-Power-BI-768x879.avif 768w" /></figure></div>


<p>The Date table should sit on the “one” side because every date appears only once. The Sales table belongs on the “many” side because multiple sales may happen on the same day.</p>



<p>This design follows a simple <strong>star schema</strong>. A star schema places transaction data in a central fact table, such as Sales, and connects it to lookup tables, such as Date, Product, Customer, and Region. It makes filters predictable and simplifies DAX measures.</p>



<h2 class="wp-block-heading">Sort Month Names Correctly in Power BI</h2>



<p>If you add <strong>Month Name</strong> to a slicer now, Power BI may sort the values alphabetically:</p>



<ul class="wp-block-list">
<li>April</li>



<li>August</li>



<li>December</li>



<li>February</li>
</ul>



<p>That order looks wrong because it ignores the calendar sequence. Fix this by sorting the month names with the numeric month field.</p>



<ol class="wp-block-list">
<li>Select the <strong>Month Name</strong> column in the Date table.</li>



<li>Open <strong>Column tools</strong>.</li>



<li>Select <a href="https://www.spguides.com/sort-slicer-by-another-column-in-power-bi/">Sort by column</a>.</li>



<li>Choose <strong>Month Number</strong>.</li>
</ol>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-dominant-color="f1f1f1" data-has-transparency="false" style="--dominant-color: #f1f1f1;" loading="lazy" decoding="async" width="1024" height="479" sizes="(max-width: 1024px) 100vw, 1024px" src="https://www.spguides.com/wp-content/uploads/2026/09/How-to-Create-a-Monthly-Date-Filter-in-Power-BI-1024x479.avif" alt="How to Create a Monthly Date Filter in Power BI" class="wp-image-137238 not-transparent" title="How to Create a Monthly Date Filter in Power BI" srcset="https://www.spguides.com/wp-content/uploads/2026/09/How-to-Create-a-Monthly-Date-Filter-in-Power-BI-1024x479.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/09/How-to-Create-a-Monthly-Date-Filter-in-Power-BI-300x140.avif 300w, https://www.spguides.com/wp-content/uploads/2026/09/How-to-Create-a-Monthly-Date-Filter-in-Power-BI-768x359.avif 768w, https://www.spguides.com/wp-content/uploads/2026/09/How-to-Create-a-Monthly-Date-Filter-in-Power-BI.avif 1112w" /></figure></div>


<p>Now Power BI will show January through December in the correct order.</p>



<p>However, a Month Name slicer alone has one important limitation. If your report contains data for multiple years, selecting “March” combines March 2025, March 2026, and every other March in your model.</p>



<p>That may be useful for seasonal analysis, but it is usually not what a sales manager expects. For normal monthly reporting, use <strong>Year Month</strong> instead.</p>



<p>Select <strong>Date[Year Month]</strong>, then choose <strong>Sort by column</strong> and select <strong>Date[Year Month Sort]</strong>. This creates a slicer with values such as:</p>



<ul class="wp-block-list">
<li>2025-11</li>



<li>2025-12</li>



<li>2026-01</li>



<li>2026-02</li>



<li>2026-03</li>
</ul>



<p>This format clearly identifies the month and year.</p>



<h2 class="wp-block-heading">Add the Power BI Date Slicer by Month</h2>



<p>Now add the interactive filter to the sales dashboard.</p>



<ol class="wp-block-list">
<li>Open <strong>Report view</strong>.</li>



<li>Select a blank part of the report canvas.</li>



<li>Choose the <a href="https://www.spguides.com/add-a-dropdown-slicer-in-power-bi/">Slicer visual</a> from the Visualizations pane.</li>



<li>Drag <strong>Date[Year Month]</strong> into the slicer Field well.</li>



<li>Resize the slicer so users can easily read each month.</li>



<li>Open the Format pane and set the slicer style to <strong>Dropdown</strong> or <strong>List</strong>.</li>
</ol>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f2f2f2" data-has-transparency="false" style="--dominant-color: #f2f2f2;" loading="lazy" decoding="async" width="985" height="491" sizes="(max-width: 985px) 100vw, 985px" src="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-Slicer-by-Month-and-Year-for-Interactive-Reports.avif" alt="Power BI Slicer by Month and Year for Interactive Reports" class="wp-image-137239 not-transparent" title="Power BI Slicer by Month and Year for Interactive Reports" srcset="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-Slicer-by-Month-and-Year-for-Interactive-Reports.avif 985w, https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-Slicer-by-Month-and-Year-for-Interactive-Reports-300x150.avif 300w, https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-Slicer-by-Month-and-Year-for-Interactive-Reports-768x383.avif 768w" /></figure></div>


<p>Use a <strong>Dropdown</strong> slicer when you have several years of data and need to save canvas space. Use a <strong>List</strong> slicer when users should quickly compare and click a few recent months.</p>



<p>For a compact executive dashboard, I usually use a dropdown slicer near the top-left corner. For an analyst page, I prefer a list slicer because the visible choices make exploration faster.</p>



<p>You can also configure the selection behavior:</p>



<ol class="wp-block-list">
<li>Select the slicer.</li>



<li>Open <strong>Format visual</strong>.</li>



<li>Expand <strong>Slicer settings</strong>.</li>



<li>Turn on <strong>Single select</strong> if users should view only one month at a time.</li>



<li>Turn off <strong>Single select</strong> if users should compare several months.</li>
</ol>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f3f3f3" data-has-transparency="false" style="--dominant-color: #f3f3f3;" loading="lazy" decoding="async" width="688" height="523" sizes="(max-width: 688px) 100vw, 688px" src="https://www.spguides.com/wp-content/uploads/2026/09/How-to-Sort-Months-Correctly-in-a-Power-BI-Slicer.avif" alt="How to Sort Months Correctly in a Power BI Slicer" class="wp-image-137240 not-transparent" title="How to Sort Months Correctly in a Power BI Slicer" srcset="https://www.spguides.com/wp-content/uploads/2026/09/How-to-Sort-Months-Correctly-in-a-Power-BI-Slicer.avif 688w, https://www.spguides.com/wp-content/uploads/2026/09/How-to-Sort-Months-Correctly-in-a-Power-BI-Slicer-300x228.avif 300w" /></figure></div>


<p>Turn on <strong>Select all</strong> only when users genuinely need it. Otherwise, it adds an unnecessary option and can confuse non-technical report users.</p>



<h2 class="wp-block-heading">Build Visuals That Respond to the Slicer</h2>



<p>A slicer becomes valuable when it filters useful visuals. Create a few basic <strong>DAX measures</strong> before building the report.</p>



<p>Select the <strong>Sales</strong> table, choose <strong>New measure</strong>, and add this formula:</p>



<pre class="wp-block-code"><code>Total Sales =
SUM(Sales&#91;SalesAmount])</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f4f4f4" data-has-transparency="false" style="--dominant-color: #f4f4f4;" loading="lazy" decoding="async" width="584" height="225" sizes="(max-width: 584px) 100vw, 584px" src="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-Date-Table-and-Month-Slicer-Tutorial.avif" alt="Power BI Date Table and Month Slicer Tutorial" class="wp-image-137241 not-transparent" title="Power BI Date Table and Month Slicer Tutorial" srcset="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-Date-Table-and-Month-Slicer-Tutorial.avif 584w, https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-Date-Table-and-Month-Slicer-Tutorial-300x116.avif 300w" /></figure></div>


<p>This <strong>Measure</strong> adds all sales amounts in the current filter context. Filter context means Power BI automatically calculates the result based on active filters, including the selected month, region, product, or page filter.</p>



<p>Create an order-count measure:</p>



<pre class="wp-block-code"><code>Total Orders =
DISTINCTCOUNT(Sales&#91;OrderID])</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f3f4f4" data-has-transparency="false" style="--dominant-color: #f3f4f4;" loading="lazy" decoding="async" width="586" height="216" sizes="(max-width: 586px) 100vw, 586px" src="https://www.spguides.com/wp-content/uploads/2026/09/How-to-Create-a-Year-Month-Slicer-in-Power-BI.avif" alt="How to Create a Year Month Slicer in Power BI" class="wp-image-137242 not-transparent" title="How to Create a Year Month Slicer in Power BI" srcset="https://www.spguides.com/wp-content/uploads/2026/09/How-to-Create-a-Year-Month-Slicer-in-Power-BI.avif 586w, https://www.spguides.com/wp-content/uploads/2026/09/How-to-Create-a-Year-Month-Slicer-in-Power-BI-300x111.avif 300w" /></figure></div>


<p>This measure counts unique order IDs. It is more reliable than a basic row count when your source may contain multiple rows for one order.</p>



<p>Now create a prior-year measure:</p>



<pre class="wp-block-code"><code>Sales LY =
CALCULATE(
    &#91;Total Sales],
    SAMEPERIODLASTYEAR(Date&#91;Date])
)</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f4f5f5" data-has-transparency="false" style="--dominant-color: #f4f5f5;" loading="lazy" decoding="async" width="588" height="289" sizes="(max-width: 588px) 100vw, 588px" src="https://www.spguides.com/wp-content/uploads/2026/09/How-to-Create-a-Sales-Dashboard-with-Month-Slicer-in-Power-BI.avif" alt="How to Create a Sales Dashboard with Month Slicer in Power BI" class="wp-image-137243 not-transparent" title="How to Create a Sales Dashboard with Month Slicer in Power BI" srcset="https://www.spguides.com/wp-content/uploads/2026/09/How-to-Create-a-Sales-Dashboard-with-Month-Slicer-in-Power-BI.avif 588w, https://www.spguides.com/wp-content/uploads/2026/09/How-to-Create-a-Sales-Dashboard-with-Month-Slicer-in-Power-BI-300x147.avif 300w" /></figure></div>


<p>The <strong>CALCULATE</strong> function changes the filter context for a measure. Here, it tells Power BI to calculate Total Sales for the same selected dates one year earlier.</p>



<p>Add a final measure for year-over-year change:</p>



<pre class="wp-block-code"><code>Sales YoY % =
DIVIDE(
    &#91;Total Sales] - &#91;Sales LY],
    &#91;Sales LY]
)</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f5f5f6" data-has-transparency="false" style="--dominant-color: #f5f5f6;" loading="lazy" decoding="async" width="605" height="284" sizes="(max-width: 605px) 100vw, 605px" src="https://www.spguides.com/wp-content/uploads/2026/09/How-to-Use-Year-Month-Sort-Column-in-Power-BI.avif" alt="How to Use Year Month Sort Column in Power BI" class="wp-image-137244 not-transparent" title="How to Use Year Month Sort Column in Power BI" srcset="https://www.spguides.com/wp-content/uploads/2026/09/How-to-Use-Year-Month-Sort-Column-in-Power-BI.avif 605w, https://www.spguides.com/wp-content/uploads/2026/09/How-to-Use-Year-Month-Sort-Column-in-Power-BI-300x141.avif 300w" /></figure></div>


<p>Format <strong>Sales YoY %</strong> as a percentage from the Measure tools ribbon.</p>



<p>Build these visuals on the report page:</p>



<ul class="wp-block-list">
<li>A <strong>Card</strong> visual for <strong>Total Sales</strong>.</li>



<li>A second Card for <strong>Total Orders</strong>.</li>



<li>A third Card for <strong>Sales YoY %</strong>.</li>



<li>A <strong>Clustered column chart</strong> with ProductCategory on the X-axis and Total Sales on the Y-axis.</li>



<li>A <strong>Line chart</strong> with Date[Date] on the X-axis and Total Sales on the Y-axis.</li>



<li>A <strong>Bar chart</strong> with Region on the Y-axis and Total Sales on the X-axis.</li>



<li>The <strong>Year Month slicer</strong> at the top of the page.</li>
</ul>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-dominant-color="e6f2fb" data-has-transparency="false" style="--dominant-color: #e6f2fb;" loading="lazy" decoding="async" width="1024" height="577" sizes="(max-width: 1024px) 100vw, 1024px" src="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-Date-Slicer-with-Month-Name-and-Month-Number-1024x577.avif" alt="Power BI Date Slicer with Month Name and Month Number" class="wp-image-137245 not-transparent" title="Power BI Date Slicer with Month Name and Month Number" srcset="https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-Date-Slicer-with-Month-Name-and-Month-Number-1024x577.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-Date-Slicer-with-Month-Name-and-Month-Number-300x169.avif 300w, https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-Date-Slicer-with-Month-Name-and-Month-Number-768x433.avif 768w, https://www.spguides.com/wp-content/uploads/2026/09/Power-BI-Date-Slicer-with-Month-Name-and-Month-Number.avif 1296w" /></figure></div>


<p>When a user picks 2026-03, every visual should update to show March 2026 data. This is the core value of an <strong>interactive report</strong>.</p>



<p>For more advanced conditional filtering, you can also use <a href="https://www.spguides.com/power-bi-dax-filter-with-selectedvalue/">DAX FILTER with SELECTEDVALUE in Power BI</a>. It helps when a measure needs to react differently based on one selected slicer value.</p>



<h2 class="wp-block-heading">Use Month and Year Slicers Separately</h2>



<p>Some teams prefer two slicers: one for Year and another for Month Name. This design works well when users want to compare the same month across selected years.</p>



<p>Add one Slicer visual with <strong>Date[Year]</strong> and another with <strong>Date[Month Name]</strong>. Because both fields come from the same Date table, they work together automatically.</p>



<p>For example, a user can select 2026 from the Year slicer and March from the Month slicer. The report then shows only March 2026.</p>



<p>This design feels familiar to people who work with Excel pivot tables. However, it needs clear labeling. A user may select a month but forget to select a year, which combines the same month across all years.</p>



<blockquote class="wp-block-quote has-contrast-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p class="has-base-2-background-color has-background"><strong>Pro Tip:</strong> In client dashboards, I use separate Year and Month slicers only when the business users ask for them. For most executive reports, one <strong>Year Month</strong> slicer causes fewer mistakes and answers the reporting question faster.</p>
</blockquote>



<h2 class="wp-block-heading">Format the Slicer for Real Users</h2>



<p>A slicer should look like part of the dashboard, not a technical control added at the end.</p>



<p>Use these formatting choices:</p>



<ul class="wp-block-list">
<li>Set the slicer title to <strong>Select Reporting Month</strong>.</li>



<li>Use the same font family as your KPI cards and charts.</li>



<li>Keep the slicer background subtle and easy to read.</li>



<li>Avoid very small text, especially for reports viewed in the <strong>Power BI Service</strong>.</li>



<li>Place the slicer in the same location across report pages.</li>



<li>Use clear labels such as “Reporting Month” instead of “Year Month.”</li>
</ul>



<p>You can control whether the slicer affects every visual. Select the slicer, open the <strong>Format</strong> ribbon, and choose <strong>Edit interactions</strong>. Each visual displays icons that let you choose filtering, highlighting, or no interaction.</p>



<p>For example, you might allow the month slicer to filter all sales visuals but not a static instruction card.</p>



<h2 class="wp-block-heading">Publish and Share the Monthly Report</h2>



<p>After testing the report in <strong>Power BI Desktop</strong>, publish it to the <strong>Power BI Service</strong>.</p>



<ol class="wp-block-list">
<li>Save the PBIX file with a meaningful name, such as Retail Sales Dashboard.</li>



<li>Select <strong>Publish</strong> from the Home ribbon.</li>



<li>Sign in with your organizational account.</li>



<li>Choose a <strong>workspace</strong>.</li>
</ol>



<p>A workspace is a shared area in Power BI Service where teams manage reports, dashboards, datasets, permissions, and apps. Create separate workspaces for development, testing, and production if several people build reports.</p>



<p>After publishing, open the report in the Power BI Service and test the slicer again. Check the report in a browser and on a smaller screen. A slicer that looks perfect in Desktop may need resizing for users on laptops.</p>



<p>If users need data outside the report, review how to <a href="https://www.spguides.com/power-bi-export-to-excel/">export Power BI data to Excel</a> without breaking reporting governance.</p>



<p>You can also embed a published report in a customer-facing portal. For example, this guide on <a href="https://www.spguides.com/display-power-bi-reports-in-power-pages/">displaying Power BI reports in Power Pages</a> is useful when a secure external portal needs interactive reporting.</p>



<h2 class="wp-block-heading">Useful Tips</h2>



<ul class="wp-block-list">
<li><strong>Use a dedicated Date table:</strong> Do not rely on scattered date columns in multiple fact tables. A single calendar keeps filtering and time intelligence consistent.</li>



<li><strong>Sort month labels by numbers:</strong> Month names sort alphabetically unless you use <strong>Sort by column</strong> with Month Number.</li>



<li><strong>Avoid Month Name by itself:</strong> “January” without a year combines data from every January. Use <strong>Year Month</strong> for regular monthly reporting.</li>



<li><strong>Keep relationships simple:</strong> Use a one-to-many relationship from Date to Sales. Avoid many-to-many date relationships unless you fully understand the business scenario.</li>



<li><strong>Use measures for KPIs:</strong> Build Total Sales, Total Orders, and growth metrics as <strong>Measures</strong>, not calculated columns. Measures respond to slicers and use less model storage.</li>



<li><strong>Secure sensitive data:</strong> Use <strong>Row-Level Security</strong> when regional managers should only see their own territory’s sales. A slicer controls analysis; it does not provide security.</li>
</ul>



<h2 class="wp-block-heading">Frequently Asked Questions</h2>


<div id="rank-math-faq" class="rank-math-block">
<div class="rank-math-list ">
<div id="faq-question-1788241439121" class="rank-math-list-item">
<h3 class="rank-math-question ">How do I create a month slicer in Power BI?</h3>
<div class="rank-math-answer ">

<p>Add a <strong>Slicer</strong> visual and drag a month-related field into it. For multi-year data, use a <strong>Year Month</strong> column from a Date table instead of Month Name alone. Sort that field with a numeric Year Month Sort column.</p>

</div>
</div>
<div id="faq-question-1788241447625" class="rank-math-list-item">
<h3 class="rank-math-question ">Why are months in alphabetical order in my Power BI slicer?</h3>
<div class="rank-math-answer ">

<p>Power BI treats month names as text, so it sorts them alphabetically by default. Select the Month Name column, choose <strong>Sort by column</strong>, and select a Month Number column containing values from 1 to 12.</p>

</div>
</div>
<div id="faq-question-1788241457074" class="rank-math-list-item">
<h3 class="rank-math-question ">Can I filter Power BI by month and year?</h3>
<div class="rank-math-answer ">

<p>Yes. Use one <strong>Year Month</strong> slicer, or add separate slicers for Year and Month Name. A single Year Month slicer usually gives clearer results because it prevents users from mixing the same month across different years.</p>

</div>
</div>
<div id="faq-question-1788241465789" class="rank-math-list-item">
<h3 class="rank-math-question ">How do I show only the latest month in a Power BI slicer?</h3>
<div class="rank-math-answer ">

<p>Use a relative date filter for rolling periods, or create a DAX flag that identifies the latest available month. Then apply that flag as a visual-level filter on the slicer. This approach works well for reports that refresh frequently.</p>

</div>
</div>
<div id="faq-question-1788241478279" class="rank-math-list-item">
<h3 class="rank-math-question ">Why does my month slicer not filter other visuals?</h3>
<div class="rank-math-answer ">

<p>First, confirm that the Date table has an active relationship to your Sales table. Next, use <strong>Edit interactions</strong> to check whether the slicer filters each visual. Also confirm that visuals use measures and fields connected to the same data model.</p>

</div>
</div>
<div id="faq-question-1788241489392" class="rank-math-list-item">
<h3 class="rank-math-question ">Do I need a Date table for a Power BI date slicer by month?</h3>
<div class="rank-math-answer ">

<p>A simple report may work without one, but a Date table is the better choice for production reports. It supports correct sorting, consistent date filtering, and time-intelligence measures such as prior-year sales.</p>

</div>
</div>
</div>
</div>


<p>A <strong>Power BI date slicer by month</strong> becomes reliable when it uses a proper Date table, a clean relationship, and correctly sorted Year Month labels. Build the slicer around <strong>Year Month</strong> for daily business reporting, then use measures to let every KPI and visual respond to the selected period. I hope you found this article helpful.</p>



<p>You May Also Like</p>



<ul class="wp-block-list">
<li><a href="https://www.spguides.com/power-bi-dax-groupby-with-filter/">Power BI DAX GROUPBY with FILTER</a></li>



<li><a href="https://www.spguides.com/power-bi-dax-filter/">Power BI DAX FILTER</a></li>



<li><a href="https://www.spguides.com/power-bi-free-vs-pro-vs-premium/">Power BI Free vs Pro vs Premium</a></li>



<li><a href="https://www.spguides.com/add-a-dropdown-slicer-in-power-bi/">Add a Dropdown Slicer in Power BI</a></li>



<li><a href="https://www.spguides.com/power-bi-date-slicer-only-shows-dates-with-data/">Power BI Date Slicer Only Shows Dates With Data</a></li>



<li><a href="https://www.spguides.com/sort-slicer-by-measure-in-power-bi/">Sort Slicer By Measure in Power BI</a></li>
</ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>PnP PowerShell Scripts for SharePoint Developers</title>
		<link>https://www.spguides.com/pnp-powershell-scripts/</link>
		
		<dc:creator><![CDATA[Bijay Kumar]]></dc:creator>
		<pubDate>Mon, 31 Aug 2026 17:08:47 +0000</pubDate>
				<category><![CDATA[PowerShell]]></category>
		<category><![CDATA[PnP PowerShell Scripts for SharePoint Developers]]></category>
		<guid isPermaLink="false">https://www.spguides.com/?p=137213</guid>

					<description><![CDATA[Every time I need to set up a new SharePoint site, list, or library, I don&#8217;t want to click through the UI ten times just to add a few columns. I want a script I can run once, change a couple of values, and be done in under a minute. That&#8217;s exactly what this post ... <a title="PnP PowerShell Scripts for SharePoint Developers" class="read-more" href="https://www.spguides.com/pnp-powershell-scripts/" aria-label="Read more about PnP PowerShell Scripts for SharePoint Developers">read more...</a>]]></description>
										<content:encoded><![CDATA[
<p>Every time I need to set up a <a href="https://www.spguides.com/create-a-sharepoint-site/" target="_blank" rel="noreferrer noopener">new SharePoint site</a>, list, or library, I don&#8217;t want to click through the UI ten times just to add a few columns. I want a script I can run once, change a couple of values, and be done in under a minute.</p>



<p>That&#8217;s exactly what this post is about. I&#8217;m putting together a growing collection of <strong>PnP PowerShell scripts</strong> that any <a href="https://www.spguides.academy/" target="_blank" rel="noreferrer noopener">SharePoint developer</a> can pick up and use directly on their own SharePoint site. You just swap out the site URL, client ID, tenant name, or a couple of parameters, and the script does the rest. No rewriting from scratch, no guessing at cmdlet syntax.</p>



<p>A lot of people I talk to don&#8217;t have this kind of script library handy. They end up rewriting the same <code>Connect-PnPOnline</code>, <code>New-PnPList</code>, or <code>Add-PnPField</code> commands from scratch every time, or worse, doing it manually through the browser. So instead of writing one script and moving on, I&#8217;m building this into a living reference: creating sites, creating lists and libraries, adding columns of different types, and even seeding sample data so you can test your setup without typing in dummy rows by hand.</p>



<p>Every script here is ready to copy, paste, and run directly on your <a href="https://www.spguides.com/create-sharepoint-site-using-power-apps-power-automate/" target="_blank" rel="noreferrer noopener">SharePoint site</a>. Just plug in your own site URL and client ID, and you&#8217;re good to go. I&#8217;ll keep adding new scripts to this post as I use them in real projects, so bookmark it.</p>



<h2 class="wp-block-heading">PnP PowerShell Script: Create SharePoint List with Multi-Select Choice Column</h2>



<p>As a developer, if you want to <a href="https://www.spguides.com/create-sharepoint-online-list-and-columns-from-excel-using-power-automate/" target="_blank" rel="noreferrer noopener">create a SharePoint list</a> with a multi-select choice column, then use the script below:</p>



<pre class="wp-block-code"><code>&lt;#
=====================================================================
 Script   : Create-CandidateList.ps1
 Purpose  : Creates a SharePoint Online list named "Candidates" with a
            multi-select Choice column (JobLocation) and populates it
            with sample candidate records.
 Module   : PnP.PowerShell
=====================================================================
#&gt;

# ----------------------------&#91; CONNECTION ]---------------------------
$ClientID = "655a839c-8659-bbbbbbb-cccc-bbbbbbbbb"
$SiteUrl  = "https://tsinfotechnologies.sharepoint.com/sites/SPGuides"

Connect-PnPOnline -Url $SiteUrl -ClientId $ClientID -Interactive

# ----------------------------&#91; VARIABLES ]----------------------------
$ListName = "Candidates"

$Choices = @(
    "Texas",
    "Washington",
    "England",
    "Victoria",
    "Scotland",
    "Ireland",
    "California",
    "Florida",
    "Brasilia",
    "Ontario"
)

# --------------------------&#91; CREATE THE LIST ]------------------------
$List = Get-PnPList -Identity $ListName -ErrorAction SilentlyContinue

if ($null -eq $List) {
    Write-Host "Creating list '$ListName'..." -ForegroundColor Cyan
    $List = New-PnPList -Title $ListName -Template GenericList -OnQuickLaunch
    Write-Host "List '$ListName' created successfully." -ForegroundColor Green
}
else {
    Write-Host "List '$ListName' already exists. Skipping creation." -ForegroundColor Yellow
}

# ------------------&#91; RENAME TITLE COLUMN (OPTIONAL) ]-----------------
# The default 'Title' column will hold the candidate name.
Set-PnPField -List $ListName -Identity "Title" -Values @{ Title = "Candidate Name" }

# --------------&#91; CREATE MULTI-CHOICE COLUMN: JobLocation ]------------
$Field = Get-PnPField -List $ListName -Identity "JobLocation" -ErrorAction SilentlyContinue

if ($null -eq $Field) {
    Write-Host "Creating 'JobLocation' multi-choice column..." -ForegroundColor Cyan

    Add-PnPField -List $ListName `
                 -DisplayName "JobLocation" `
                 -InternalName "JobLocation" `
                 -Type MultiChoice `
                 -Choices $Choices `
                 -AddToDefaultView

    Write-Host "'JobLocation' column created with multiple selections enabled." -ForegroundColor Green
}
else {
    Write-Host "'JobLocation' column already exists. Skipping creation." -ForegroundColor Yellow
}

# ----------------------&#91; SAMPLE CANDIDATE RECORDS ]-------------------
$Candidates = @(
    @{ Name = "Michael Johnson";   Locations = @("Texas", "California") },
    @{ Name = "Jennifer Martinez"; Locations = @("Florida", "Ontario", "Texas") },
    @{ Name = "Christopher Davis"; Locations = @("Washington") },
    @{ Name = "Ashley Wilson";     Locations = @("England", "Scotland", "Ireland") },
    @{ Name = "Matthew Anderson";  Locations = @("Victoria", "Ontario") },
    @{ Name = "Emily Thompson";    Locations = @("California", "Washington", "Florida") },
    @{ Name = "Daniel Rodriguez";  Locations = @("Brasilia", "Texas") },
    @{ Name = "Sarah Brown";       Locations = @("Ireland", "England") },
    @{ Name = "Joshua Miller";     Locations = @("Ontario", "Victoria", "Scotland") },
    @{ Name = "Amanda Garcia";     Locations = @("Florida") },
    @{ Name = "Andrew Taylor";     Locations = @("Texas", "Washington", "California") },
    @{ Name = "Jessica White";     Locations = @("Scotland", "Brasilia") },
    @{ Name = "Ryan Harris";       Locations = @("California") },
    @{ Name = "Megan Clark";       Locations = @("England", "Victoria") },
    @{ Name = "Brandon Lewis";     Locations = @("Ontario", "Florida", "Ireland") }
)

Write-Host "`nAdding candidate records..." -ForegroundColor Cyan

foreach ($Candidate in $Candidates) {
    try {
        Add-PnPListItem -List $ListName -Values @{
            "Title"       = $Candidate.Name
            "JobLocation" = $Candidate.Locations
        } | Out-Null

        Write-Host ("  &#91;OK] {0}  -&gt;  {1}" -f $Candidate.Name, ($Candidate.Locations -join ", ")) -ForegroundColor Green
    }
    catch {
        Write-Host ("  &#91;FAIL] {0} : {1}" -f $Candidate.Name, $_.Exception.Message) -ForegroundColor Red
    }
}

Write-Host "`nAll done! '$ListName' list is ready with $($Candidates.Count) records." -ForegroundColor Cyan

# -------------------------&#91; DISCONNECT ]------------------------------
Disconnect-PnPOnline</code></pre>



<p>It will create a SharePoint list like in the screenshot below:</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f9f7f7" data-has-transparency="false" style="--dominant-color: #f9f7f7;" loading="lazy" decoding="async" width="906" height="728" sizes="(max-width: 906px) 100vw, 906px" src="https://www.spguides.com/wp-content/uploads/2026/08/PnP-PowerShell-script-examples.avif" alt="PnP PowerShell script examples" class="wp-image-137218 not-transparent" title="PnP PowerShell script" srcset="https://www.spguides.com/wp-content/uploads/2026/08/PnP-PowerShell-script-examples.avif 906w, https://www.spguides.com/wp-content/uploads/2026/08/PnP-PowerShell-script-examples-300x241.avif 300w, https://www.spguides.com/wp-content/uploads/2026/08/PnP-PowerShell-script-examples-768x617.avif 768w" /></figure></div>


<h2 class="wp-block-heading">PnP PowerShell Script: Create a SharePoint List (With Person or Group (single &amp; multiple) column) and Add 10 Records</h2>



<p>Below is a single, self-contained script. It connects, creates the&nbsp;<strong>Project Tracker</strong>&nbsp;list, adds the Person and Person-multi columns, resolves the five users, and then inserts 10 items with randomly assigned managers and members.</p>



<p>One important technical note:&nbsp;<code>Add-PnPField -Type User</code>&nbsp;creates a&nbsp;<strong>single</strong>&nbsp;person column, but there is no&nbsp;<code>UserMulti</code>&nbsp;value in the&nbsp;<code>FieldType</code>&nbsp;enum. To get a&nbsp;<em>multiple</em>&nbsp;person column you must create it from field XML with&nbsp;<code>Mult="TRUE"</code>. The script handles this correctly.</p>



<pre class="wp-block-code"><code>&lt;#
    Creates the "Projects Tracker" list in SharePoint Online and
    populates it with 10 sample records using PnP PowerShell.

    Columns:
        Title           (Text)
        ProjectManager  (Person or Group - single)
        ProjectMembers  (Person or Group - multiple)
#&gt;

#region Configuration ---------------------------------------------------------

$ClientID = "655a839c-8659-3322-3333-44444444444444"
$SiteUrl  = "https://tsinfotechnologies.sharepoint.com/sites/SPGuides"
$ListName = "Projects Tracker"

$UserEmails = @(
    "user1@tsinfotechnologies.onmicrosoft.com",
    "user2@tsinfotechnologies.onmicrosoft.com",
    "bijay@tsinfotechnologies.onmicrosoft.com",
    "preeti@tsinfotechnologies.onmicrosoft.com",
    "asit@tsinfotechnologies.onmicrosoft.com"
)

$ProjectTitles = @(
    "Website Redesign",
    "SharePoint Migration",
    "Power Automate Rollout",
    "Intranet Governance Review",
    "Teams Adoption Program",
    "Document Retention Policy",
    "Power BI Reporting Hub",
    "Security Baseline Audit",
    "Customer Portal Upgrade",
    "Knowledge Base Cleanup"
)

$ErrorActionPreference = "Stop"

#endregion

#region Connect ---------------------------------------------------------------

Write-Host "`nConnecting to $SiteUrl ..." -ForegroundColor Cyan

Connect-PnPOnline -Url $SiteUrl -ClientId $ClientID -Interactive

Write-Host "Connected." -ForegroundColor Green

#endregion

#region Create the list -------------------------------------------------------

Write-Host "`nChecking for the '$ListName' list ..." -ForegroundColor Cyan

$List = Get-PnPList -Identity $ListName -ErrorAction SilentlyContinue

if ($null -eq $List) {

    $List = New-PnPList `
        -Title $ListName `
        -Template GenericList `
        -OnQuickLaunch

    Write-Host "List '$ListName' created." -ForegroundColor Green
}
else {
    Write-Host "List '$ListName' already exists. Reusing it." -ForegroundColor Yellow
}

#endregion

#region Add the Person columns ------------------------------------------------

# --- ProjectManager : single person ---------------------------------------
$ManagerField = Get-PnPField `
    -List $ListName `
    -Identity "ProjectManager" `
    -ErrorAction SilentlyContinue

if ($null -eq $ManagerField) {

    Add-PnPField `
        -List $ListName `
        -DisplayName "Project Manager" `
        -InternalName "ProjectManager" `
        -Type User `
        -AddToDefaultView | Out-Null

    Write-Host "Column 'Project Manager' created." -ForegroundColor Green
}
else {
    Write-Host "Column 'Project Manager' already exists." -ForegroundColor Yellow
}

# --- ProjectMembers : multiple people -------------------------------------
# There is no 'UserMulti' value in the FieldType enum, so this column
# must be created from field XML using Mult="TRUE".

$MembersField = Get-PnPField `
    -List $ListName `
    -Identity "ProjectMembers" `
    -ErrorAction SilentlyContinue

if ($null -eq $MembersField) {

    $MembersFieldXml = @"
&lt;Field Type="UserMulti"
       DisplayName="Project Members"
       Name="ProjectMembers"
       StaticName="ProjectMembers"
       List="UserInfo"
       ShowField="ImnName"
       UserSelectionMode="PeopleOnly"
       UserSelectionScope="0"
       Mult="TRUE"
       Required="FALSE" /&gt;
"@

    Add-PnPFieldFromXml `
        -List $ListName `
        -FieldXml $MembersFieldXml | Out-Null

    Write-Host "Column 'Project Members' created." -ForegroundColor Green

    # Add it to the default view
    $DefaultView = Get-PnPView -List $ListName | Where-Object { $_.DefaultView }

    if ($null -ne $DefaultView) {
        $ViewFields = @($DefaultView.ViewFields)

        if ($ViewFields -notcontains "ProjectMembers") {
            $DefaultView.ViewFields.Add("ProjectMembers")
            $DefaultView.Update()
            Invoke-PnPQuery
        }
    }
}
else {
    Write-Host "Column 'Project Members' already exists." -ForegroundColor Yellow
}

#endregion

#region Confirm the schema ----------------------------------------------------

Write-Host "`nList schema:" -ForegroundColor Cyan

Get-PnPField -List $ListName |
    Where-Object { $_.InternalName -in @("Title", "ProjectManager", "ProjectMembers") } |
    Select-Object Title, InternalName, TypeAsString |
    Format-Table -AutoSize

#endregion

#region Resolve the users -----------------------------------------------------

Write-Host "Resolving users ..." -ForegroundColor Cyan

$ResolvedUsers = @(
    foreach ($Email in $UserEmails) {
        try {
            New-PnPUser -LoginName $Email -ErrorAction Stop
        }
        catch {
            Write-Warning "Could not resolve '$Email'. $($_.Exception.Message)"
        }
    }
)

if ($ResolvedUsers.Count -eq 0) {
    throw "No users could be resolved. Cannot continue."
}

Write-Host "Resolved $($ResolvedUsers.Count) of $($UserEmails.Count) users." -ForegroundColor Green

$ResolvedUsers |
    Select-Object Id, Title, Email, LoginName |
    Format-Table -AutoSize

#endregion

#region Add 10 records --------------------------------------------------------

Write-Host "Adding 10 list items ..." -ForegroundColor Cyan

$Created = @()

for ($i = 0; $i -lt 10; $i++) {

    $Title = $ProjectTitles&#91;$i]

    # Random project manager
    $Manager = Get-Random -InputObject $ResolvedUsers

    # Random 2 to 3 members (must be an array, never a delimited string)
    $MemberCount = Get-Random -Minimum 2 -Maximum (&#91;Math]::Min(4, $ResolvedUsers.Count + 1))

    $Members = @(
        $ResolvedUsers |
            Get-Random -Count $MemberCount |
            ForEach-Object { $_.LoginName }
    )

    try {
        $NewItem = Add-PnPListItem `
            -List $ListName `
            -Values @{
                Title          = $Title
                ProjectManager = $Manager.LoginName
                ProjectMembers = $Members
            } `
            -ErrorAction Stop

        $Created += &#91;pscustomobject]@{
            Id      = $NewItem.Id
            Title   = $Title
            Manager = $Manager.Title
            Members = $MemberCount
        }

        Write-Host ("  &#91;{0,2}] {1,-32} PM: {2}" -f $NewItem.Id, $Title, $Manager.Title) `
            -ForegroundColor Green
    }
    catch {
        Write-Warning "Failed to create '$Title'. $($_.Exception.Message)"
    }
}

#endregion

#region Summary ---------------------------------------------------------------

Write-Host "`n$($Created.Count) of 10 items created successfully.`n" -ForegroundColor Cyan

$Created | Format-Table -AutoSize

Write-Host "Verifying from SharePoint ...`n" -ForegroundColor Cyan

Get-PnPListItem -List $ListName -Fields "Title", "ProjectManager", "ProjectMembers" |
    ForEach-Object {
        $Manager = $_&#91;"ProjectManager"]
        $Members = @($_&#91;"ProjectMembers"])

        &#91;pscustomobject]@{
            Id      = $_.Id
            Title   = $_&#91;"Title"]
            Manager = if ($Manager) { $Manager.LookupValue } else { "" }
            Members = ($Members | ForEach-Object { $_.LookupValue }) -join "; "
        }
    } |
    Format-Table -AutoSize -Wrap

Write-Host "Done. View the list at:" -ForegroundColor Green
Write-Host "$SiteUrl/Lists/$($ListName -replace ' ','')`n"

Disconnect-PnPOnline

#endregion</code></pre>



<p>Once you will execute the above script, it will create a SharePoint Online list like the below:</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-dominant-color="f9f7f7" data-has-transparency="false" style="--dominant-color: #f9f7f7;" loading="lazy" decoding="async" width="1024" height="701" sizes="(max-width: 1024px) 100vw, 1024px" src="https://www.spguides.com/wp-content/uploads/2026/08/Projects-tracker-list-pnp-powershell-script-1024x701.avif" alt="Projects tracker list pnp powershell script" class="wp-image-137228 not-transparent" title="Projects tracker list pnp powershell script" srcset="https://www.spguides.com/wp-content/uploads/2026/08/Projects-tracker-list-pnp-powershell-script-1024x701.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/08/Projects-tracker-list-pnp-powershell-script-300x205.avif 300w, https://www.spguides.com/wp-content/uploads/2026/08/Projects-tracker-list-pnp-powershell-script-768x526.avif 768w, https://www.spguides.com/wp-content/uploads/2026/08/Projects-tracker-list-pnp-powershell-script.avif 1046w" /></figure></div>


<h2 class="wp-block-heading">PnP PowerShell Script: Create a Leave Request List in SharePoint Online</h2>



<p>The script will create a new SharePoint list called &#8220;Leave Requests&#8221; and add the following columns:</p>



<ul class="wp-block-list">
<li><code>EmployeeName</code> – Person or Group field</li>



<li><code>LeaveType</code> – Choice field (common leave categories, edit as needed)</li>



<li><code>StartDate</code> / <code>EndDate</code> – Date and Time fields</li>



<li><code>Reason</code> – Multiple lines of text (Note type)</li>



<li><code>Status</code> – Choice field (Pending, Approved, Rejected)</li>
</ul>



<p>The <code>Title</code> column is included automatically since every SharePoint list has it by default.</p>



<p>The script will also check if the list already exists in the SharePoint site.</p>



<pre class="wp-block-code"><code>$ClientID = "655a839c-8659-4229-44455-44444444444444"
$SiteUrl  = "https://tsinfotechnologies.sharepoint.com/sites/SPGuides"
$ListName = "Leave Requests"

Connect-PnPOnline -Url $SiteUrl -ClientId $ClientID -Interactive

$list = Get-PnPList -Identity $ListName -ErrorAction SilentlyContinue

if ($list) {
    Write-Host "List '$ListName' already exists. Skipping creation." -ForegroundColor Yellow
}
else {
    New-PnPList -Title $ListName -Template GenericList -OnQuickLaunch

    Add-PnPField -List $ListName -DisplayName "EmployeeName" -InternalName "EmployeeName" -Type User
    Add-PnPField -List $ListName -DisplayName "LeaveType" -InternalName "LeaveType" -Type Choice -Choices "Casual Leave","Sick Leave","Earned Leave","Maternity Leave","Paternity Leave"
    Add-PnPField -List $ListName -DisplayName "StartDate" -InternalName "StartDate" -Type DateTime
    Add-PnPField -List $ListName -DisplayName "EndDate" -InternalName "EndDate" -Type DateTime
    Add-PnPField -List $ListName -DisplayName "Reason" -InternalName "Reason" -Type Note
    Add-PnPField -List $ListName -DisplayName "Status" -InternalName "Status" -Type Choice -Choices "Pending","Approved","Rejected"

    Write-Host "List '$ListName' created successfully with all columns." -ForegroundColor Green
}</code></pre>



<p>Once the SharePoint list is created, you can also add items to it using the following script.</p>



<p>This is how the script will work:</p>



<ul class="wp-block-list">
<li>Loops 5 times, each time picking a random email from <code>$UserEmails</code>, a random <code>LeaveType</code>, and a random <code>Status</code> using <code>Get-Random</code>.</li>



<li><code>EmployeeName</code> (a Person field) accepts the email string directly in <code>Add-PnPListItem</code> — PnP resolves it to the correct user internally, so no separate lookup call is strictly needed.</li>



<li><code>StartDate</code> is staggered by <code>$i * 2</code> days from today, and <code>EndDate</code> is 2 days after that, so each item has a distinct, valid date range.</li>



<li><code>Reason</code> is a simple auto-generated string tied to the leave type, and <code>Title</code> is set as <code>"Leave Request 1"</code> through <code>"Leave Request 5"</code>.</li>
</ul>



<pre class="wp-block-code"><code>$ClientID = "655a839c-8659-4229-9993-444444444444444444"
$SiteUrl  = "https://tsinfotechnologies.sharepoint.com/sites/SPGuides"
$ListName = "Leave Requests"

$UserEmails = @(
    "user1@tsinfotechnologies.onmicrosoft.com",
    "user2@tsinfotechnologies.onmicrosoft.com",
    "bijay@tsinfotechnologies.onmicrosoft.com",
    "preeti@tsinfotechnologies.onmicrosoft.com",
    "asit@tsinfotechnologies.onmicrosoft.com"
)

$LeaveTypes = @("Casual Leave","Sick Leave","Earned Leave","Maternity Leave","Paternity Leave")
$StatusList = @("Pending","Approved","Rejected")

Connect-PnPOnline -Url $SiteUrl -ClientId $ClientID -Interactive

for ($i = 1; $i -le 5; $i++) {
    $email = Get-Random -InputObject $UserEmails
    $leaveType = Get-Random -InputObject $LeaveTypes
    $status = Get-Random -InputObject $StatusList
    $startDate = (Get-Date).AddDays($i * 2)
    $endDate = $startDate.AddDays(2)

    $user = Resolve-PnPUpn -Email $email -ErrorAction SilentlyContinue
    $userField = $email

    Add-PnPListItem -List $ListName -Values @{
        "Title"        = "Leave Request $i"
        "EmployeeName" = $userField
        "LeaveType"    = $leaveType
        "StartDate"    = $startDate
        "EndDate"      = $endDate
        "Reason"       = "Sample reason for $leaveType"
        "Status"       = $status
    }

    Write-Host "Item $i added for $email" -ForegroundColor Green
}</code></pre>



<p>To add more items to the SharePoint list, change the value in the for loop.</p>



<h2 class="wp-block-heading" id="conclusion">Conclusion</h2>



<p>That&#8217;s the script collection so far. Each one is built the same way: connect once, plug in your site URL and client ID, and run it directly on your SharePoint site. No extra setup, no digging through documentation halfway through your workday.</p>



<p>I&#8217;ll keep expanding this post as I come across more scenarios worth automating, so if there&#8217;s a specific SharePoint setup task you keep repeating manually, drop it in the comments, and I&#8217;ll add a script for it. Bookmark this page since it&#8217;s going to keep growing.</p>



<p>You may also like the following tutorials:</p>



<ul class="wp-block-list">
<li><a href="https://www.spguides.com/download-all-files-from-sharepoint-using-powershell/">Download All Files from a SharePoint Document Library Using PnP PowerShell</a></li>



<li><a href="https://www.spguides.com/get-sharepoint-folder-permissions-using-powershell/">Get SharePoint Folder Permissions Using PnP PowerShell</a></li>



<li><a href="https://www.spguides.com/add-5000-items-sharepoint-list-pnp-powershell/">Add More Than 5000 Items to a SharePoint Online List Using PnP PowerShell</a></li>
</ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Download All Files from a SharePoint Document Library Using PnP PowerShell</title>
		<link>https://www.spguides.com/download-all-files-from-sharepoint-using-powershell/</link>
		
		<dc:creator><![CDATA[Bijay Kumar]]></dc:creator>
		<pubDate>Mon, 31 Aug 2026 01:35:06 +0000</pubDate>
				<category><![CDATA[PowerShell]]></category>
		<category><![CDATA[Download All Files From A SharePoint Online Document Library Using PnP PowerShell]]></category>
		<guid isPermaLink="false">https://www.spguides.com/?p=114501</guid>

					<description><![CDATA[If you&#8217;ve ever tried to download a few thousand files from a SharePoint Online document library using the browser, you already know how painful it is. You select the files, click Download, SharePoint starts zipping them, and then… it fails. Or it silently skips files. Or it hits the 250 MB / 10,000 file limit ... <a title="Download All Files from a SharePoint Document Library Using PnP PowerShell" class="read-more" href="https://www.spguides.com/download-all-files-from-sharepoint-using-powershell/" aria-label="Read more about Download All Files from a SharePoint Document Library Using PnP PowerShell">read more...</a>]]></description>
										<content:encoded><![CDATA[
<p>If you&#8217;ve ever tried to download a few thousand files from a <a href="https://www.spguides.com/create-document-library-sharepoint-pnp-powershell/" target="_blank" rel="noreferrer noopener">SharePoint Online document library</a> using the browser, you already know how painful it is.</p>



<p>You select the files, click Download, SharePoint starts zipping them, and then… it fails. Or it silently skips files. Or it hits the 250 MB / 10,000 file limit and gives up.</p>



<p>There&#8217;s a much better way – PnP PowerShell.</p>



<p>In this tutorial, I will show you how to <strong>download all documents from a SharePoint Online document library using PnP PowerShell</strong>, along with a lot of practical variations that people actually need in real projects – such as downloading only PDF files, downloading only files larger than 5 MB, downloading files from a specific folder, downloading files older than 30 days, and more.</p>



<p>Everything here is copy-paste ready. You only need to change a few variables at the top of each script.</p>



<p>Let&#8217;s get started.</p>



<h2 class="wp-block-heading">What You Need Before You Start (Prerequisites)</h2>



<p>Before running any script in this guide, make sure you have the following ready.</p>



<p><strong>1. PowerShell 7 (recommended)</strong></p>



<p>PnP PowerShell (version 2.x and above) requires PowerShell 7. Windows PowerShell 5.1 works only with the older, deprecated&nbsp;<code>SharePointPnPPowerShellOnline</code>&nbsp;module, which I do not recommend for new work.</p>



<p>To check your version, run:</p>



<pre class="wp-block-code"><code>$PSVersionTable.PSVersion</code></pre>



<p>If you see 5.1, install PowerShell 7 first.</p>



<p><strong>2. The PnP.PowerShell module</strong></p>



<p>Install it with this command:</p>



<pre class="wp-block-code"><code>Install-Module -Name PnP.PowerShell -Scope CurrentUser -Force</code></pre>



<p>To confirm it installed correctly:</p>



<pre class="wp-block-code"><code>Get-Module PnP.PowerShell -ListAvailable | Select-Object Name, Version</code></pre>



<p><strong>3. Permissions on the SharePoint site</strong></p>



<p>You need at least&nbsp;<strong>Read</strong>&nbsp;access to the document library you want to download from. If you&#8217;re a Site Owner or SharePoint Administrator, you&#8217;re good.</p>



<p><strong>4. An Entra ID (Azure AD) app registration</strong></p>



<p>This is the part that trips up most beginners in PnP PowerShell v2 and v3. The old multi-tenant PnP app no longer works, so you must register your own app once per tenant.</p>



<p>Run this one-time command (you need to be a Global Administrator or Application Administrator):</p>



<pre class="wp-block-code"><code>Register-PnPEntraIDAppForInteractiveLogin -ApplicationName "PnP Rocks" -Tenant contoso.onmicrosoft.com -Interactive</code></pre>



<p>Copy the&nbsp;<strong>Client ID</strong>&nbsp;that gets returned. You will use it in every connection command below.</p>



<p>Note: If your tenant already has a registered PnP app, ask your admin for the Client ID instead of creating a new one.</p>



<p><strong>5. Connect to Your SharePoint Site</strong></p>



<p>Every script in this tutorial starts with a connection. The most common method is interactive login (a browser window pops up, and you sign in normally).</p>



<pre class="wp-block-code"><code>$SiteURL  = "https://contoso.sharepoint.com/sites/Finance"
$ClientId = "11111111-2222-3333-4444-555555555555"

Connect-PnPOnline -Url $SiteURL -Interactive -ClientId $ClientId</code></pre>



<p>To verify the connection worked:</p>



<pre class="wp-block-code"><code>Get-PnPWeb | Select-Object Title, Url</code></pre>



<p>If you see your site title, you&#8217;re connected.</p>



<p><strong>Running it unattended?</strong>&nbsp;If you plan to schedule the script (Task Scheduler or Azure Automation), use certificate-based app-only authentication instead:</p>



<pre class="wp-block-code"><code>Connect-PnPOnline -Url $SiteURL `
    -ClientId "11111111-2222-3333-4444-555555555555" `
    -Tenant "contoso.onmicrosoft.com" `
    -CertificatePath "C:\Certs\PnPCert.pfx" `
    -CertificatePassword (ConvertTo-SecureString -String "YourPfxPassword" -AsPlainText -Force)</code></pre>



<p>App-only auth needs the&nbsp;<strong>Sites.Read.All</strong>&nbsp;(or&nbsp;<strong>Sites.FullControl.All</strong>) Graph/SharePoint API permission granted with admin consent.</p>



<p><strong>6. Find the Correct Library Name and URL</strong></p>



<p>Before downloading, confirm the exact name of your document library. Display names and internal URLs are often different (for example, a library shown as &#8220;Project Files&#8221; may have the URL&nbsp;<code>/ProjectFiles</code>).</p>



<p>Run this to list every document library on the site:</p>



<pre class="wp-block-code"><code>Get-PnPList | Where-Object { $_.BaseTemplate -eq 101 } |
    Select-Object Title, DefaultViewUrl, ItemCount</code></pre>



<p>Make a note of the&nbsp;<strong>Title</strong>&nbsp;and the folder part of the&nbsp;<strong>DefaultViewUrl</strong>. You&#8217;ll need these next.</p>



<p>Check out <a href="https://www.spguides.com/get-sharepoint-document-library-size-using-powershell/">Get SharePoint Document Library Size Using PnP PowerShell</a></p>



<h2 class="wp-block-heading">Download All Files from a SharePoint Document Library</h2>



<p>This is the core script. It downloads every file from the <a href="https://www.spguides.com/sharepoint-document-library-best-practices/" target="_blank" rel="noreferrer noopener">SharePoint document library</a>, including all files inside subfolders, and recreates the exact same folder structure on your local drive.</p>



<pre class="wp-block-code"><code># ---------- CONFIGURATION ----------
$SiteURL      = "https://contoso.sharepoint.com/sites/Finance"
$ClientId     = "11111111-2222-3333-4444-555555555555"
$LibraryName  = "Documents"
$DownloadPath = "C:\SPDownloads\Finance"
# -----------------------------------

Connect-PnPOnline -Url $SiteURL -Interactive -ClientId $ClientId

# Make sure the destination folder exists
if (-not (Test-Path $DownloadPath)) {
    New-Item -Path $DownloadPath -ItemType Directory -Force | Out-Null
}

# Get the library and its server-relative URL
$List       = Get-PnPList -Identity $LibraryName -Includes RootFolder
$LibraryUrl = $List.RootFolder.ServerRelativeUrl

# Get all FILES only (FSObjType 0), page by page for large libraries
$Items = Get-PnPListItem -List $LibraryName -PageSize 2000 |
         Where-Object { $_.FileSystemObjectType -eq "File" }

Write-Host "Found $($Items.Count) files. Starting download..." -ForegroundColor Cyan

$Counter = 0
foreach ($Item in $Items) {

    $Counter++
    $FileUrl = $Item.FieldValues.FileRef

    # Recreate the SharePoint folder structure locally
    $RelativePath = $FileUrl.Replace($LibraryUrl, "").Replace("/", "\")
    $LocalFile    = Join-Path $DownloadPath $RelativePath.TrimStart("\")
    $LocalFolder  = Split-Path $LocalFile -Parent

    if (-not (Test-Path $LocalFolder)) {
        New-Item -Path $LocalFolder -ItemType Directory -Force | Out-Null
    }

    Write-Progress -Activity "Downloading files" `
                   -Status "$Counter of $($Items.Count): $($Item.FieldValues.FileLeafRef)" `
                   -PercentComplete (($Counter / $Items.Count) * 100)

    try {
        Get-PnPFile -Url $FileUrl -Path $LocalFolder `
                    -FileName $Item.FieldValues.FileLeafRef -AsFile -Force
    }
    catch {
        Write-Host "Failed: $FileUrl - $($_.Exception.Message)" -ForegroundColor Red
    }
}

Write-Host "Download complete. Files saved to $DownloadPath" -ForegroundColor Green
Disconnect-PnPOnline</code></pre>



<p><strong>How this script works:</strong></p>



<ul class="wp-block-list">
<li><code>Get-PnPListItem</code>&nbsp;with&nbsp;<code>-PageSize 2000</code>&nbsp;pulls items in batches so you don&#8217;t hit the 5,000 item list view threshold.</li>



<li><code>FileSystemObjectType -eq "File"</code>&nbsp;filters out folder objects, so you only process actual documents.</li>



<li><code>FileRef</code>&nbsp;is the server-relative URL of the file (for example,&nbsp;<code>/sites/Finance/Shared Documents/2024/Report.pdf</code>).</li>



<li>Replacing the library URL with an empty string gives you the relative path, which is then used to recreate the folder tree locally.</li>



<li><code>-AsFile -Force</code>&nbsp;writes the file to disk and overwrites it if it already exists.</li>
</ul>



<p>That&#8217;s it. Run it once and your entire library lands on your local drive with folders intact.</p>



<p>You can see the screenshot below for your reference.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-dominant-color="ebebea" data-has-transparency="false" style="--dominant-color: #ebebea;" loading="lazy" decoding="async" width="1024" height="585" sizes="(max-width: 1024px) 100vw, 1024px" src="https://www.spguides.com/wp-content/uploads/2026/08/Download-All-Files-from-a-SharePoint-Document-Library-1024x585.avif" alt="Download All Files from a SharePoint Document Library" class="wp-image-137222 not-transparent" title="Download All Files from a SharePoint Document Library" srcset="https://www.spguides.com/wp-content/uploads/2026/08/Download-All-Files-from-a-SharePoint-Document-Library-1024x585.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/08/Download-All-Files-from-a-SharePoint-Document-Library-300x171.avif 300w, https://www.spguides.com/wp-content/uploads/2026/08/Download-All-Files-from-a-SharePoint-Document-Library-768x438.avif 768w, https://www.spguides.com/wp-content/uploads/2026/08/Download-All-Files-from-a-SharePoint-Document-Library.avif 1291w" /></figure></div>


<p>Check out <a href="https://www.spguides.com/spfx-upload-file-to-sharepoint-document-library-with-metadata/">Upload File to SharePoint Document Library With Metadata in SPFx</a></p>



<h2 class="wp-block-heading">Download All Files Without Keeping the Folder Structure (Flat Download)</h2>



<p>Sometimes you just want every file dumped into one folder. The problem is duplicate file names in different subfolders will overwrite each other.</p>



<p>The script below handles that by adding a numeric suffix when a duplicate name is found.</p>



<pre class="wp-block-code"><code>&lt;#
=====================================================================
 Script  : Download all files from a SharePoint library (FLAT)
 Purpose : Downloads every file from a document library, including
           all subfolders, into ONE local folder without recreating
           the SharePoint folder structure.
 Requires: PowerShell 7+ and the PnP.PowerShell module
=====================================================================
#&gt;

# ------------------------- CONFIGURATION -------------------------
$SiteURL      = "https://contoso.sharepoint.com/sites/Finance"
$ClientId     = "11111111-2222-3333-4444-555555555555"
$LibraryName  = "Documents"                       # Library display name
$DownloadPath = "C:\SPDownloads\Finance_Flat"     # Local flat folder
$MappingFile  = "C:\SPDownloads\FlatDownload_Map_$(Get-Date -f 'yyyyMMdd_HHmmss').csv"
$MaxRetries   = 3
$SkipExisting = $false    # $true = skip files already downloaded
# -----------------------------------------------------------------

# ---------- STEP 1: Connect to SharePoint ----------
try {
    Connect-PnPOnline -Url $SiteURL -Interactive -ClientId $ClientId -ErrorAction Stop
    Write-Host "Connected to $SiteURL" -ForegroundColor Green
}
catch {
    Write-Host "Connection failed: $($_.Exception.Message)" -ForegroundColor Red
    return
}

# ---------- STEP 2: Prepare the local download folder ----------
if (-not (Test-Path $DownloadPath)) {
    New-Item -Path $DownloadPath -ItemType Directory -Force | Out-Null
    Write-Host "Created folder: $DownloadPath" -ForegroundColor Gray
}

# ---------- STEP 3: Get every file in the library ----------
Write-Host "Reading library '$LibraryName'..." -ForegroundColor Cyan

$Items = Get-PnPListItem -List $LibraryName -PageSize 2000 `
         -Fields "FileRef", "FileLeafRef", "File_x0020_Size", "Modified" |
         Where-Object { $_.FileSystemObjectType -eq "File" }

$Total = @($Items).Count

if ($Total -eq 0) {
    Write-Host "No files found in '$LibraryName'. Nothing to download." -ForegroundColor Yellow
    Disconnect-PnPOnline
    return
}

Write-Host "Found $Total files. Starting flat download...`n" -ForegroundColor Cyan

# ---------- STEP 4: Download loop with duplicate-name handling ----------
$Downloaded = 0
$Skipped    = 0
$Failed     = 0
$Renamed    = 0
$Counter    = 0
$Mapping    = @()

foreach ($Item in $Items) {

    $Counter++
    $SourceUrl    = $Item.FieldValues.FileRef
    $OriginalName = $Item.FieldValues.FileLeafRef

    Write-Progress -Activity "Flat download from '$LibraryName'" `
                   -Status "$Counter of $Total : $OriginalName" `
                   -PercentComplete (($Counter / $Total) * 100)

    # --- Strip characters Windows does not allow in file names ---
    $SafeName = $OriginalName -replace '&#91;\\/:*?"&lt;&gt;|]', '_'

    $TargetName = $SafeName
    $TargetPath = Join-Path $DownloadPath $TargetName

    # --- Optionally skip if the exact file already exists ---
    if ($SkipExisting -and (Test-Path $TargetPath)) {
        Write-Host "&#91;$Counter/$Total] Skipped (exists): $TargetName" -ForegroundColor DarkGray
        $Skipped++
        continue
    }

    # --- Resolve duplicate names: File.pdf -&gt; File_1.pdf -&gt; File_2.pdf ---
    $Suffix = 1
    while (Test-Path $TargetPath) {
        $BaseName   = &#91;System.IO.Path]::GetFileNameWithoutExtension($SafeName)
        $Extension  = &#91;System.IO.Path]::GetExtension($SafeName)
        $TargetName = "$($BaseName)_$Suffix$Extension"
        $TargetPath = Join-Path $DownloadPath $TargetName
        $Suffix++
    }

    if ($TargetName -ne $SafeName) { $Renamed++ }

    # --- Download with retry + exponential back-off for throttling ---
    $Attempt = 0
    $Success = $false

    while (-not $Success -and $Attempt -lt $MaxRetries) {

        $Attempt++

        try {
            Get-PnPFile -Url $SourceUrl `
                        -Path $DownloadPath `
                        -FileName $TargetName `
                        -AsFile -Force -ErrorAction Stop

            $Success = $true
            $Downloaded++

            Write-Host "&#91;$Counter/$Total] Downloaded: $TargetName" -ForegroundColor Green

            $Mapping += &#91;PSCustomObject]@{
                OriginalName   = $OriginalName
                SavedAs        = $TargetName
                SharePointPath = $SourceUrl
                SizeKB         = &#91;math]::Round(&#91;int64]$Item.FieldValues.File_x0020_Size / 1KB, 2)
                Modified       = $Item.FieldValues.Modified
                Status         = "Success"
            }
        }
        catch {
            if ($Attempt -ge $MaxRetries) {

                $Failed++
                Write-Host "&#91;$Counter/$Total] FAILED: $OriginalName - $($_.Exception.Message)" -ForegroundColor Red

                $Mapping += &#91;PSCustomObject]@{
                    OriginalName   = $OriginalName
                    SavedAs        = ""
                    SharePointPath = $SourceUrl
                    SizeKB         = 0
                    Modified       = $Item.FieldValues.Modified
                    Status         = "Failed: $($_.Exception.Message)"
                }
            }
            else {
                # Wait 10s, then 20s, then 40s before retrying
                Start-Sleep -Seconds (&#91;math]::Pow(2, $Attempt) * 5)
            }
        }
    }
}

Write-Progress -Activity "Flat download" -Completed

# ---------- STEP 5: Export the mapping file ----------
if ($Mapping.Count -gt 0) {
    $Mapping | Export-Csv -Path $MappingFile -NoTypeInformation -Encoding UTF8
}

# ---------- STEP 6: Summary ----------
$LocalCount = (Get-ChildItem -Path $DownloadPath -File).Count

Write-Host "`n================ SUMMARY ================" -ForegroundColor Cyan
Write-Host " Library          : $LibraryName"
Write-Host " Files in library : $Total"
Write-Host " Downloaded       : $Downloaded" -ForegroundColor Green
Write-Host " Renamed (dupes)  : $Renamed"    -ForegroundColor Yellow
Write-Host " Skipped          : $Skipped"    -ForegroundColor DarkGray
Write-Host " Failed           : $Failed"     -ForegroundColor Red
Write-Host " Files on disk    : $LocalCount"
Write-Host " Mapping CSV      : $MappingFile"
Write-Host "=========================================`n" -ForegroundColor Cyan

Disconnect-PnPOnline</code></pre>



<p>Check out <a href="https://www.spguides.com/delete-files-from-sharepoint-document-library-using-rest-api-in-power-automate/">Delete Files From SharePoint Document Library Using Rest API in Power Automate</a></p>



<h2 class="wp-block-heading">Download Only PDF Files from a SharePoint Library</h2>



<p>This is one of the most common requests. You only want the PDFs – not the Word docs, not the images.</p>



<p>The cleanest approach is to filter on the&nbsp;<code>File_x0020_Type</code>&nbsp;field, which stores the extension without the dot.</p>



<pre class="wp-block-code"><code>$PDFs = Get-PnPListItem -List $LibraryName -PageSize 2000 |
        Where-Object { $_.FieldValues.File_x0020_Type -eq "pdf" }

Write-Host "Found $($PDFs.Count) PDF files."

foreach ($Item in $PDFs) {
    Get-PnPFile -Url $Item.FieldValues.FileRef -Path $DownloadPath `
                -FileName $Item.FieldValues.FileLeafRef -AsFile -Force
}</code></pre>



<p><strong>Faster method using a CAML query</strong></p>



<p>If your library has tens of thousands of items, filtering on the server side is dramatically faster because SharePoint returns only the matching rows:</p>



<pre class="wp-block-code"><code>$CAML = @"
&lt;View Scope='RecursiveAll'&gt;
  &lt;Query&gt;
    &lt;Where&gt;
      &lt;Eq&gt;
        &lt;FieldRef Name='File_x0020_Type'/&gt;
        &lt;Value Type='Text'&gt;pdf&lt;/Value&gt;
      &lt;/Eq&gt;
    &lt;/Where&gt;
  &lt;/Query&gt;
&lt;/View&gt;
"@

$PDFs = Get-PnPListItem -List $LibraryName -Query $CAML -PageSize 2000

foreach ($Item in $PDFs) {
    Get-PnPFile -Url $Item.FieldValues.FileRef -Path $DownloadPath `
                -FileName $Item.FieldValues.FileLeafRef -AsFile -Force
}</code></pre>



<p>Note:&nbsp;<code>Scope='RecursiveAll'</code>&nbsp;is important – without it, CAML only looks at the root folder and ignores subfolders.</p>



<p>Here is the complete PnP PowerShell script:</p>



<pre class="wp-block-code"><code># ------------------------- CONFIGURATION -------------------------
$SiteURL      = "https://contoso.sharepoint.com/sites/Finance"
$ClientId     = "11111111-2222-3333-4444-555555555555"
$LibraryName  = "Documents"
$DownloadPath = "C:\SPDownloads\PDFs"
$KeepStructure = $true      # $true = recreate folders, $false = flat
$MaxRetries    = 3
$LogFile       = "C:\SPDownloads\PDFDownload_$(Get-Date -f 'yyyyMMdd_HHmmss').csv"
# -----------------------------------------------------------------

try {
    Connect-PnPOnline -Url $SiteURL -Interactive -ClientId $ClientId -ErrorAction Stop
    Write-Host "Connected to $SiteURL" -ForegroundColor Green
}
catch {
    Write-Host "Connection failed: $($_.Exception.Message)" -ForegroundColor Red
    return
}

if (-not (Test-Path $DownloadPath)) {
    New-Item -Path $DownloadPath -ItemType Directory -Force | Out-Null
}

$List       = Get-PnPList -Identity $LibraryName -Includes RootFolder
$LibraryUrl = $List.RootFolder.ServerRelativeUrl

$CAML = @"
&lt;View Scope='RecursiveAll'&gt;
  &lt;Query&gt;
    &lt;Where&gt;
      &lt;Eq&gt;
        &lt;FieldRef Name='File_x0020_Type'/&gt;
        &lt;Value Type='Text'&gt;pdf&lt;/Value&gt;
      &lt;/Eq&gt;
    &lt;/Where&gt;
  &lt;/Query&gt;
  &lt;ViewFields&gt;
    &lt;FieldRef Name='FileRef'/&gt;
    &lt;FieldRef Name='FileLeafRef'/&gt;
    &lt;FieldRef Name='File_x0020_Size'/&gt;
    &lt;FieldRef Name='Modified'/&gt;
  &lt;/ViewFields&gt;
&lt;/View&gt;
"@

Write-Host "Searching for PDF files in '$LibraryName'..." -ForegroundColor Cyan

$PDFs  = Get-PnPListItem -List $LibraryName -Query $CAML -PageSize 2000
$Total = @($PDFs).Count

if ($Total -eq 0) {
    Write-Host "No PDF files found." -ForegroundColor Yellow
    Disconnect-PnPOnline
    return
}

$TotalMB = &#91;math]::Round((($PDFs | Measure-Object -Property { &#91;int64]$_.FieldValues.File_x0020_Size } -Sum).Sum / 1MB), 2)
Write-Host "Found $Total PDF files ($TotalMB MB). Starting download...`n" -ForegroundColor Cyan

$Downloaded = 0
$Failed     = 0
$Counter    = 0
$Log        = @()

foreach ($Item in $PDFs) {

    $Counter   = $Counter + 1
    $SourceUrl = $Item.FieldValues.FileRef
    $FileName  = $Item.FieldValues.FileLeafRef -replace '&#91;\\/:*?"&lt;&gt;|]', '_'

    if ($KeepStructure) {
        $RelPath     = $SourceUrl.Replace($LibraryUrl, "").Replace("/", "\").TrimStart("\")
        $TargetFolder = Split-Path (Join-Path $DownloadPath $RelPath) -Parent
    }
    else {
        $TargetFolder = $DownloadPath
        $Suffix = 1
        while (Test-Path (Join-Path $TargetFolder $FileName)) {
            $Base     = &#91;System.IO.Path]::GetFileNameWithoutExtension($Item.FieldValues.FileLeafRef)
            $FileName = "$($Base)_$Suffix.pdf"
            $Suffix   = $Suffix + 1
        }
    }

    if (-not (Test-Path $TargetFolder)) {
        New-Item -Path $TargetFolder -ItemType Directory -Force | Out-Null
    }

    Write-Progress -Activity "Downloading PDFs from '$LibraryName'" `
                   -Status "$Counter of $Total : $FileName" `
                   -PercentComplete (($Counter / $Total) * 100)

    $Attempt = 0
    $Success = $false

    while (-not $Success -and $Attempt -lt $MaxRetries) {

        $Attempt = $Attempt + 1

        try {
            Get-PnPFile -Url $SourceUrl -Path $TargetFolder -FileName $FileName -AsFile -Force -ErrorAction Stop
            $Success    = $true
            $Downloaded = $Downloaded + 1

            $SizeKB = &#91;math]::Round(&#91;int64]$Item.FieldValues.File_x0020_Size / 1KB, 2)
            Write-Host "&#91;$Counter/$Total] $FileName ($SizeKB KB)" -ForegroundColor Green

            $Log += &#91;PSCustomObject]@{
                FileName       = $FileName
                SharePointPath = $SourceUrl
                SizeKB         = $SizeKB
                Modified       = $Item.FieldValues.Modified
                Status         = "Success"
            }
        }
        catch {
            if ($Attempt -ge $MaxRetries) {
                $Failed = $Failed + 1
                Write-Host "&#91;$Counter/$Total] FAILED: $FileName" -ForegroundColor Red

                $Log += &#91;PSCustomObject]@{
                    FileName       = $FileName
                    SharePointPath = $SourceUrl
                    SizeKB         = 0
                    Modified       = $Item.FieldValues.Modified
                    Status         = "Failed: $($_.Exception.Message)"
                }
            }
            else {
                Start-Sleep -Seconds (&#91;math]::Pow(2, $Attempt) * 5)
            }
        }
    }
}

Write-Progress -Activity "Downloading PDFs" -Completed

if ($Log.Count -gt 0) {
    $Log | Export-Csv -Path $LogFile -NoTypeInformation -Encoding UTF8
}

Write-Host "`n============== SUMMARY ==============" -ForegroundColor Cyan
Write-Host " PDFs found  : $Total"
Write-Host " Downloaded  : $Downloaded" -ForegroundColor Green
Write-Host " Failed      : $Failed"     -ForegroundColor Red
Write-Host " Total size  : $TotalMB MB"
Write-Host " Saved to    : $DownloadPath"
Write-Host " Log file    : $LogFile"
Write-Host "=====================================`n" -ForegroundColor Cyan

Disconnect-PnPOnline</code></pre>



<p>Read <a href="https://www.spguides.com/create-folders-and-subfolders-in-sharepoint-document-library-using-spfx/">Create Folders and Subfolders in SharePoint document library</a></p>



<h2 class="wp-block-heading">Download Only Excel Files (or Any Set of File Types)</h2>



<p>Excel files can be&nbsp;<code>.xlsx</code>,&nbsp;<code>.xls</code>,&nbsp;<code>.xlsm</code>, or&nbsp;<code>.csv</code>, so you need to check for multiple extensions.</p>



<pre class="wp-block-code"><code>$Extensions = @("xlsx", "xls", "xlsm", "csv")

$ExcelFiles = Get-PnPListItem -List $LibraryName -PageSize 2000 |
              Where-Object { $Extensions -contains $_.FieldValues.File_x0020_Type }

foreach ($Item in $ExcelFiles) {
    Get-PnPFile -Url $Item.FieldValues.FileRef -Path $DownloadPath `
                -FileName $Item.FieldValues.FileLeafRef -AsFile -Force
}</code></pre>



<p>You can reuse this pattern for any file type. For Word documents use&nbsp;<code>@("docx","doc")</code>, for images use&nbsp;<code>@("jpg","jpeg","png","gif")</code>, and for PowerPoint use&nbsp;<code>@("pptx","ppt")</code>.</p>



<h2 class="wp-block-heading">Download Files Larger Than 5 MB</h2>



<p>Useful when you&#8217;re cleaning up storage or auditing which large files are eating your site quota.</p>



<p>The file size is stored in the&nbsp;<code>File_x0020_Size</code>&nbsp;field, in bytes.</p>



<pre class="wp-block-code"><code>$MinSizeMB = 5
$MinBytes  = $MinSizeMB * 1MB

$BigFiles = Get-PnPListItem -List $LibraryName -PageSize 2000 |
            Where-Object {
                $_.FileSystemObjectType -eq "File" -and
                &#91;int64]$_.FieldValues.File_x0020_Size -gt $MinBytes
            }

Write-Host "Files larger than $MinSizeMB MB: $($BigFiles.Count)"

foreach ($Item in $BigFiles) {
    $SizeMB = &#91;math]::Round(&#91;int64]$Item.FieldValues.File_x0020_Size / 1MB, 2)
    Write-Host "Downloading $($Item.FieldValues.FileLeafRef) ($SizeMB MB)"

    Get-PnPFile -Url $Item.FieldValues.FileRef -Path $DownloadPath `
                -FileName $Item.FieldValues.FileLeafRef -AsFile -Force
}</code></pre>



<p>Tip: If you only want a report of large files (without downloading them), replace the download line with an export:</p>



<pre class="wp-block-code"><code>$BigFiles | ForEach-Object {
    &#91;PSCustomObject]@{
        Name   = $_.FieldValues.FileLeafRef
        SizeMB = &#91;math]::Round(&#91;int64]$_.FieldValues.File_x0020_Size / 1MB, 2)
        Path   = $_.FieldValues.FileRef
    }
} | Export-Csv "C:\Reports\LargeFiles.csv" -NoTypeInformation</code></pre>



<p>Here is the complete PnP PowerShell script:</p>



<pre class="wp-block-code"><code># ------------------------- CONFIGURATION -------------------------
$SiteURL       = "https://contoso.sharepoint.com/sites/Finance"
$ClientId      = "11111111-2222-3333-4444-555555555555"
$LibraryName   = "Documents"
$DownloadPath  = "C:\SPDownloads\LargeFiles"
$MinSizeMB     = 5
$KeepStructure = $true
$MaxRetries    = 3
$LogFile       = "C:\SPDownloads\LargeFiles_$(Get-Date -f 'yyyyMMdd_HHmmss').csv"
# -----------------------------------------------------------------

$MinBytes = $MinSizeMB * 1MB

try {
    Connect-PnPOnline -Url $SiteURL -Interactive -ClientId $ClientId -ErrorAction Stop
    Write-Host "Connected to $SiteURL" -ForegroundColor Green
}
catch {
    Write-Host "Connection failed: $($_.Exception.Message)" -ForegroundColor Red
    return
}

if (-not (Test-Path $DownloadPath)) {
    New-Item -Path $DownloadPath -ItemType Directory -Force | Out-Null
}

$List       = Get-PnPList -Identity $LibraryName -Includes RootFolder
$LibraryUrl = $List.RootFolder.ServerRelativeUrl

$CAML = @"
&lt;View Scope='RecursiveAll'&gt;
  &lt;Query&gt;
    &lt;Where&gt;
      &lt;Gt&gt;
        &lt;FieldRef Name='File_x0020_Size'/&gt;
        &lt;Value Type='Number'&gt;$MinBytes&lt;/Value&gt;
      &lt;/Gt&gt;
    &lt;/Where&gt;
    &lt;OrderBy&gt;
      &lt;FieldRef Name='File_x0020_Size' Ascending='FALSE'/&gt;
    &lt;/OrderBy&gt;
  &lt;/Query&gt;
  &lt;ViewFields&gt;
    &lt;FieldRef Name='FileRef'/&gt;
    &lt;FieldRef Name='FileLeafRef'/&gt;
    &lt;FieldRef Name='File_x0020_Size'/&gt;
    &lt;FieldRef Name='File_x0020_Type'/&gt;
    &lt;FieldRef Name='Modified'/&gt;
  &lt;/ViewFields&gt;
&lt;/View&gt;
"@

Write-Host "Searching for files larger than $MinSizeMB MB..." -ForegroundColor Cyan

$BigFiles = Get-PnPListItem -List $LibraryName -Query $CAML -PageSize 2000 |
            Where-Object { $_.FileSystemObjectType -eq "File" }

$Total = @($BigFiles).Count

if ($Total -eq 0) {
    Write-Host "No files larger than $MinSizeMB MB found." -ForegroundColor Yellow
    Disconnect-PnPOnline
    return
}

$TotalMB = &#91;math]::Round((($BigFiles | ForEach-Object { &#91;int64]$_.FieldValues.File_x0020_Size } | Measure-Object -Sum).Sum / 1MB), 2)
$TotalGB = &#91;math]::Round($TotalMB / 1024, 2)

Write-Host "Found $Total files larger than $MinSizeMB MB" -ForegroundColor Cyan
Write-Host "Total download size: $TotalMB MB ($TotalGB GB)`n" -ForegroundColor Cyan

$Confirm = Read-Host "Continue with download? (Y/N)"
if ($Confirm -ne "Y") {
    Write-Host "Cancelled by user." -ForegroundColor Yellow
    Disconnect-PnPOnline
    return
}

$Downloaded    = 0
$Failed        = 0
$Counter       = 0
$BytesDone     = 0
$Log           = @()
$StartTime     = Get-Date

foreach ($Item in $BigFiles) {

    $Counter   = $Counter + 1
    $SourceUrl = $Item.FieldValues.FileRef
    $FileName  = $Item.FieldValues.FileLeafRef -replace '&#91;\\/:*?"&lt;&gt;|]', '_'
    $Bytes     = &#91;int64]$Item.FieldValues.File_x0020_Size
    $SizeMB    = &#91;math]::Round($Bytes / 1MB, 2)

    if ($KeepStructure) {
        $RelPath      = $SourceUrl.Replace($LibraryUrl, "").Replace("/", "\").TrimStart("\")
        $TargetFolder = Split-Path (Join-Path $DownloadPath $RelPath) -Parent
    }
    else {
        $TargetFolder = $DownloadPath
        $Suffix = 1
        while (Test-Path (Join-Path $TargetFolder $FileName)) {
            $Base      = &#91;System.IO.Path]::GetFileNameWithoutExtension($Item.FieldValues.FileLeafRef)
            $Ext       = &#91;System.IO.Path]::GetExtension($Item.FieldValues.FileLeafRef)
            $FileName  = "$($Base)_$Suffix$Ext"
            $Suffix    = $Suffix + 1
        }
    }

    if (-not (Test-Path $TargetFolder)) {
        New-Item -Path $TargetFolder -ItemType Directory -Force | Out-Null
    }

    $PctDone = &#91;math]::Round(($BytesDone / ($TotalMB * 1MB)) * 100, 1)

    Write-Progress -Activity "Downloading files over $MinSizeMB MB" `
                   -Status "$Counter of $Total : $FileName ($SizeMB MB) - $PctDone% of data" `
                   -PercentComplete (($Counter / $Total) * 100)

    $Attempt = 0
    $Success = $false

    while (-not $Success -and $Attempt -lt $MaxRetries) {

        $Attempt = $Attempt + 1

        try {
            Get-PnPFile -Url $SourceUrl -Path $TargetFolder -FileName $FileName -AsFile -Force -ErrorAction Stop

            $Success    = $true
            $Downloaded = $Downloaded + 1
            $BytesDone  = $BytesDone + $Bytes

            Write-Host "&#91;$Counter/$Total] $FileName ($SizeMB MB)" -ForegroundColor Green

            $Log += &#91;PSCustomObject]@{
                FileName       = $FileName
                SizeMB         = $SizeMB
                FileType       = $Item.FieldValues.File_x0020_Type
                SharePointPath = $SourceUrl
                Modified       = $Item.FieldValues.Modified
                Status         = "Success"
            }
        }
        catch {
            if ($Attempt -ge $MaxRetries) {
                $Failed = $Failed + 1
                Write-Host "&#91;$Counter/$Total] FAILED: $FileName ($SizeMB MB)" -ForegroundColor Red

                $Log += &#91;PSCustomObject]@{
                    FileName       = $FileName
                    SizeMB         = $SizeMB
                    FileType       = $Item.FieldValues.File_x0020_Type
                    SharePointPath = $SourceUrl
                    Modified       = $Item.FieldValues.Modified
                    Status         = "Failed: $($_.Exception.Message)"
                }
            }
            else {
                Start-Sleep -Seconds (&#91;math]::Pow(2, $Attempt) * 5)
            }
        }
    }
}

Write-Progress -Activity "Downloading large files" -Completed

if ($Log.Count -gt 0) {
    $Log | Export-Csv -Path $LogFile -NoTypeInformation -Encoding UTF8
}

$Elapsed      = (Get-Date) - $StartTime
$DownloadedMB = &#91;math]::Round($BytesDone / 1MB, 2)
$SpeedMBps    = if ($Elapsed.TotalSeconds -gt 0) { &#91;math]::Round($DownloadedMB / $Elapsed.TotalSeconds, 2) } else { 0 }

Write-Host "`n================ SUMMARY ================" -ForegroundColor Cyan
Write-Host " Size threshold : $MinSizeMB MB"
Write-Host " Files found    : $Total"
Write-Host " Downloaded     : $Downloaded" -ForegroundColor Green
Write-Host " Failed         : $Failed"     -ForegroundColor Red
Write-Host " Data pulled    : $DownloadedMB MB"
Write-Host " Elapsed        : $($Elapsed.ToString('hh\:mm\:ss'))"
Write-Host " Avg speed      : $SpeedMBps MB/s"
Write-Host " Saved to       : $DownloadPath"
Write-Host " Log file       : $LogFile"
Write-Host "=========================================`n" -ForegroundColor Cyan

Disconnect-PnPOnline</code></pre>



<p>Here, the first script pulls every item in the library and then filters in PowerShell. That works, but on a 50,000-item library you&#8217;re transferring metadata for all 50,000 items just to find the 300 that are over 5 MB.</p>



<p>The CAML version pushes the&nbsp;<code>File_x0020_Size</code>&nbsp;comparison to the server. SharePoint returns only the matching items. The&nbsp;<code>&lt;OrderBy&gt;</code>&nbsp;clause also sorts largest-first, so you see the biggest offenders immediately and can cancel early if the total looks wrong.</p>



<p>One thing to note:&nbsp;<code>File_x0020_Size</code>&nbsp;is not stored on folder objects, so the&nbsp;<code>Where-Object { $_.FileSystemObjectType -eq "File" }</code>&nbsp;line stays in as a safety filter.</p>



<p><strong>Files Between 5 MB and 100 MB</strong></p>



<pre class="wp-block-code"><code>$MinBytes = 5MB
$MaxBytes = 100MB

$CAML = @"
&lt;View Scope='RecursiveAll'&gt;
  &lt;Query&gt;
    &lt;Where&gt;
      &lt;And&gt;
        &lt;Gt&gt;&lt;FieldRef Name='File_x0020_Size'/&gt;&lt;Value Type='Number'&gt;$MinBytes&lt;/Value&gt;&lt;/Gt&gt;
        &lt;Lt&gt;&lt;FieldRef Name='File_x0020_Size'/&gt;&lt;Value Type='Number'&gt;$MaxBytes&lt;/Value&gt;&lt;/Lt&gt;
      &lt;/And&gt;
    &lt;/Where&gt;
  &lt;/Query&gt;
&lt;/View&gt;
"@</code></pre>



<p><strong>Large Files of a Specific Type</strong></p>



<pre class="wp-block-code"><code>$MinBytes = 5MB

$CAML = @"
&lt;View Scope='RecursiveAll'&gt;
  &lt;Query&gt;
    &lt;Where&gt;
      &lt;And&gt;
        &lt;Gt&gt;&lt;FieldRef Name='File_x0020_Size'/&gt;&lt;Value Type='Number'&gt;$MinBytes&lt;/Value&gt;&lt;/Gt&gt;
        &lt;Eq&gt;&lt;FieldRef Name='File_x0020_Type'/&gt;&lt;Value Type='Text'&gt;mp4&lt;/Value&gt;&lt;/Eq&gt;
      &lt;/And&gt;
    &lt;/Where&gt;
  &lt;/Query&gt;
&lt;/View&gt;
"@</code></pre>



<p><strong>Large Files Not Modified in Over a Year</strong></p>



<pre class="wp-block-code"><code>$MinBytes = 5MB
$CutOff   = (Get-Date).AddYears(-1).ToString("yyyy-MM-ddTHH:mm:ssZ")

$CAML = @"
&lt;View Scope='RecursiveAll'&gt;
  &lt;Query&gt;
    &lt;Where&gt;
      &lt;And&gt;
        &lt;Gt&gt;&lt;FieldRef Name='File_x0020_Size'/&gt;&lt;Value Type='Number'&gt;$MinBytes&lt;/Value&gt;&lt;/Gt&gt;
        &lt;Leq&gt;&lt;FieldRef Name='Modified'/&gt;&lt;Value Type='DateTime' IncludeTimeValue='TRUE'&gt;$CutOff&lt;/Value&gt;&lt;/Leq&gt;
      &lt;/And&gt;
    &lt;/Where&gt;
  &lt;/Query&gt;
&lt;/View&gt;
"@</code></pre>



<p>Check out <a href="https://www.spguides.com/create-and-delete-sharepoint-document-library-using-powershell/">Create and Delete SharePoint Document Library Using PowerShell</a></p>



<h2 class="wp-block-heading">Download Files from a Specific Folder in SharePoint</h2>



<p>If you only need one folder (and not the entire library), use&nbsp;<code>Get-PnPFolderItem</code>. This is much faster because it doesn&#8217;t scan the whole library.</p>



<p><strong>Download files from one folder only (no subfolders):</strong></p>



<pre class="wp-block-code"><code>$FolderUrl    = "/sites/Finance/Shared Documents/2025/Q1 Reports"
$DownloadPath = "C:\SPDownloads\Q1Reports"

New-Item -Path $DownloadPath -ItemType Directory -Force | Out-Null

$Files = Get-PnPFolderItem -FolderSiteRelativeUrl "Shared Documents/2025/Q1 Reports" -ItemType File

foreach ($File in $Files) {
    Get-PnPFile -Url $File.ServerRelativeUrl -Path $DownloadPath `
                -FileName $File.Name -AsFile -Force
}</code></pre>



<p>Here is the complete PnP PowerShell script:</p>



<pre class="wp-block-code"><code># ------------------------- CONFIGURATION -------------------------
$SiteURL      = "https://contoso.sharepoint.com/sites/Finance"
$ClientId     = "11111111-2222-3333-4444-555555555555"
$FolderUrl    = "/sites/Finance/Shared Documents/2025/Q1 Reports"
$SiteRelative = "Shared Documents/2025/Q1 Reports"
$DownloadPath = "C:\SPDownloads\Q1Reports"
$IncludeSubfolders = $false
$MaxRetries   = 3
$LogFile      = "C:\SPDownloads\FolderDownload_$(Get-Date -f 'yyyyMMdd_HHmmss').csv"
# -----------------------------------------------------------------

try {
    Connect-PnPOnline -Url $SiteURL -Interactive -ClientId $ClientId -ErrorAction Stop
    Write-Host "Connected to $SiteURL" -ForegroundColor Green
}
catch {
    Write-Host "Connection failed: $($_.Exception.Message)" -ForegroundColor Red
    return
}

if (-not (Test-Path $DownloadPath)) {
    New-Item -Path $DownloadPath -ItemType Directory -Force | Out-Null
}

function Get-FilesRecursive {
    param (
        &#91;string]$RelativeUrl,
        &#91;string]$LocalPath
    )

    if (-not (Test-Path $LocalPath)) {
        New-Item -Path $LocalPath -ItemType Directory -Force | Out-Null
    }

    $Items = Get-PnPFolderItem -FolderSiteRelativeUrl $RelativeUrl -ItemType File
    foreach ($Item in $Items) {
        &#91;PSCustomObject]@{
            Name      = $Item.Name
            Url       = $Item.ServerRelativeUrl
            Length    = $Item.Length
            LocalPath = $LocalPath
        }
    }

    if ($IncludeSubfolders) {
        $SubFolders = Get-PnPFolderItem -FolderSiteRelativeUrl $RelativeUrl -ItemType Folder |
                      Where-Object { $_.Name -ne "Forms" }

        foreach ($Sub in $SubFolders) {
            Get-FilesRecursive -RelativeUrl "$RelativeUrl/$($Sub.Name)" -LocalPath (Join-Path $LocalPath $Sub.Name)
        }
    }
}

Write-Host "Reading folder '$FolderUrl'..." -ForegroundColor Cyan

$Files = @(Get-FilesRecursive -RelativeUrl $SiteRelative -LocalPath $DownloadPath)
$Total = $Files.Count

if ($Total -eq 0) {
    Write-Host "No files found in the specified folder." -ForegroundColor Yellow
    Disconnect-PnPOnline
    return
}

$TotalMB = &#91;math]::Round((($Files | Measure-Object -Property Length -Sum).Sum / 1MB), 2)
Write-Host "Found $Total files ($TotalMB MB). Starting download...`n" -ForegroundColor Cyan

$Downloaded = 0
$Failed     = 0
$Counter    = 0
$Log        = @()

foreach ($File in $Files) {

    $Counter  = $Counter + 1
    $FileName = $File.Name -replace '&#91;\\/:*?"&lt;&gt;|]', '_'
    $SizeMB   = &#91;math]::Round($File.Length / 1MB, 2)

    Write-Progress -Activity "Downloading from '$SiteRelative'" `
                   -Status "$Counter of $Total : $FileName" `
                   -PercentComplete (($Counter / $Total) * 100)

    $Attempt = 0
    $Success = $false

    while (-not $Success -and $Attempt -lt $MaxRetries) {

        $Attempt = $Attempt + 1

        try {
            Get-PnPFile -Url $File.Url -Path $File.LocalPath -FileName $FileName -AsFile -Force -ErrorAction Stop

            $Success    = $true
            $Downloaded = $Downloaded + 1

            Write-Host "&#91;$Counter/$Total] $FileName ($SizeMB MB)" -ForegroundColor Green

            $Log += &#91;PSCustomObject]@{
                FileName       = $FileName
                SizeMB         = $SizeMB
                SharePointPath = $File.Url
                LocalFolder    = $File.LocalPath
                Status         = "Success"
            }
        }
        catch {
            if ($Attempt -ge $MaxRetries) {
                $Failed = $Failed + 1
                Write-Host "&#91;$Counter/$Total] FAILED: $FileName" -ForegroundColor Red

                $Log += &#91;PSCustomObject]@{
                    FileName       = $FileName
                    SizeMB         = $SizeMB
                    SharePointPath = $File.Url
                    LocalFolder    = $File.LocalPath
                    Status         = "Failed: $($_.Exception.Message)"
                }
            }
            else {
                Start-Sleep -Seconds (&#91;math]::Pow(2, $Attempt) * 5)
            }
        }
    }
}

Write-Progress -Activity "Downloading folder" -Completed

if ($Log.Count -gt 0) {
    $Log | Export-Csv -Path $LogFile -NoTypeInformation -Encoding UTF8
}

Write-Host "`n============== SUMMARY ==============" -ForegroundColor Cyan
Write-Host " Source folder : $FolderUrl"
Write-Host " Subfolders    : $IncludeSubfolders"
Write-Host " Files found   : $Total"
Write-Host " Downloaded    : $Downloaded" -ForegroundColor Green
Write-Host " Failed        : $Failed"     -ForegroundColor Red
Write-Host " Total size    : $TotalMB MB"
Write-Host " Saved to      : $DownloadPath"
Write-Host " Log file      : $LogFile"
Write-Host "=====================================`n" -ForegroundColor Cyan

Disconnect-PnPOnline</code></pre>



<p><strong>Download a folder including all its subfolders (recursive):</strong></p>



<pre class="wp-block-code"><code>$FolderUrl    = "/sites/Finance/Shared Documents/2025"
$DownloadPath = "C:\SPDownloads\2025"

function Download-SPFolder {
    param($SPFolderUrl, $LocalPath)

    if (-not (Test-Path $LocalPath)) {
        New-Item -Path $LocalPath -ItemType Directory -Force | Out-Null
    }

    # Download files in the current folder
    Get-PnPFolderItem -FolderSiteRelativeUrl $SPFolderUrl -ItemType File | ForEach-Object {
        Get-PnPFile -Url $_.ServerRelativeUrl -Path $LocalPath -FileName $_.Name -AsFile -Force
        Write-Host "Downloaded: $($_.Name)"
    }

    # Recurse into subfolders
    Get-PnPFolderItem -FolderSiteRelativeUrl $SPFolderUrl -ItemType Folder | ForEach-Object {
        Download-SPFolder -SPFolderUrl "$SPFolderUrl/$($_.Name)" `
                          -LocalPath (Join-Path $LocalPath $_.Name)
    }
}

Download-SPFolder -SPFolderUrl "Shared Documents/2025" -LocalPath $DownloadPath</code></pre>



<p>Note:&nbsp;<code>-FolderSiteRelativeUrl</code>&nbsp;is relative to the&nbsp;<strong>site</strong>, not the server. So use&nbsp;<code>Shared Documents/2025</code>, not&nbsp;<code>/sites/Finance/Shared Documents/2025</code>.</p>



<p>Check out <a href="https://www.spguides.com/get-sharepoint-folder-permissions-using-powershell/">Get SharePoint Folder Permissions Using PnP PowerShell</a></p>



<h2 class="wp-block-heading">Download Files Older Than 30 Days</h2>



<p>Perfect for archiving. This filters on the&nbsp;<strong>Modified</strong>&nbsp;date, but you can switch to&nbsp;<strong>Created</strong>&nbsp;just as easily.</p>



<pre class="wp-block-code"><code>$DaysOld  = 30
$CutOff   = (Get-Date).AddDays(-$DaysOld)

$OldFiles = Get-PnPListItem -List $LibraryName -PageSize 2000 |
            Where-Object {
                $_.FileSystemObjectType -eq "File" -and
                &#91;datetime]$_.FieldValues.Modified -lt $CutOff
            }

Write-Host "Files older than $DaysOld days: $($OldFiles.Count)"

foreach ($Item in $OldFiles) {
    Get-PnPFile -Url $Item.FieldValues.FileRef -Path $DownloadPath `
                -FileName $Item.FieldValues.FileLeafRef -AsFile -Force
}</code></pre>



<p><strong>Server-side version using CAML</strong>&nbsp;(faster on big libraries):</p>



<pre class="wp-block-code"><code>$CutOffString = (Get-Date).AddDays(-30).ToString("yyyy-MM-ddTHH:mm:ssZ")

$CAML = @"
&lt;View Scope='RecursiveAll'&gt;
  &lt;Query&gt;
    &lt;Where&gt;
      &lt;Lt&gt;
        &lt;FieldRef Name='Modified'/&gt;
        &lt;Value Type='DateTime' IncludeTimeValue='TRUE'&gt;$CutOffString&lt;/Value&gt;
      &lt;/Lt&gt;
    &lt;/Where&gt;
  &lt;/Query&gt;
&lt;/View&gt;
"@

$OldFiles = Get-PnPListItem -List $LibraryName -Query $CAML -PageSize 2000</code></pre>



<h2 class="wp-block-heading">Download Files Modified in the Last 7 Days (Incremental Download)</h2>



<p>This is the reverse of the above and is extremely useful if you&#8217;re running a scheduled sync job – you only pull what changed.</p>



<pre class="wp-block-code"><code>$Since = (Get-Date).AddDays(-7)

$RecentFiles = Get-PnPListItem -List $LibraryName -PageSize 2000 |
               Where-Object {
                   $_.FileSystemObjectType -eq "File" -and
                   &#91;datetime]$_.FieldValues.Modified -ge $Since
               }

foreach ($Item in $RecentFiles) {
    Get-PnPFile -Url $Item.FieldValues.FileRef -Path $DownloadPath `
                -FileName $Item.FieldValues.FileLeafRef -AsFile -Force
}</code></pre>



<p><strong>Even smarter – skip files that haven&#8217;t changed locally:</strong></p>



<pre class="wp-block-code"><code>foreach ($Item in $Items) {
    $LocalFile = Join-Path $DownloadPath $Item.FieldValues.FileLeafRef
    $SPModified = &#91;datetime]$Item.FieldValues.Modified

    if (Test-Path $LocalFile) {
        $LocalModified = (Get-Item $LocalFile).LastWriteTime
        if ($LocalModified -ge $SPModified) {
            Write-Host "Skipping (unchanged): $($Item.FieldValues.FileLeafRef)" -ForegroundColor DarkGray
            continue
        }
    }

    Get-PnPFile -Url $Item.FieldValues.FileRef -Path $DownloadPath `
                -FileName $Item.FieldValues.FileLeafRef -AsFile -Force
}</code></pre>



<h2 class="wp-block-heading">Download Files Created or Modified by a Specific User</h2>



<p>Handy during offboarding, when you need to collect everything a departing employee uploaded.</p>



<pre class="wp-block-code"><code>$TargetUser = "john.doe@contoso.com"

$UserFiles = Get-PnPListItem -List $LibraryName -PageSize 2000 |
             Where-Object {
                 $_.FileSystemObjectType -eq "File" -and
                 $_.FieldValues.Author.Email -eq $TargetUser
             }

foreach ($Item in $UserFiles) {
    Get-PnPFile -Url $Item.FieldValues.FileRef -Path $DownloadPath `
                -FileName $Item.FieldValues.FileLeafRef -AsFile -Force
}</code></pre>



<p>Change&nbsp;<code>Author</code>&nbsp;to&nbsp;<code>Editor</code>&nbsp;if you want files&nbsp;<strong>last modified by</strong>&nbsp;that person instead of files they created.</p>



<p>Here is the complete PnP PowerShell script:</p>



<pre class="wp-block-code"><code># ------------------------- CONFIGURATION -------------------------
$SiteURL       = "https://contoso.sharepoint.com/sites/Finance"
$ClientId      = "11111111-2222-3333-4444-555555555555"
$LibraryName   = "Documents"
$DownloadPath  = "C:\SPDownloads\UserFiles"
$TargetUser    = "john.doe@contoso.com"
$MatchField    = "Author"        # Author = created by, Editor = last modified by
$KeepStructure = $true
$MaxRetries    = 3
$LogFile       = "C:\SPDownloads\UserFiles_$(Get-Date -f 'yyyyMMdd_HHmmss').csv"
# -----------------------------------------------------------------

try {
    Connect-PnPOnline -Url $SiteURL -Interactive -ClientId $ClientId -ErrorAction Stop
    Write-Host "Connected to $SiteURL" -ForegroundColor Green
}
catch {
    Write-Host "Connection failed: $($_.Exception.Message)" -ForegroundColor Red
    return
}

try {
    $User   = Get-PnPUser | Where-Object { $_.Email -eq $TargetUser }
    if ($null -eq $User) {
        $User = New-PnPUser -LoginName $TargetUser -ErrorAction Stop
    }
    $UserId = $User.Id
    Write-Host "Resolved user '$TargetUser' to ID $UserId" -ForegroundColor Gray
}
catch {
    Write-Host "Could not resolve user '$TargetUser': $($_.Exception.Message)" -ForegroundColor Red
    Disconnect-PnPOnline
    return
}

if (-not (Test-Path $DownloadPath)) {
    New-Item -Path $DownloadPath -ItemType Directory -Force | Out-Null
}

$List       = Get-PnPList -Identity $LibraryName -Includes RootFolder
$LibraryUrl = $List.RootFolder.ServerRelativeUrl

$CAML = @"
&lt;View Scope='RecursiveAll'&gt;
  &lt;Query&gt;
    &lt;Where&gt;
      &lt;Eq&gt;
        &lt;FieldRef Name='$MatchField' LookupId='TRUE'/&gt;
        &lt;Value Type='Integer'&gt;$UserId&lt;/Value&gt;
      &lt;/Eq&gt;
    &lt;/Where&gt;
  &lt;/Query&gt;
  &lt;ViewFields&gt;
    &lt;FieldRef Name='FileRef'/&gt;
    &lt;FieldRef Name='FileLeafRef'/&gt;
    &lt;FieldRef Name='File_x0020_Size'/&gt;
    &lt;FieldRef Name='Author'/&gt;
    &lt;FieldRef Name='Editor'/&gt;
    &lt;FieldRef Name='Created'/&gt;
    &lt;FieldRef Name='Modified'/&gt;
  &lt;/ViewFields&gt;
&lt;/View&gt;
"@

Write-Host "Searching for files where $MatchField = $TargetUser..." -ForegroundColor Cyan

$UserFiles = Get-PnPListItem -List $LibraryName -Query $CAML -PageSize 2000 |
             Where-Object { $_.FileSystemObjectType -eq "File" }

$Total = @($UserFiles).Count

if ($Total -eq 0) {
    Write-Host "No files found for user '$TargetUser'." -ForegroundColor Yellow
    Disconnect-PnPOnline
    return
}

$TotalMB = &#91;math]::Round((($UserFiles | ForEach-Object { &#91;int64]$_.FieldValues.File_x0020_Size } | Measure-Object -Sum).Sum / 1MB), 2)
Write-Host "Found $Total files ($TotalMB MB). Starting download...`n" -ForegroundColor Cyan

$Downloaded = 0
$Failed     = 0
$Counter    = 0
$Log        = @()

foreach ($Item in $UserFiles) {

    $Counter   = $Counter + 1
    $SourceUrl = $Item.FieldValues.FileRef
    $FileName  = $Item.FieldValues.FileLeafRef -replace '&#91;\\/:*?"&lt;&gt;|]', '_'
    $SizeMB    = &#91;math]::Round(&#91;int64]$Item.FieldValues.File_x0020_Size / 1MB, 2)

    if ($KeepStructure) {
        $RelPath      = $SourceUrl.Replace($LibraryUrl, "").Replace("/", "\").TrimStart("\")
        $TargetFolder = Split-Path (Join-Path $DownloadPath $RelPath) -Parent
    }
    else {
        $TargetFolder = $DownloadPath
        $Suffix = 1
        while (Test-Path (Join-Path $TargetFolder $FileName)) {
            $Base     = &#91;System.IO.Path]::GetFileNameWithoutExtension($Item.FieldValues.FileLeafRef)
            $Ext      = &#91;System.IO.Path]::GetExtension($Item.FieldValues.FileLeafRef)
            $FileName = "$($Base)_$Suffix$Ext"
            $Suffix   = $Suffix + 1
        }
    }

    if (-not (Test-Path $TargetFolder)) {
        New-Item -Path $TargetFolder -ItemType Directory -Force | Out-Null
    }

    Write-Progress -Activity "Downloading files for $TargetUser" `
                   -Status "$Counter of $Total : $FileName" `
                   -PercentComplete (($Counter / $Total) * 100)

    $Attempt = 0
    $Success = $false

    while (-not $Success -and $Attempt -lt $MaxRetries) {

        $Attempt = $Attempt + 1

        try {
            Get-PnPFile -Url $SourceUrl -Path $TargetFolder -FileName $FileName -AsFile -Force -ErrorAction Stop

            $Success    = $true
            $Downloaded = $Downloaded + 1

            Write-Host "&#91;$Counter/$Total] $FileName ($SizeMB MB)" -ForegroundColor Green

            $Log += &#91;PSCustomObject]@{
                FileName       = $FileName
                SizeMB         = $SizeMB
                CreatedBy      = $Item.FieldValues.Author.LookupValue
                ModifiedBy     = $Item.FieldValues.Editor.LookupValue
                Created        = $Item.FieldValues.Created
                Modified       = $Item.FieldValues.Modified
                SharePointPath = $SourceUrl
                Status         = "Success"
            }
        }
        catch {
            if ($Attempt -ge $MaxRetries) {
                $Failed = $Failed + 1
                Write-Host "&#91;$Counter/$Total] FAILED: $FileName" -ForegroundColor Red

                $Log += &#91;PSCustomObject]@{
                    FileName       = $FileName
                    SizeMB         = $SizeMB
                    CreatedBy      = $Item.FieldValues.Author.LookupValue
                    ModifiedBy     = $Item.FieldValues.Editor.LookupValue
                    Created        = $Item.FieldValues.Created
                    Modified       = $Item.FieldValues.Modified
                    SharePointPath = $SourceUrl
                    Status         = "Failed: $($_.Exception.Message)"
                }
            }
            else {
                Start-Sleep -Seconds (&#91;math]::Pow(2, $Attempt) * 5)
            }
        }
    }
}

Write-Progress -Activity "Downloading user files" -Completed

if ($Log.Count -gt 0) {
    $Log | Export-Csv -Path $LogFile -NoTypeInformation -Encoding UTF8
}

Write-Host "`n============== SUMMARY ==============" -ForegroundColor Cyan
Write-Host " User        : $TargetUser"
Write-Host " Match field : $MatchField"
Write-Host " Files found : $Total"
Write-Host " Downloaded  : $Downloaded" -ForegroundColor Green
Write-Host " Failed      : $Failed"     -ForegroundColor Red
Write-Host " Total size  : $TotalMB MB"
Write-Host " Saved to    : $DownloadPath"
Write-Host " Log file    : $LogFile"
Write-Host "=====================================`n" -ForegroundColor Cyan

Disconnect-PnPOnline</code></pre>



<h2 class="wp-block-heading">Download Files Based on a Metadata Column Value</h2>



<p>Most real-world libraries have custom columns like Department, Status, or Project. You can filter on those too.</p>



<pre class="wp-block-code"><code># Download only files where the "Status" choice column equals "Approved"
$Approved = Get-PnPListItem -List $LibraryName -PageSize 2000 |
            Where-Object { $_.FieldValues.Status -eq "Approved" }

foreach ($Item in $Approved) {
    Get-PnPFile -Url $Item.FieldValues.FileRef -Path $DownloadPath `
                -FileName $Item.FieldValues.FileLeafRef -AsFile -Force
}</code></pre>



<p>Not sure of the internal column name? Run this to list them:</p>



<pre class="wp-block-code"><code>Get-PnPField -List $LibraryName |
    Select-Object Title, InternalName, TypeAsString |
    Sort-Object Title</code></pre>



<p>Note: Always use the&nbsp;<strong>InternalName</strong>&nbsp;in your scripts, not the display name. Spaces become&nbsp;<code>_x0020_</code>&nbsp;in internal names.</p>



<p>Check out <a href="https://www.spguides.com/add-5000-items-sharepoint-list-pnp-powershell/">Add More Than 5000 Items to a SharePoint Online List Using PnP PowerShell</a></p>



<h2 class="wp-block-heading">Download Files by Name Pattern (Wildcard Search)</h2>



<p>Need all files with &#8220;Invoice&#8221; in the name? Use a simple&nbsp;<code>-like</code>&nbsp;match.</p>



<pre class="wp-block-code"><code>$Pattern = "*Invoice*"

$Matched = Get-PnPListItem -List $LibraryName -PageSize 2000 |
           Where-Object {
               $_.FileSystemObjectType -eq "File" -and
               $_.FieldValues.FileLeafRef -like $Pattern
           }

foreach ($Item in $Matched) {
    Get-PnPFile -Url $Item.FieldValues.FileRef -Path $DownloadPath `
                -FileName $Item.FieldValues.FileLeafRef -AsFile -Force
}</code></pre>



<h2 class="wp-block-heading">Download a Single File from SharePoint</h2>



<p>If you just need one specific document, you don&#8217;t need any loops at all:</p>



<pre class="wp-block-code"><code>Get-PnPFile -Url "/sites/Finance/Shared Documents/Budget 2025.xlsx" `
            -Path "C:\SPDownloads" `
            -FileName "Budget 2025.xlsx" -AsFile -Force
</code></pre>



<p>You can also grab the file&#8217;s contents directly into memory instead of saving it:</p>



<pre class="wp-block-code"><code>$Content = Get-PnPFile -Url "/sites/Finance/Shared Documents/Notes.txt" -AsString
Write-Host $Content</code></pre>



<h2 class="wp-block-heading">Download All Files and Zip Them Automatically</h2>



<p>Great for handing over an archive to someone or for a monthly backup job.</p>



<pre class="wp-block-code"><code>$DownloadPath = "C:\SPDownloads\Finance"
$ZipPath      = "C:\SPDownloads\Finance_$(Get-Date -Format 'yyyyMMdd').zip"

# ... run your download loop first ...

Compress-Archive -Path "$DownloadPath\*" -DestinationPath $ZipPath -Force
Write-Host "Archive created: $ZipPath" -ForegroundColor Green</code></pre>



<h2 class="wp-block-heading">Download Files from ALL Document Libraries on a Site</h2>



<p>If you want everything on a site (not just one library), loop through all libraries and skip the system ones.</p>



<pre class="wp-block-code"><code>$ExcludeLists = @("Form Templates", "Site Assets", "Style Library",
                  "Site Pages", "Preservation Hold Library", "Customized Reports")

$Libraries = Get-PnPList | Where-Object {
    $_.BaseTemplate -eq 101 -and $_.Hidden -eq $false -and
    $ExcludeLists -notcontains $_.Title
}

foreach ($Lib in $Libraries) {

    $LibFolder = Join-Path "C:\SPDownloads" ($Lib.Title -replace '&#91;\\/:*?"&lt;&gt;|]', '_')
    New-Item -Path $LibFolder -ItemType Directory -Force | Out-Null

    Write-Host "`n=== Library: $($Lib.Title) ===" -ForegroundColor Yellow

    Get-PnPListItem -List $Lib.Title -PageSize 2000 |
    Where-Object { $_.FileSystemObjectType -eq "File" } |
    ForEach-Object {
        Get-PnPFile -Url $_.FieldValues.FileRef -Path $LibFolder `
                    -FileName $_.FieldValues.FileLeafRef -AsFile -Force
    }
}</code></pre>



<h2 class="wp-block-heading">Add Logging, Retry Logic, and Error Handling (Production-Ready Script)</h2>



<p>The scripts above are fine for a one-off task. But if you&#8217;re downloading 20,000 files, you need logging and retries – because SharePoint Online&nbsp;<strong>will</strong>&nbsp;throttle you.</p>



<p>Here&#8217;s a hardened version you can use in production.</p>



<pre class="wp-block-code"><code># ---------- CONFIGURATION ----------
$SiteURL      = "https://contoso.sharepoint.com/sites/Finance"
$ClientId     = "11111111-2222-3333-4444-555555555555"
$LibraryName  = "Documents"
$DownloadPath = "C:\SPDownloads\Finance"
$LogFile      = "C:\SPDownloads\DownloadLog_$(Get-Date -f 'yyyyMMdd_HHmmss').csv"
$MaxRetries   = 3
# -----------------------------------

function Write-Log {
    param($File, $Status, $Message)
    &#91;PSCustomObject]@{
        Timestamp = (Get-Date -Format "yyyy-MM-dd HH:mm:ss")
        File      = $File
        Status    = $Status
        Message   = $Message
    } | Export-Csv -Path $LogFile -NoTypeInformation -Append
}

Connect-PnPOnline -Url $SiteURL -Interactive -ClientId $ClientId

$List       = Get-PnPList -Identity $LibraryName -Includes RootFolder
$LibraryUrl = $List.RootFolder.ServerRelativeUrl

$Items = Get-PnPListItem -List $LibraryName -PageSize 2000 |
         Where-Object { $_.FileSystemObjectType -eq "File" }

$Total = $Items.Count
$Ok = 0; $Fail = 0; $n = 0

foreach ($Item in $Items) {

    $n++
    $FileUrl  = $Item.FieldValues.FileRef
    $FileName = $Item.FieldValues.FileLeafRef

    $RelPath     = $FileUrl.Replace($LibraryUrl, "").Replace("/", "\").TrimStart("\")
    $LocalFile   = Join-Path $DownloadPath $RelPath
    $LocalFolder = Split-Path $LocalFile -Parent

    if (-not (Test-Path $LocalFolder)) {
        New-Item -Path $LocalFolder -ItemType Directory -Force | Out-Null
    }

    Write-Progress -Activity "Downloading from $LibraryName" `
                   -Status "$n / $Total : $FileName" `
                   -PercentComplete (($n / $Total) * 100)

    $Attempt = 0
    $Done = $false

    while (-not $Done -and $Attempt -lt $MaxRetries) {
        $Attempt++
        try {
            Get-PnPFile -Url $FileUrl -Path $LocalFolder -FileName $FileName -AsFile -Force -ErrorAction Stop
            Write-Log -File $FileUrl -Status "Success" -Message "Attempt $Attempt"
            $Ok++; $Done = $true
        }
        catch {
            if ($Attempt -ge $MaxRetries) {
                Write-Log -File $FileUrl -Status "Failed" -Message $_.Exception.Message
                Write-Host "FAILED: $FileName" -ForegroundColor Red
                $Fail++
            }
            else {
                # Exponential back-off for throttling (429 / 503)
                Start-Sleep -Seconds (&#91;math]::Pow(2, $Attempt) * 5)
            }
        }
    }
}

Write-Host "`n===== SUMMARY =====" -ForegroundColor Cyan
Write-Host "Total files : $Total"
Write-Host "Downloaded  : $Ok"   -ForegroundColor Green
Write-Host "Failed      : $Fail" -ForegroundColor Red
Write-Host "Log file    : $LogFile"

Disconnect-PnPOnline</code></pre>



<p>Why the exponential back-off matters: when SharePoint Online throttles you, it returns HTTP 429 or 503. If you keep hammering it, the throttling gets more aggressive. Waiting 10 seconds, then 20, then 40 gives the service time to breathe and dramatically improves your success rate on large jobs.</p>



<h2 class="wp-block-heading">Handle Long File Paths (The 260-Character Problem)</h2>



<p>Windows has a legacy 260-character path limit. SharePoint allows much longer paths, so deeply nested libraries will fail on download with a &#8220;path too long&#8221; error.</p>



<p>Two fixes:</p>



<p><strong>1. Enable long paths in Windows</strong>&nbsp;(requires admin rights and a restart):</p>



<pre class="wp-block-code"><code>New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" `
    -Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force</code></pre>



<p><strong>2. Use a short root download folder and truncate names in your script:</strong></p>



<pre class="wp-block-code"><code>$MaxPath = 240

if ($LocalFile.Length -gt $MaxPath) {
    $Ext  = &#91;System.IO.Path]::GetExtension($FileName)
    $Base = &#91;System.IO.Path]::GetFileNameWithoutExtension($FileName)
    $Allowed = $MaxPath - $LocalFolder.Length - $Ext.Length - 1
    if ($Allowed -gt 10) {
        $FileName  = $Base.Substring(0, $Allowed) + $Ext
        $LocalFile = Join-Path $LocalFolder $FileName
    }
}</code></pre>



<p>Also strip invalid Windows characters from folder names coming from SharePoint:</p>



<pre class="wp-block-code"><code>$SafeName = $Name -replace '&#91;\\/:*?"&lt;&gt;|]', '_'</code></pre>



<h2 class="wp-block-heading">Performance Tips for Large Document Libraries</h2>



<p>If your library has 50,000+ files, these tips will save you hours.</p>



<p><strong>1. Always use&nbsp;<code>-PageSize</code>.</strong>&nbsp;Without it, you&#8217;ll hit the 5,000 item list view threshold error.&nbsp;<code>-PageSize 2000</code>&nbsp;is a good balance.</p>



<p><strong>2. Filter on the server, not in PowerShell.</strong>&nbsp;A CAML query returns only matching items.&nbsp;<code>Where-Object</code>&nbsp;downloads all metadata first, then filters – wasteful on large libraries.</p>



<p><strong>3. Request only the fields you need:</strong></p>



<pre class="wp-block-code"><code>$Items = Get-PnPListItem -List $LibraryName -PageSize 2000 `
         -Fields "FileRef", "FileLeafRef", "File_x0020_Size", "Modified"</code></pre>



<p>This alone can cut your metadata retrieval time in half.</p>



<p><strong>4. Download to a local SSD, not a network drive or mapped OneDrive folder.</strong>&nbsp;Writing to a synced OneDrive folder means every file gets uploaded again immediately – which is both slow and pointless.</p>



<p><strong>5. Batch by folder.</strong>&nbsp;Instead of one script pulling 100,000 files, run separate jobs per top-level folder. If one fails, you don&#8217;t restart everything.</p>



<p><strong>6. Don&#8217;t run it during business hours.</strong>&nbsp;Throttling is far more likely during peak tenant usage. Schedule overnight runs.</p>



<h2 class="wp-block-heading">Verify Your Download Was Complete</h2>



<p>Never assume the download worked. Always compare counts. Below PowerShell script you can run to compare the count.</p>



<pre class="wp-block-code"><code>$SPCount    = (Get-PnPListItem -List $LibraryName -PageSize 2000 |
               Where-Object { $_.FileSystemObjectType -eq "File" }).Count

$LocalCount = (Get-ChildItem -Path $DownloadPath -Recurse -File).Count

Write-Host "SharePoint files : $SPCount"
Write-Host "Local files      : $LocalCount"

if ($SPCount -eq $LocalCount) {
    Write-Host "MATCH - download complete." -ForegroundColor Green
} else {
    Write-Host "MISMATCH - check the log file." -ForegroundColor Red
}</code></pre>



<p>For an even stricter check, compare total byte size:</p>



<pre class="wp-block-code"><code>$SPBytes = (Get-PnPListItem -List $LibraryName -PageSize 2000 |
            Where-Object { $_.FileSystemObjectType -eq "File" } |
            Measure-Object -Property { &#91;int64]$_.FieldValues.File_x0020_Size } -Sum).Sum

$LocalBytes = (Get-ChildItem $DownloadPath -Recurse -File | Measure-Object Length -Sum).Sum

Write-Host "SharePoint: $(&#91;math]::Round($SPBytes/1GB,2)) GB"
Write-Host "Local     : $(&#91;math]::Round($LocalBytes/1GB,2)) GB"</code></pre>



<h2 class="wp-block-heading">Schedule the Download to Run Automatically</h2>



<p>Once your script works, you can schedule it to run nightly.</p>



<p>Save the script as&nbsp;<code>Download-SPFiles.ps1</code>, switch the connection to certificate-based app-only authentication (interactive login won&#8217;t work unattended), and create a scheduled task:</p>



<pre class="wp-block-code"><code>$Action  = New-ScheduledTaskAction -Execute "pwsh.exe" `
           -Argument "-NoProfile -ExecutionPolicy Bypass -File C:\Scripts\Download-SPFiles.ps1"

$Trigger = New-ScheduledTaskTrigger -Daily -At 2:00AM

Register-ScheduledTask -TaskName "SharePoint Nightly Download" `
    -Action $Action -Trigger $Trigger -RunLevel Highest -Description "Downloads files from SharePoint library"</code></pre>



<p><strong>Tip:</strong> Combine this with the &#8220;modified in the last 7 days&#8221; filter so each nightly run only pulls the delta instead of re-downloading everything.</p>



<h2 class="wp-block-heading">Wrapping Up</h2>



<p>In this tutorial, we learned how to <strong>download all files from a SharePoint document library using PnP PowerShell</strong>.</p>



<p>Here&#8217;s my suggested approach:</p>



<ol class="wp-block-list">
<li>Start with the basic&nbsp;<strong>download all files</strong>&nbsp;script and confirm your connection works on a small library.</li>



<li>Add the filter you actually need – file type, size, date, folder, or metadata.</li>



<li>Once it works, upgrade to the production script with logging and retries.</li>



<li>Verify with the file count and byte size comparison.</li>



<li>Schedule it if this needs to happen regularly.</li>
</ol>



<p>The single biggest mistake I see people make is skipping the verification step. Always compare the SharePoint count with the local count. A script that &#8220;finished successfully&#8221; while silently skipping 400 throttled files is worse than no script at all.</p>



<p>Bookmark this page – you&#8217;ll find yourself coming back to these snippets more often than you&#8217;d expect.</p>



<p>You may also like:</p>



<ul class="wp-block-list">
<li><a href="https://www.spguides.com/remove-all-items-from-a-sharepoint-online-list-using-pnp-powershell/">Delete All Items from a SharePoint List Using PnP PowerShell</a></li>



<li><a href="https://www.spguides.com/add-sharepoint-list-fields-from-excel-powershell/">Add SharePoint List Fields From Excel Using PnP PowerShell</a></li>



<li><a href="https://www.spguides.com/enable-sensitivity-labels-sharepoint-m365-groups-powershell/">Enable Sensitivity Labels For Microsoft 365 Groups &amp; SharePoint Sites Using PowerShell</a></li>



<li><a href="https://www.spguides.com/read-excel-file-and-add-bulk-data-to-sharepoint-list-pnp-powershell/">Add Bulk Data from Excel File to SharePoint List Using PnP PowerShell</a></li>
</ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Get SharePoint Folder Permissions Using PnP PowerShell</title>
		<link>https://www.spguides.com/get-sharepoint-folder-permissions-using-powershell/</link>
		
		<dc:creator><![CDATA[Bijay Kumar]]></dc:creator>
		<pubDate>Sat, 29 Aug 2026 17:29:12 +0000</pubDate>
				<category><![CDATA[PowerShell]]></category>
		<category><![CDATA[Get SharePoint Folder Permissions]]></category>
		<guid isPermaLink="false">https://www.spguides.com/?p=112938</guid>

					<description><![CDATA[Managing permissions in a SharePoint Online document library can become difficult when the library contains multiple folders, subfolders, files, and unique permission assignments. A permissions report helps administrators identify who has access to each folder or file, which permission level they have, and whether the permission is inherited or assigned directly. In this tutorial, you ... <a title="Get SharePoint Folder Permissions Using PnP PowerShell" class="read-more" href="https://www.spguides.com/get-sharepoint-folder-permissions-using-powershell/" aria-label="Read more about Get SharePoint Folder Permissions Using PnP PowerShell">read more...</a>]]></description>
										<content:encoded><![CDATA[
<p>Managing permissions in a <a href="https://www.spguides.com/get-sharepoint-document-library-size-using-powershell/" target="_blank" rel="noreferrer noopener">SharePoint Online document library</a> can become difficult when the library contains multiple folders, subfolders, files, and <a href="https://www.spguides.com/check-site-permissions-in-sharepoint-online/" target="_blank" rel="noreferrer noopener">unique permission</a> assignments. A permissions report helps administrators identify who has access to each folder or file, which permission level they have, and whether the permission is inherited or assigned directly.</p>



<p>In this tutorial, you will learn how to use <strong>PnP PowerShell to get SharePoint folder permissions</strong>, file permissions, and list item permissions. The script exports the results to a CSV file that you can open in Microsoft Excel for review, auditing, or compliance reporting.</p>



<p>The report includes:</p>



<ul class="wp-block-list">
<li>Folder, file, list item, or library name</li>



<li>Direct URL to the SharePoint object</li>



<li>Whether the object has unique permissions</li>



<li>User or SharePoint group name</li>



<li>Email address</li>



<li>Principal type</li>



<li>Assigned permission levels</li>



<li>Whether access was granted directly or through a <a href="https://www.spguides.com/remove-user-from-sharepoint-group-power-automate/" target="_blank" rel="noreferrer noopener">SharePoint group</a></li>
</ul>



<h2 class="wp-block-heading" id="prerequisites">Prerequisites</h2>



<p>Before running the script, make sure you have completed the following requirements:</p>



<ul class="wp-block-list">
<li>Install the latest PnP PowerShell module.</li>



<li>Register a Microsoft Entra ID application and copy its client ID.</li>



<li>Ensure that the signed-in account has sufficient access to read permissions from the target SharePoint site, library, folders, and files.</li>



<li><a href="https://www.spguides.com/create-folder-if-not-exists-using-powershell/" target="_blank" rel="noreferrer noopener">Create a local folder</a> to the CSV permissions report. <strong>Note:</strong> The output file is a CSV file. You can open it directly in Microsoft Excel after the script finishes.</li>
</ul>



<h2 class="wp-block-heading" id="pnp-powershell-script-to-get-sharepoint-folder-per">PnP PowerShell Script to Get SharePoint Folder Permissions</h2>



<p>The following PnP PowerShell script retrieves permissions from a <a href="https://www.spguides.com/create-folders-and-subfolders-in-sharepoint-document-library-using-spfx/" target="_blank" rel="noreferrer noopener">SharePoint document library</a>. It can scan the library itself and, when enabled, every folder, subfolder, file, and list item in that library.</p>



<p>Keep the script unchanged and update the configuration values at the beginning.</p>



<pre class="wp-block-preformatted">$SiteURL="https://.sharepoint.com/sites/RetailManagementSite"<br>$ListName = "Employee Satisfaction Survey PDF Files"<br>$ReportFile="D:\Temp Folder\Employee Satisfaction Survey PDF Files.csv"<br><br>If (Test-Path $ReportFile) {<br>    Remove-Item $ReportFile<br>}<br><br>Function Get-LibraryPermissions([Microsoft.SharePoint.Client.SecurableObject] $Object) {<br>    Switch($Object.TypedObject.ToString()) {<br>        "Microsoft.SharePoint.Client.ListItem" {<br>            If($Object.FileSystemObjectType -eq "Folder") {<br>                $ObjectType = "Folder"<br><br>                <em>#Get the URL of the Folder</em><br>                $Folder = Get-PnPProperty -ClientObject $Object -Property Folder<br>                $ObjectTitle = $Object.Folder.Name<br>                $ObjectURL = ("{0}{1}" -f $Web.Url.Replace($Web.ServerRelativeUrl,''),$Object.Folder.ServerRelativeUrl)<br>            }<br>            Else {<br>                Get-PnPProperty -ClientObject $Object -Property File, ParentList<br><br>                If($Object.File.Name -ne $Null) {<br>                    $ObjectType = "File"<br>                    $ObjectTitle = $Object.File.Name<br>                    $ObjectURL = ("{0}{1}" -f $Web.Url.Replace($Web.ServerRelativeUrl,''),$Object.File.ServerRelativeUrl)<br>                }<br>                else {<br>                    $ObjectType = "List Item"<br>                    $ObjectTitle = $Object["Title"]<br><br>                    <em>#Get the URL of the List Item</em><br>                    $DefaultDisplayFormUrl = Get-PnPProperty -ClientObject $Object.ParentList -Property DefaultDisplayFormUrl<br>                    $ObjectURL = ("{0}{1}?ID={2}" -f $Web.Url.Replace($Web.ServerRelativeUrl,''), $DefaultDisplayFormUrl,$Object.ID)<br>                }<br>            }<br>        }<br><br>        Default {<br>            $ObjectType = "List or Library"<br>            $ObjectTitle = $Object.Title<br><br>            <em>#Get the URL of the List or Library</em><br>            $RootFolder = Get-PnPProperty -ClientObject $Object -Property RootFolder<br>            $ObjectURL = ("{0}{1}" -f $Web.Url.Replace($Web.ServerRelativeUrl,''), $RootFolder.ServerRelativeUrl)<br>        }<br>    }<br><br>    Get-PnPProperty -ClientObject $Object -Property HasUniqueRoleAssignments, RoleAssignments<br><br>    $HasUniquePermissions = $Object.HasUniqueRoleAssignments<br>    $PermissionCollection = @()<br><br>    Foreach($RoleAssignment in $Object.RoleAssignments) {<br>        Get-PnPProperty -ClientObject $RoleAssignment -Property RoleDefinitionBindings, Member<br><br>        $PermissionType = $RoleAssignment.Member.PrincipalType<br>        $PermissionLevels = $RoleAssignment.RoleDefinitionBindings | Select -ExpandProperty Name<br>        $PermissionLevels = ($PermissionLevels | Where { $_ -ne "Limited Access"}) -join ","<br><br>        If($PermissionLevels.Length -eq 0) {<br>            Continue<br>        }<br><br>        If($PermissionType -eq "SharePointGroup") {<br>            $GroupMembers = Get-PnPGroupMember -Identity $RoleAssignment.Member.LoginName<br><br>            If($GroupMembers.count -eq 0) {<br>                Continue<br>            }<br><br>            $GroupUsers = ($GroupMembers | Select -ExpandProperty Title) -join "; "<br><br>            $Permissions = New-Object PSObject<br>            $Permissions | Add-Member NoteProperty Object($ObjectType)<br>            $Permissions | Add-Member NoteProperty Title($ObjectTitle)<br>            $Permissions | Add-Member NoteProperty URL($ObjectURL)<br>            $Permissions | Add-Member NoteProperty HasUniquePermissions($HasUniquePermissions)<br>            $Permissions | Add-Member NoteProperty Users($GroupUsers)<br>            $Permissions | Add-Member NoteProperty Email($RoleAssignment.Member.Email)<br>            $Permissions | Add-Member NoteProperty Type($PermissionType)<br>            $Permissions | Add-Member NoteProperty Permissions($PermissionLevels)<br>            $Permissions | Add-Member NoteProperty GrantedThrough("SharePoint Group: $($RoleAssignment.Member.LoginName)")<br><br>            $PermissionCollection += $Permissions<br>        }<br>        Else {<br>            $Permissions = New-Object PSObject<br>            $Permissions | Add-Member NoteProperty Object($ObjectType)<br>            $Permissions | Add-Member NoteProperty Title($ObjectTitle)<br>            $Permissions | Add-Member NoteProperty URL($ObjectURL)<br>            $Permissions | Add-Member NoteProperty HasUniquePermissions($HasUniquePermissions)<br>            $Permissions | Add-Member NoteProperty Users($RoleAssignment.Member.Title)<br>            $Permissions | Add-Member NoteProperty Email($RoleAssignment.Member.Email)<br>            $Permissions | Add-Member NoteProperty Type($PermissionType)<br>            $Permissions | Add-Member NoteProperty Permissions($PermissionLevels)<br>            $Permissions | Add-Member NoteProperty GrantedThrough("Direct Permissions")<br><br>            $PermissionCollection += $Permissions<br>        }<br>    }<br><br>    $PermissionCollection | Export-CSV $ReportFile -NoTypeInformation -Append<br>}<br><br>Function Generate-LibraryPermissionsReport() {<br>    [cmdletbinding()]<br><br>    Param (<br>        [Parameter(Mandatory=$false)]<br>        [String] $SiteURL,<br><br>        [Parameter(Mandatory=$false)]<br>        [String] $ListName,<br><br>        [Parameter(Mandatory=$false)]<br>        [String] $ReportFile,<br><br>        [Parameter(Mandatory=$false)]<br>        [switch] $ScanItemLevel,<br><br>        [Parameter(Mandatory=$false)]<br>        [switch] $IncludeInheritedPermissions<br>    )<br><br>    Try {<br>        Function Get-PnPListItemsPermission([Microsoft.SharePoint.Client.List]$List) {<br>            Write-host -f Yellow "`t `t Getting Permissions of List Items in the List:"$List.Title<br><br>            $ListItems = Get-PnPListItem -List $List -PageSize 500<br>            $ItemCounter = 0<br><br>            ForEach($ListItem in $ListItems) {<br>                If($IncludeInheritedPermissions) {<br>                    Get-LibraryPermissions -Object $ListItem<br>                }<br>                Else {<br>                    $HasUniquePermissions = Get-PnPProperty -ClientObject $ListItem -Property HasUniqueRoleAssignments<br><br>                    If($HasUniquePermissions -eq $True) {<br>                        Get-LibraryPermissions -Object $ListItem<br>                    }<br>                }<br><br>                $ItemCounter++<br><br>                Write-Progress -PercentComplete ($ItemCounter / ($List.ItemCount) * 100) `<br>                    -Activity "Processing Items $ItemCounter of $($List.ItemCount)" `<br>                    -Status "Searching Unique Permissions in List Items of '$($List.Title)'"<br>            }<br>        }<br><br>        $List = Get-PnpList -Identity $ListName -Includes RoleAssignments<br><br>        Write-host -f Yellow "Getting Permissions of the List '$ListName'..."<br><br>        Get-LibraryPermissions -Object $List<br><br>        If($ScanItemLevel) {<br>            Get-PnPListItemsPermission -List $List<br>        }<br><br>        Write-host -f Green "`t List Permission Report Generated Successfully!"<br>    }<br>    Catch {<br>        write-host -f Red "Error Generating List Permission Report!"<br>        $_.Exception.Message<br>    }<br>}<br><br>Connect-PnPOnline -URL $SiteURL -ClientId "Provide your client ID" -Interactive<br><br>$Web = Get-PnPWeb<br><br>Generate-LibraryPermissionsReport -SiteURL $SiteURL -ListName $ListName -ReportFile $ReportFile -ScanItemLevel</pre>



<h3 class="wp-block-heading" id="update-the-script-variables">Update the Script Variables</h3>



<p>Before you run the script, update these values with information from your SharePoint environment.</p>



<pre class="wp-block-preformatted">$SiteURL="https://.sharepoint.com/sites/RetailManagementSite"<br>$ListName = "Employee Satisfaction Survey PDF Files"<br>$ReportFile="D:\Temp Folder\Employee Satisfaction Survey PDF Files.csv"</pre>



<ul class="wp-block-list">
<li><strong>$SiteURL</strong>: Enter the URL of the <a href="https://www.spguides.com/create-sharepoint-site-using-power-apps-power-automate/" target="_blank" rel="noreferrer noopener">SharePoint Online site</a> that contains the target library.</li>



<li><strong>$ListName</strong>: Enter the display name of the <a href="https://www.spguides.com/create-document-library-sharepoint-pnp-powershell/" target="_blank" rel="noreferrer noopener">SharePoint document library</a> or list.</li>



<li><strong>$ReportFile</strong>: Enter the complete local path and file name for the CSV report.</li>
</ul>



<p>For example:</p>



<pre class="wp-block-preformatted">$SiteURL = "https://contoso.sharepoint.com/sites/HR"<br>$ListName = "Employee Documents"<br>$ReportFile = "C:\Reports\SharePointFolderPermissions.csv"</pre>



<p>If the report file already exists at the selected location, the script removes it before generating a new report.</p>



<pre class="wp-block-preformatted">If (Test-Path $ReportFile) {<br>    Remove-Item $ReportFile<br>}</pre>



<h3 class="wp-block-heading" id="add-your-microsoft-entra-id-client-id">Add Your Microsoft Entra ID Client ID</h3>



<p>Locate the following line in the script:</p>



<pre class="wp-block-preformatted">Connect-PnPOnline -URL $SiteURL -ClientId "Provide your client ID" -Interactive</pre>



<p>Replace <code>"Provide your client ID"</code> with the application or client ID from your Microsoft Entra ID app registration.</p>



<pre class="wp-block-preformatted">Connect-PnPOnline -URL $SiteURL -ClientId "YOUR-CLIENT-ID" -Interactive</pre>



<p>The <code>-Interactive</code> parameter opens the Microsoft sign-in window. Sign in with an account that has permission to access the SharePoint site and inspect the library permissions.</p>



<h3 class="wp-block-heading" id="run-the-sharepoint-permission-report-script">Run the SharePoint Permission Report Script</h3>



<p>Save the script as a <code>.ps1</code> file, such as:</p>



<pre class="wp-block-preformatted">Get-SharePoint-Folder-Permissions.ps1</pre>



<p>Open PowerShell, navigate to the folder where you saved the script, and run it.</p>



<p>The script connects to your <a href="https://www.spguides.com/check-if-sharepoint-site-already-exists-in-power-automate/" target="_blank" rel="noreferrer noopener">SharePoint Online site</a>, retrieves permissions from the selected library, scans files and folders, and exports the details to the configured CSV file.</p>



<p>When the script completes successfully, you will see the following message:</p>



<pre class="wp-block-preformatted">List Permission Report Generated Successfully!</pre>



<p>Open the generated CSV file in Microsoft Excel to review the <a href="https://www.spguides.com/build-a-sharepoint-folder-tree-view-using-spfx/" target="_blank" rel="noreferrer noopener">SharePoint folder permissions</a> and file permissions.</p>



<h2 class="wp-block-heading" id="understanding-the-permission-report">Understanding the Permission Report</h2>



<p>The exported CSV report provides a detailed view of permission assignments across the selected SharePoint document library.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://www.spguides.com/wp-content/uploads/2025/04/get-sharepoint-library-file-permissions-into-an-excel-1536x629.jpg" alt="Get SharePoint Folder Permissions Using PnP PowerShell" title="get sharepoint library file permissions into an excel"></figure></div>


<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>Column</th><th>Description</th></tr></thead><tbody><tr><td>Object</td><td>Identifies whether the entry is a library, folder, file, or list item</td></tr><tr><td>Title</td><td>Shows the name of the SharePoint object</td></tr><tr><td>URL</td><td>Provides a direct link to the folder, file, list item, or library</td></tr><tr><td>HasUniquePermissions</td><td>Shows whether the object has unique permissions instead of inheriting permissions</td></tr><tr><td>Users</td><td>Lists the user name or members of the assigned SharePoint group</td></tr><tr><td>Email</td><td>Shows the email address associated with the assigned user or group</td></tr><tr><td>Type</td><td>Identifies the principal type, such as a SharePoint group or user</td></tr><tr><td>Permissions</td><td>Displays assigned permission levels, such as Read, Edit, or Full Control</td></tr><tr><td>GrantedThrough</td><td>Shows whether access was assigned directly or through a SharePoint group</td></tr></tbody></table></figure>



<p>This report is particularly useful for identifying folders and files with broken permission inheritance. You can also use it to detect unexpected direct permissions, external access, or users who have access through SharePoint groups.</p>



<h2 class="wp-block-heading" id="how-the-get-librarypermissions-function-works">How the Get-LibraryPermissions Function Works</h2>



<p>The <code>Get-LibraryPermissions</code> function is responsible for retrieving permission information for a SharePoint object.</p>



<p>It works with the following SharePoint object types:</p>



<ul class="wp-block-list">
<li>SharePoint list or document library</li>



<li>Folder</li>



<li>File</li>



<li>List item</li>
</ul>



<p>The function first identifies the object type and retrieves its title and URL. It then checks whether the object has unique role assignments by reading the <code>HasUniqueRoleAssignments</code> property.</p>



<pre class="wp-block-preformatted">Get-PnPProperty -ClientObject $Object -Property HasUniqueRoleAssignments, RoleAssignments</pre>



<p>If an object inherits permissions, the <code>HasUniquePermissions</code> value in the report is set to <code>False</code>. If permission inheritance has been broken and the object has its own assignments, the value is set to <code>True</code>.</p>



<p>The function then loops through each role assignment, retrieves the assigned permission levels, and excludes the <code>Limited Access</code> role from the report. This keeps the output focused on meaningful permissions such as Read, Edit, Contribute, Design, or Full Control.</p>



<p>For SharePoint groups, the script retrieves the group members and adds them to the report. For individual users, it records direct permissions and identifies them as <strong>Direct Permissions</strong> in the <code>GrantedThrough</code> column.</p>



<h2 class="wp-block-heading" id="how-the-generate-librarypermissionsreport-function">How the Generate-LibraryPermissionsReport Function Works</h2>



<p>The <code>Generate-LibraryPermissionsReport</code> function controls the overall permission-reporting process.</p>



<p>It accepts the following parameters:</p>



<pre class="wp-block-preformatted">$SiteURL<br>$ListName<br>$ReportFile<br>$ScanItemLevel<br>$IncludeInheritedPermissions</pre>



<p>The function first retrieves the selected SharePoint list or document library.</p>



<pre class="wp-block-preformatted">$List = Get-PnpList -Identity $ListName -Includes RoleAssignments</pre>



<p>It then retrieves the library-level permissions by calling the <code>Get-LibraryPermissions</code> function.</p>



<pre class="wp-block-preformatted">Get-LibraryPermissions -Object $List</pre>



<p>When you use the <code>-ScanItemLevel</code> switch, the script also scans every item in the library. This includes folders, subfolders, files, and list items.</p>



<pre class="wp-block-preformatted">Generate-LibraryPermissionsReport -SiteURL $SiteURL -ListName $ListName -ReportFile $ReportFile -ScanItemLevel</pre>



<p>For large document libraries, scanning every item can take time because the script must inspect each folder and file individually. The progress bar in PowerShell shows the number of items processed during the scan.</p>



<h3 class="wp-block-heading" id="include-inherited-permissions">Include Inherited Permissions</h3>



<p>By default, the script focuses on items with <a href="https://www.spguides.com/break-permission-inheritance-sharepoint-online-power-automate/" target="_blank" rel="noreferrer noopener">unique permissions</a> when scanning item-level permissions. This is helpful when you want to identify folders and files where inheritance has been broken.</p>



<p>To include inherited permissions in the report as well, run the command with the <code>-IncludeInheritedPermissions</code> switch.</p>



<pre class="wp-block-preformatted">Generate-LibraryPermissionsReport `<br>    -SiteURL $SiteURL `<br>    -ListName $ListName `<br>    -ReportFile $ReportFile `<br>    -ScanItemLevel `<br>    -IncludeInheritedPermissions</pre>



<p>Use this option when you need a complete SharePoint permission inventory. Keep in mind that the CSV file can become large when the document library contains many files and folders.</p>



<h2 class="wp-block-heading" id="access-denied-error-in-pnp-powershell">Access Denied Error in PnP PowerShell</h2>



<p>While running the script, you may encounter the following error:</p>



<pre class="wp-block-preformatted">Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))</pre>



<p>You can see the exact error message in the screenshot below.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://www.spguides.com/wp-content/uploads/2025/04/Access-is-denied.-Exception-from-HRESULT-0x80070005-E_ACCESSDENIED.jpg" alt="Access Denied Error in PnP PowerShell" title="Access is denied. Exception from HRESULT 0x80070005 E ACCESSDENIED"></figure></div>


<p>This error usually occurs when the account used to connect through <code>Connect-PnPOnline</code> does not have sufficient access to read permissions from the SharePoint site, document library, folders, or files.</p>



<p>To resolve the issue, sign in with an account that has the required permissions. In many tenant-wide administrative scenarios, assigning the <strong>SharePoint Administrator</strong> role to the account can resolve the error.</p>



<p>You can assign the SharePoint Administrator role from the Microsoft 365 admin center:</p>



<ol class="wp-block-list">
<li>Open the Microsoft 365 admin center.</li>



<li>Select <strong>Users</strong> and then <strong>Active users</strong>.</li>



<li>Choose the user account.</li>



<li>Open the <strong>Roles</strong> section.</li>



<li>Select <strong>Admin center access</strong>.</li>



<li>Assign the <strong>SharePoint Administrator</strong> role.</li>



<li>Save the changes.</li>
</ol>



<p>Here is a screenshot for your reference.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img decoding="async" src="https://www.spguides.com/wp-content/uploads/2025/04/Access-is-denied-while-trying-to-get-library-permissions-using-pnp-powershell.jpg" alt="PnP PowerShell Get SharePoint Folder Permissions" title="Access is denied while trying to get library permissions using pnp powershell"></figure></div>


<p>After the role assignment has taken effect, run the PnP PowerShell script again.</p>



<h2 class="wp-block-heading" id="important-notes">Important Notes</h2>



<ul class="wp-block-list">
<li>The account running the script must have access to the site and the objects being scanned.</li>



<li>The script can retrieve permissions for document libraries as well as SharePoint lists.</li>



<li>Use <code>-ScanItemLevel</code> to include folders, subfolders, files, and list items.</li>



<li>Use <code>-IncludeInheritedPermissions</code> when you need both unique and inherited permission assignments.</li>



<li>The report excludes <code>Limited Access</code> permissions to make the output easier to review.</li>



<li>For very large libraries, run the report during off-peak hours and allow enough time for the scan to complete.</li>



<li>Review the CSV file in Excel and filter the <code>HasUniquePermissions</code> column to quickly find files and folders with broken permission inheritance.</li>
</ul>



<h2 class="wp-block-heading" id="conclusion">Conclusion</h2>



<p>Using PnP PowerShell is an efficient way to audit SharePoint folder permissions, file permissions, and item-level permissions in a SharePoint Online document library. The script gives you an exportable CSV report that clearly shows users, groups, permission levels, direct assignments, inherited access, and unique permissions.</p>



<p>This approach is useful for SharePoint permission audits, compliance reviews, security assessments, migration planning, and routine administration of document libraries with complex folder structures.</p>



<p>You may also like the following tutorials:</p>



<ul class="wp-block-list">
<li><a href="https://www.spguides.com/add-5000-items-sharepoint-list-pnp-powershell/">Add More Than 5000 Items to a SharePoint Online List Using PnP PowerShell</a></li>



<li><a href="https://www.spguides.com/create-document-library-sharepoint-pnp-powershell/">Create Document Library in SharePoint Using PnP PowerShell (With Multiple Folders)</a></li>



<li><a href="https://www.spguides.com/remove-all-items-from-a-sharepoint-online-list-using-pnp-powershell/">Delete All Items from a SharePoint List Using PnP PowerShell</a></li>



<li><a href="https://www.spguides.com/enable-sensitivity-labels-sharepoint-m365-groups-powershell/">Enable Sensitivity Labels For Microsoft 365 Groups &amp; SharePoint Sites Using PowerShell</a></li>
</ul>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to Create a Report in Power BI Desktop [With Real Scenarios]</title>
		<link>https://www.spguides.com/create-a-report-in-power-bi-desktop/</link>
					<comments>https://www.spguides.com/create-a-report-in-power-bi-desktop/#respond</comments>
		
		<dc:creator><![CDATA[Bijay Kumar]]></dc:creator>
		<pubDate>Thu, 27 Aug 2026 06:16:02 +0000</pubDate>
				<category><![CDATA[Power BI]]></category>
		<category><![CDATA[Create a Report in Power BI Desktop]]></category>
		<guid isPermaLink="false">https://www.sharepointsky.com/?p=13340</guid>

					<description><![CDATA[A sales manager often starts with a familiar problem: several Excel files, a SharePoint list, and no quick way to see monthly sales, top products, or low-performing regions. I have worked with teams that spent hours combining files and updating summary tables before every review meeting. That process changes quickly when you create a report ... <a title="How to Create a Report in Power BI Desktop [With Real Scenarios]" class="read-more" href="https://www.spguides.com/create-a-report-in-power-bi-desktop/" aria-label="Read more about How to Create a Report in Power BI Desktop [With Real Scenarios]">read more...</a>]]></description>
										<content:encoded><![CDATA[
<p>A sales manager often starts with a familiar problem: several Excel files, a <a href="https://www.spguides.com/filter-sharepoint-list-items-spfx/">SharePoint list</a>, and no quick way to see monthly sales, top products, or low-performing regions. I have worked with teams that spent hours combining files and updating summary tables before every review meeting.</p>



<p>That process changes quickly when you create a report in <strong>Power BI Desktop</strong>. You can connect to your Excel or SharePoint data, clean it once, <a href="https://www.spguides.com/power-bi-card/">build interactive visuals</a>, and give managers a report they can filter themselves.</p>



<p>This step-by-step <strong>Power BI tutorial</strong> uses a retail sales dashboard example and covers the full process, from loading data through publishing and sharing.</p>



<h2 class="wp-block-heading">Before You Create a Report in Power BI Desktop</h2>



<p>Before you create a report in <strong>Power BI Desktop</strong>, prepare data that supports analysis. For this example, assume a mid-sized retail company tracks sales in an Excel workbook or a SharePoint Online list.</p>



<p>The sales data should include fields such as:</p>



<ul class="wp-block-list">
<li>Order Date</li>



<li>Order ID</li>



<li>Product Name</li>



<li>Product Category</li>



<li>Region</li>



<li>Salesperson</li>



<li>Quantity</li>



<li>Sales Amount</li>



<li>Cost Amount</li>
</ul>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-dominant-color="dbdcdc" data-has-transparency="false" style="--dominant-color: #dbdcdc;" loading="lazy" decoding="async" width="1024" height="309" sizes="(max-width: 1024px) 100vw, 1024px" src="https://www.spguides.com/wp-content/uploads/2026/08/How-to-create-a-sales-dashboard-in-Power-BI-from-Excel-1024x309.avif" alt="How to create a sales dashboard in Power BI from Excel" class="wp-image-137182 not-transparent" title="How to create a sales dashboard in Power BI from Excel" srcset="https://www.spguides.com/wp-content/uploads/2026/08/How-to-create-a-sales-dashboard-in-Power-BI-from-Excel-1024x309.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/08/How-to-create-a-sales-dashboard-in-Power-BI-from-Excel-300x91.avif 300w, https://www.spguides.com/wp-content/uploads/2026/08/How-to-create-a-sales-dashboard-in-Power-BI-from-Excel-768x232.avif 768w, https://www.spguides.com/wp-content/uploads/2026/08/How-to-create-a-sales-dashboard-in-Power-BI-from-Excel-1536x464.avif 1536w, https://www.spguides.com/wp-content/uploads/2026/08/How-to-create-a-sales-dashboard-in-Power-BI-from-Excel.avif 1564w" /></figure></div>


<p>Each row should represent one transaction. This structure matters because <strong>Power BI Desktop</strong> works best when every record has a clear business meaning. Avoid merged cells, blank header rows, totals inside the source data, and inconsistent date formats.</p>



<p>For Excel, convert your range into an <strong>Excel Table</strong> before connecting. Click inside the data, select <strong>Insert</strong> &gt; <strong>Table</strong>, and give it a meaningful name such as Sales. A proper table expands when new rows arrive, which makes refreshes far more reliable.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-dominant-color="c7c8c8" data-has-transparency="false" style="--dominant-color: #c7c8c8;" loading="lazy" decoding="async" width="1024" height="472" sizes="(max-width: 1024px) 100vw, 1024px" src="https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-Desktop-retail-sales-dashboard-tutorial-1024x472.avif" alt="Power BI Desktop retail sales dashboard tutorial" class="wp-image-137181 not-transparent" title="Power BI Desktop retail sales dashboard tutorial" srcset="https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-Desktop-retail-sales-dashboard-tutorial-1024x472.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-Desktop-retail-sales-dashboard-tutorial-300x138.avif 300w, https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-Desktop-retail-sales-dashboard-tutorial-768x354.avif 768w, https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-Desktop-retail-sales-dashboard-tutorial-1536x707.avif 1536w, https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-Desktop-retail-sales-dashboard-tutorial.avif 1572w" /></figure></div>


<p>For SharePoint, keep column names simple and clear. Use a <strong>Number</strong> column for quantity, <strong>Currency</strong> for sales values, and <strong>Date and Time</strong> for order dates. If you are starting with SharePoint lists, review this guide on how to <a href="https://www.spguides.com/sharepoint-list/">create and manage a SharePoint list</a>.</p>



<blockquote class="wp-block-quote has-contrast-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p class="has-base-2-background-color has-background"><strong>Pro Tip:</strong> I have found that most report issues begin in the source data, not the visual. Spend a few minutes cleaning headers and data types before loading anything into Power BI.</p>
</blockquote>



<h2 class="wp-block-heading">Create a Report in Power BI Desktop From Excel</h2>



<p>Excel remains a common starting point for small business reporting. It works well when a team stores transactional data in a controlled workbook and updates it regularly.</p>



<h3 class="wp-block-heading">Connect to the Excel workbook</h3>



<p>Open <strong>Power BI Desktop</strong> and select <strong>Get data</strong> from the <strong>Home</strong> tab.</p>



<ol class="wp-block-list">
<li>Select <strong>Excel workbook</strong>.</li>



<li>Browse to your workbook and select <strong>Open</strong>.</li>



<li>In the <strong>Navigator</strong> window, select the <code>Sales</code> table.</li>



<li>Choose <strong>Transform Data</strong> instead of loading immediately.</li>
</ol>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-dominant-color="f4f4f4" data-has-transparency="false" style="--dominant-color: #f4f4f4;" loading="lazy" decoding="async" width="1024" height="502" sizes="(max-width: 1024px) 100vw, 1024px" src="https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-sales-dashboard-using-Excel-data-step-by-step-1024x502.avif" alt="Power BI sales dashboard using Excel data step by step" class="wp-image-137183 not-transparent" title="Power BI sales dashboard using Excel data step by step" srcset="https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-sales-dashboard-using-Excel-data-step-by-step-1024x502.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-sales-dashboard-using-Excel-data-step-by-step-300x147.avif 300w, https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-sales-dashboard-using-Excel-data-step-by-step-768x376.avif 768w, https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-sales-dashboard-using-Excel-data-step-by-step-1536x753.avif 1536w, https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-sales-dashboard-using-Excel-data-step-by-step.avif 1761w" /></figure></div>


<p>Choose <strong>Transform Data</strong> because it opens <strong>Power Query Editor</strong>. Power Query is the data preparation area in Power BI Desktop. You use it to clean, rename, filter, split, and format columns before the data enters your report.</p>



<p>If your sales file contains extra columns such as Notes, Internal Comments, or Created By, remove them now. Smaller tables refresh faster and make the <strong>data model</strong> easier to understand.</p>



<h3 class="wp-block-heading">Clean the sales data in Power Query</h3>



<p>Inside <strong>Power Query Editor</strong>, check each column’s data type. Power BI may guess correctly, but do not rely on that guess.</p>



<p>For the retail sales example, use these data types:</p>



<ul class="wp-block-list">
<li><strong>Order Date:</strong> Date</li>



<li><strong>Order ID:</strong> Text</li>



<li><strong>Product Name:</strong> Text</li>



<li><strong>Region:</strong> Text</li>



<li><strong>Quantity:</strong> Whole Number</li>



<li><strong>Sales Amount:</strong> Fixed Decimal Number or Currency</li>



<li><strong>Cost Amount:</strong> Fixed Decimal Number or Currency</li>
</ul>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-dominant-color="f2f3f3" data-has-transparency="false" style="--dominant-color: #f2f3f3;" loading="lazy" decoding="async" width="1024" height="324" sizes="(max-width: 1024px) 100vw, 1024px" src="https://www.spguides.com/wp-content/uploads/2026/08/Create-Power-BI-report-from-Excel-sales-data-1024x324.avif" alt="Create Power BI report from Excel sales data" class="wp-image-137184 not-transparent" title="Create Power BI report from Excel sales data" srcset="https://www.spguides.com/wp-content/uploads/2026/08/Create-Power-BI-report-from-Excel-sales-data-1024x324.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/08/Create-Power-BI-report-from-Excel-sales-data-300x95.avif 300w, https://www.spguides.com/wp-content/uploads/2026/08/Create-Power-BI-report-from-Excel-sales-data-768x243.avif 768w, https://www.spguides.com/wp-content/uploads/2026/08/Create-Power-BI-report-from-Excel-sales-data-1536x485.avif 1536w, https://www.spguides.com/wp-content/uploads/2026/08/Create-Power-BI-report-from-Excel-sales-data-2048x647.avif 2048w" /></figure></div>


<p>To change a type, select the column, choose the type icon beside the column name, and select the correct option. This step is important because Power BI calculations depend on data types. A sales amount stored as text will not calculate correctly in a measure.</p>



<p>Rename unclear columns while you are here. For example, rename <code>Amt</code> to Sales Amount and <code>ProdNm</code> to Product Name. Clear names make report development easier, especially when several people maintain the file.</p>



<p>If you need to add or modify fields during data preparation, see these practical <a href="https://www.spguides.com/power-query-add-column/">Power Query column examples</a>.</p>



<p>When the table looks clean, click <strong>Close &amp; Apply</strong>. Power BI loads the data into the report.</p>



<h2 class="wp-block-heading">Create a Report in Power BI Desktop From SharePoint</h2>



<p>A <strong>SharePoint Online list</strong> works well when multiple people enter or update operational data. For example, a sales operations team may maintain product, vendor, inventory, or order status information in SharePoint.</p>



<h3 class="wp-block-heading">Connect to a SharePoint Online list</h3>



<p>In <strong>Power BI Desktop</strong>, select <strong>Home</strong> &gt; <strong>Get data</strong> &gt; <strong>More</strong>.</p>



<ol class="wp-block-list">
<li>Select <strong>Online Services</strong>.</li>



<li>Choose <strong>SharePoint Online List</strong>.</li>



<li>Click <strong>Connect</strong>.</li>



<li>Enter the SharePoint site URL, not the full list URL.</li>



<li>Sign in with an account that has access to the site.</li>



<li>Select the required list in the <strong>Navigator</strong> window.</li>



<li>Click <strong>Transform Data</strong>.</li>
</ol>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-dominant-color="f7f7f7" data-has-transparency="false" style="--dominant-color: #f7f7f7;" loading="lazy" decoding="async" width="1024" height="236" sizes="(max-width: 1024px) 100vw, 1024px" src="https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-retail-sales-dashboard-for-beginners-1024x236.avif" alt="Power BI retail sales dashboard for beginners" class="wp-image-137185 not-transparent" title="Power BI retail sales dashboard for beginners" srcset="https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-retail-sales-dashboard-for-beginners-1024x236.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-retail-sales-dashboard-for-beginners-300x69.avif 300w, https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-retail-sales-dashboard-for-beginners-768x177.avif 768w, https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-retail-sales-dashboard-for-beginners-1536x354.avif 1536w, https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-retail-sales-dashboard-for-beginners-2048x472.avif 2048w" /></figure></div>


<p>Power BI displays available lists from that SharePoint site. Select only the lists needed for the report. For a sales dashboard, you may choose a <code>Sales Orders</code> list, a <code>Products</code> list, and a <code>Regions</code> list.</p>



<h3 class="wp-block-heading">Expand lookup and person columns</h3>



<p>SharePoint lookup, person, and choice columns often appear as <strong>Record</strong> values in Power Query. You must expand them before building visuals.</p>



<p>Select the expand icon on the column header. Then choose only the subfields you need, such as Value, Title, DisplayName, or Email.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="eff1f1" data-has-transparency="false" style="--dominant-color: #eff1f1;" loading="lazy" decoding="async" width="872" height="438" sizes="(max-width: 872px) 100vw, 872px" src="https://www.spguides.com/wp-content/uploads/2026/08/How-to-connect-Excel-data-to-Power-BI-Desktop.avif" alt="How to connect Excel data to Power BI Desktop" class="wp-image-137186 not-transparent" title="How to connect Excel data to Power BI Desktop" srcset="https://www.spguides.com/wp-content/uploads/2026/08/How-to-connect-Excel-data-to-Power-BI-Desktop.avif 872w, https://www.spguides.com/wp-content/uploads/2026/08/How-to-connect-Excel-data-to-Power-BI-Desktop-300x151.avif 300w, https://www.spguides.com/wp-content/uploads/2026/08/How-to-connect-Excel-data-to-Power-BI-Desktop-768x386.avif 768w" /></figure></div>


<p>For example, if the Product field is a SharePoint lookup column, expand it and select the product name. If you leave it as a record, your chart cannot display a readable product label.</p>



<p>You should also remove SharePoint system fields that do not help the report, such as Version, Attachments, Content Type, or Edit links. Retain fields only when users need them for reporting or filtering.</p>



<h3 class="wp-block-heading">Validate the SharePoint data types</h3>



<p>SharePoint connectors often bring fields into Power Query with the <strong>Any</strong> type. Correct them before loading.</p>



<p>Set:</p>



<ul class="wp-block-list">
<li>Sales dates to <strong>Date</strong></li>



<li>Quantity to <strong>Whole Number</strong></li>



<li>Sales values to <strong>Currency</strong></li>



<li>Product and region names to <strong>Text</strong></li>
</ul>



<p>If dates look wrong after loading, use this guide to <a href="https://www.spguides.com/power-bi-convert-yyyymmdd-to-date/">convert yyyymmdd values to proper dates in Power BI</a>.</p>



<h2 class="wp-block-heading">Build a Simple Data Model</h2>



<p>A <strong>data model</strong> connects related tables so Power BI can filter and calculate across them. For a beginner report built from one Excel table, Power BI may only have one table. That is fine for an initial dashboard.</p>



<p>For a more reliable sales dashboard, split your model into a <strong>fact table</strong> and supporting <strong>dimension tables</strong>.</p>



<ul class="wp-block-list">
<li><strong>Sales:</strong> The fact table that contains transactions and numeric values.</li>



<li><strong>Product:</strong> A dimension table with product name, category, and brand.</li>



<li><strong>Region:</strong> A dimension table with region and territory details.</li>



<li><strong>Date:</strong> A dimension table that supports monthly, quarterly, and yearly analysis.</li>
</ul>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f1f1f1" data-has-transparency="false" style="--dominant-color: #f1f1f1;" loading="lazy" decoding="async" width="296" height="242" sizes="(max-width: 296px) 100vw, 296px" src="https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-sales-dashboard-with-KPI-cards-and-slicers.avif" alt="Power BI sales dashboard with KPI cards and slicers" class="wp-image-137187 not-transparent" title="Power BI sales dashboard with KPI cards and slicers"></figure></div>


<p>Open <strong>Model view</strong> from the left navigation. Create one-to-many relationships from each dimension to the Sales table. For example, Product[Product ID] should connect to Sales[Product ID].</p>



<p>A <strong>one-to-many relationship</strong> means one product can appear in many sales transactions. This design is called a <strong>star schema</strong> because the Sales table sits in the middle and the lookup tables surround it.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="ececec" data-has-transparency="false" style="--dominant-color: #ececec;" loading="lazy" decoding="async" width="872" height="668" sizes="(max-width: 872px) 100vw, 872px" src="https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-sales-dashboard-with-monthly-sales-trend.avif" alt="Power BI sales dashboard with monthly sales trend" class="wp-image-137188 not-transparent" title="Power BI sales dashboard with monthly sales trend" srcset="https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-sales-dashboard-with-monthly-sales-trend.avif 872w, https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-sales-dashboard-with-monthly-sales-trend-300x230.avif 300w, https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-sales-dashboard-with-monthly-sales-trend-768x588.avif 768w" /></figure></div>


<p>Avoid linking every table to every other table. That creates confusing filter paths and incorrect totals. If you notice blank values in visuals, inspect your keys and relationships using this guide on <a href="https://www.spguides.com/filter-blank-value-in-power-bi/">filtering blank values in Power BI</a>.</p>



<h2 class="wp-block-heading">Add a Date Table for Time Analysis</h2>



<p>A proper <strong>Date table</strong> helps you compare months, quarters, and years. It also supports time intelligence calculations such as last year’s sales.</p>



<p>Create a new table by selecting <strong>Modeling</strong> &gt; <strong>New table</strong>. Add this DAX formula:</p>



<pre class="wp-block-code"><code>Date =
CALENDAR(
    MIN(Sales&#91;Order Date]),
    MAX(Sales&#91;Order Date])
)</code></pre>



<p>This formula creates one row for every date between the earliest and latest order date in your Sales table.</p>



<p>Next, add useful date columns:</p>



<pre class="wp-block-code"><code>Year = YEAR('Date'&#91;Date])</code></pre>



<pre class="wp-block-code"><code>Month Number = MONTH('Date'&#91;Date])</code></pre>



<pre class="wp-block-code"><code>Month Name = FORMAT('Date'&#91;Date], "MMMM")</code></pre>



<pre class="wp-block-code"><code>Year Month = FORMAT('Date'&#91;Date], "YYYY MMM")</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f0f1f0" data-has-transparency="false" style="--dominant-color: #f0f1f0;" loading="lazy" decoding="async" width="662" height="758" sizes="(max-width: 662px) 100vw, 662px" src="https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-sales-dashboard-using-DAX-measures.avif" alt="Power BI sales dashboard using DAX measures" class="wp-image-137189 not-transparent" title="Power BI sales dashboard using DAX measures" srcset="https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-sales-dashboard-using-DAX-measures.avif 662w, https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-sales-dashboard-using-DAX-measures-262x300.avif 262w" /></figure></div>


<p>Then select the <code>Date</code> table, choose <strong>Table tools</strong>, and click <strong>Mark as date table</strong>. Select the <code>Date</code> column when Power BI asks for the date field.</p>



<p>Marking the table tells Power BI that this table controls calendar logic. Create a relationship from Date[Date] to Sales[Order Date].</p>



<p>For a deeper walkthrough, read <a href="https://www.spguides.com/power-bi-dax-calendar-function/">how to create a calendar table using the DAX CALENDAR function</a>.</p>



<h2 class="wp-block-heading">Create DAX Measures for Your KPIs</h2>



<p>A <strong>measure</strong> is a calculation that responds to filters, slicers, and visuals. Measures work better than calculated columns for totals, averages, and KPIs because Power BI calculates them when users interact with the report.</p>



<p>Right-click the Sales table and select <strong>New measure</strong>.</p>



<p>Create the main sales measure:</p>



<pre class="wp-block-code"><code>Total Sales =
SUM(Sales&#91;Sales Amount])</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f4f4f4" data-has-transparency="false" style="--dominant-color: #f4f4f4;" loading="lazy" decoding="async" width="596" height="231" sizes="(max-width: 596px) 100vw, 596px" src="https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-sales-dashboard-with-sales-and-profit-analysis.avif" alt="Power BI sales dashboard with sales and profit analysis" class="wp-image-137190 not-transparent" title="Power BI sales dashboard with sales and profit analysis" srcset="https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-sales-dashboard-with-sales-and-profit-analysis.avif 596w, https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-sales-dashboard-with-sales-and-profit-analysis-300x116.avif 300w" /></figure></div>


<p>This measure adds the Sales Amount values in the current filter context. If a user chooses the West region in a slicer, it returns only West region sales.</p>



<p>Create a quantity measure:</p>



<pre class="wp-block-code"><code>Total Quantity =
SUM(Sales&#91;Quantity])</code></pre>



<p>Create a profit measure:</p>



<pre class="wp-block-code"><code>Total Profit =
SUM(Sales&#91;Sales Amount]) - SUM(Sales&#91;Cost Amount])</code></pre>



<p>Create last year sales:</p>



<pre class="wp-block-code"><code>Sales LY =
CALCULATE(
    &#91;Total Sales],
    SAMEPERIODLASTYEAR('Date'&#91;Date])
)</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f4f5f5" data-has-transparency="false" style="--dominant-color: #f4f5f5;" loading="lazy" decoding="async" width="602" height="786" sizes="(max-width: 602px) 100vw, 602px" src="https://www.spguides.com/wp-content/uploads/2026/08/How-to-create-sales-KPIs-in-Power-BI.avif" alt="How to create sales KPIs in Power BI" class="wp-image-137191 not-transparent" title="How to create sales KPIs in Power BI" srcset="https://www.spguides.com/wp-content/uploads/2026/08/How-to-create-sales-KPIs-in-Power-BI.avif 602w, https://www.spguides.com/wp-content/uploads/2026/08/How-to-create-sales-KPIs-in-Power-BI-230x300.avif 230w" /></figure></div>


<p>The <strong>CALCULATE</strong> function changes the filter context. <strong>SAMEPERIODLASTYEAR</strong> shifts the selected date range back one year. If the visual shows March 2026 sales, this measure returns March 2025 sales.</p>



<p>Create year-over-year growth:</p>



<pre class="wp-block-code"><code>Sales YoY % =
DIVIDE(
    &#91;Total Sales] - &#91;Sales LY],
    &#91;Sales LY]
)</code></pre>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="f5f5f6" data-has-transparency="false" style="--dominant-color: #f5f5f6;" loading="lazy" decoding="async" width="589" height="292" sizes="(max-width: 589px) 100vw, 589px" src="https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-Total-Sales-Total-Profit-DAX-measures.avif" alt="Power BI Total Sales Total Profit DAX measures" class="wp-image-137192 not-transparent" title="Power BI Total Sales Total Profit DAX measures" srcset="https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-Total-Sales-Total-Profit-DAX-measures.avif 589w, https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-Total-Sales-Total-Profit-DAX-measures-300x149.avif 300w" /></figure></div>


<p>The <strong>DIVIDE</strong> function avoids errors when last year’s sales equal zero. Format this measure as a percentage.</p>



<blockquote class="wp-block-quote has-contrast-2-background-color has-background is-layout-flow wp-block-quote-is-layout-flow">
<p class="has-base-2-background-color has-background"><strong>Pro Tip:</strong> In my experience, a report becomes easier to maintain when all core calculations are measures. Avoid creating a new calculated column just to show a total on a card.</p>
</blockquote>



<h2 class="wp-block-heading">Design the Sales Dashboard</h2>



<p>Now move to <strong>Report view</strong>. A strong report answers a business question without making users hunt for data.</p>



<p>For this example, create one page called <strong>Sales Overview</strong>.</p>



<h3 class="wp-block-heading">Add KPI cards</h3>



<p>Use <strong>Card</strong> visuals at the top of the report. Add:</p>



<ul class="wp-block-list">
<li>Total Sales</li>



<li>Total Profit</li>



<li>Total Quantity</li>



<li>Sales YoY %</li>
</ul>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-dominant-color="f8f8f8" data-has-transparency="false" style="--dominant-color: #f8f8f8;" loading="lazy" decoding="async" width="1024" height="127" sizes="(max-width: 1024px) 100vw, 1024px" src="https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-sales-dashboard-with-region-analysis-1024x127.avif" alt="Power BI sales dashboard with region analysis" class="wp-image-137193 not-transparent" title="Power BI sales dashboard with region analysis" srcset="https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-sales-dashboard-with-region-analysis-1024x127.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-sales-dashboard-with-region-analysis-300x37.avif 300w, https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-sales-dashboard-with-region-analysis-768x96.avif 768w, https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-sales-dashboard-with-region-analysis.avif 1302w" /></figure></div>


<p>Cards give managers a quick view of the numbers that matter. Keep four or fewer cards in one row to avoid clutter.</p>



<h3 class="wp-block-heading">Add a monthly sales trend</h3>



<p>Add a <strong>Line chart</strong>.</p>



<ul class="wp-block-list">
<li>X-axis: Date[Year Month]</li>



<li>Y-axis: Total Sales</li>
</ul>



<p>Sort the axis by a real date field or month number. Alphabetical month names create the wrong sequence, such as April, August, December, and February.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="fafbfc" data-has-transparency="false" style="--dominant-color: #fafbfc;" loading="lazy" decoding="async" width="526" height="278" sizes="(max-width: 526px) 100vw, 526px" src="https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-sales-dashboard-with-product-category-analysis.avif" alt="Power BI sales dashboard with product category analysis" class="wp-image-137194 not-transparent" title="Power BI sales dashboard with product category analysis" srcset="https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-sales-dashboard-with-product-category-analysis.avif 526w, https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-sales-dashboard-with-product-category-analysis-300x159.avif 300w" /></figure></div>


<p>A line chart shows movement over time. It helps a manager spot a slow month, seasonal demand, or sudden growth.</p>



<h3 class="wp-block-heading">Add sales by region and product</h3>



<p>Add a <strong>Clustered bar chart</strong>.</p>



<ul class="wp-block-list">
<li>Y-axis: Region[Region Name]</li>



<li>X-axis: Total Sales</li>
</ul>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="e8eff5" data-has-transparency="false" style="--dominant-color: #e8eff5;" loading="lazy" decoding="async" width="845" height="552" sizes="(max-width: 845px) 100vw, 845px" src="https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-dashboard-using-Excel-and-SharePoint-data.avif" alt="Power BI dashboard using Excel and SharePoint data" class="wp-image-137195 not-transparent" title="Power BI dashboard using Excel and SharePoint data" srcset="https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-dashboard-using-Excel-and-SharePoint-data.avif 845w, https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-dashboard-using-Excel-and-SharePoint-data-300x196.avif 300w, https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-dashboard-using-Excel-and-SharePoint-data-768x502.avif 768w" /></figure></div>


<p>Then add another bar chart:</p>



<ul class="wp-block-list">
<li>Y-axis: Product[Product Category]</li>



<li>X-axis: Total Sales</li>
</ul>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img data-dominant-color="eef1f4" data-has-transparency="false" style="--dominant-color: #eef1f4;" loading="lazy" decoding="async" width="830" height="761" sizes="(max-width: 830px) 100vw, 830px" src="https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-SharePoint-Online-sales-dashboard-tutorial.avif" alt="Power BI SharePoint Online sales dashboard tutorial" class="wp-image-137196 not-transparent" title="Power BI SharePoint Online sales dashboard tutorial" srcset="https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-SharePoint-Online-sales-dashboard-tutorial.avif 830w, https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-SharePoint-Online-sales-dashboard-tutorial-300x275.avif 300w, https://www.spguides.com/wp-content/uploads/2026/08/Power-BI-SharePoint-Online-sales-dashboard-tutorial-768x704.avif 768w" /></figure></div>


<p>Bar charts make comparisons easy. Use them for categories, regions, salespeople, or product lines. Sort each chart by <strong>Total Sales</strong> in descending order so the highest performer appears first.</p>



<h3 class="wp-block-heading">Add slicers for interaction</h3>



<p>A <strong>Slicer</strong> lets users filter report visuals without editing the report.</p>



<p>Add slicers for:</p>



<ul class="wp-block-list">
<li>Year</li>



<li>Region</li>



<li>Product Category</li>



<li>Salesperson</li>
</ul>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img data-dominant-color="e8f3fb" data-has-transparency="false" style="--dominant-color: #e8f3fb;" loading="lazy" decoding="async" width="1024" height="587" sizes="(max-width: 1024px) 100vw, 1024px" src="https://www.spguides.com/wp-content/uploads/2026/08/How-to-Create-a-Report-in-Power-BI-Desktop-1024x587.avif" alt="How to Create a Report in Power BI Desktop" class="wp-image-137197 not-transparent" title="How to Create a Report in Power BI Desktop" srcset="https://www.spguides.com/wp-content/uploads/2026/08/How-to-Create-a-Report-in-Power-BI-Desktop-1024x587.avif 1024w, https://www.spguides.com/wp-content/uploads/2026/08/How-to-Create-a-Report-in-Power-BI-Desktop-300x172.avif 300w, https://www.spguides.com/wp-content/uploads/2026/08/How-to-Create-a-Report-in-Power-BI-Desktop-768x440.avif 768w, https://www.spguides.com/wp-content/uploads/2026/08/How-to-Create-a-Report-in-Power-BI-Desktop.avif 1305w" /></figure></div>


<p>Use a dropdown design when your slicer has many choices. It saves space and keeps the dashboard clean. This detailed guide explains how to <a href="https://www.spguides.com/add-a-dropdown-slicer-in-power-bi/">add a dropdown slicer in Power BI</a>.</p>



<p>Make sure every visual responds to the slicers. Select a slicer, go to <strong>Format</strong> &gt; <strong>Edit interactions</strong>, and confirm that it filters the correct charts.</p>



<h2 class="wp-block-heading">Publish and Share the Report</h2>



<p>Save the file as a .pbix file first. Use a meaningful name such as <code>Retail-Sales-Dashboard.pbix</code>.</p>



<p>To publish, select <strong>Publish</strong> from the Home ribbon and sign in to your Power BI account. Choose a <strong>workspace</strong>, which is a shared area in <strong>Power BI Service</strong> where teams store reports, datasets, dashboards, and apps.</p>



<p>Choose a workspace based on the audience:</p>



<ul class="wp-block-list">
<li>Use <strong>My workspace</strong> for personal testing.</li>



<li>Use a team workspace for department reports.</li>



<li>Use a controlled workspace for production reports.</li>
</ul>



<p>After publishing, open the report in <strong>Power BI Service</strong>. Check the layout, refresh the data, and configure access for the correct users.</p>



<p>If your report uses SharePoint Online lists or files stored in SharePoint, scheduled refresh usually works without an on-premises gateway. If the Excel file sits on your laptop or a local network drive, you may need a gateway before Power BI Service can refresh it.</p>



<h2 class="wp-block-heading">Things to Consider</h2>



<ul class="wp-block-list">
<li><strong>Use a star schema:</strong> Keep transaction data in a central Sales table and connect it to dimensions such as Date, Product, and Region. This model produces more reliable filtering and calculations.</li>



<li><strong>Use measures for KPIs:</strong> Create <strong>DAX measures</strong> for sales, profit, and growth rates. Measures react to slicers, while calculated columns increase model size.</li>



<li><strong>Check data types early:</strong> Set dates, currency values, and quantities correctly in <strong>Power Query</strong> before building visuals or writing DAX.</li>



<li><strong>Avoid too many visuals:</strong> A dashboard should answer key questions quickly. Use a few meaningful cards, charts, and slicers instead of filling every empty space.</li>



<li><strong>Protect sensitive data:</strong> Use <strong>Row-Level Security</strong> when regional managers should only view their own region’s data.</li>



<li><strong>Test refresh before sharing:</strong> Refresh the report in Power BI Service after publishing. Check credentials, source paths, and list permissions before users depend on it.</li>
</ul>



<h2 class="wp-block-heading">Frequently Asked Questions</h2>


<div id="rank-math-faq" class="rank-math-block">
<div class="rank-math-list ">
<div id="faq-question-1787810365235" class="rank-math-list-item">
<h3 class="rank-math-question ">How do I create a report in Power BI Desktop from Excel?</h3>
<div class="rank-math-answer ">

<p>Open <strong>Power BI Desktop</strong>, select <strong>Get data</strong>, and choose <strong>Excel workbook</strong>. Select your Excel table, clean it in Power Query, then use visuals and measures to build the report. Save the .pbix file before publishing it.</p>

</div>
</div>
<div id="faq-question-1787810374547" class="rank-math-list-item">
<h3 class="rank-math-question ">Can Power BI Desktop connect to a SharePoint list?</h3>
<div class="rank-math-answer ">

<p>Yes. Select <strong>Get data</strong> &gt; <strong>More</strong> &gt; <strong>Online Services</strong> &gt; <strong>SharePoint Online List</strong>. Enter the SharePoint site URL, sign in, select the list, and transform the data before loading it.</p>

</div>
</div>
<div id="faq-question-1787810389942" class="rank-math-list-item">
<h3 class="rank-math-question ">Should I choose Load or Transform Data in Power BI?</h3>
<div class="rank-math-answer ">

<p>Choose <strong>Transform Data</strong> when you need to remove columns, fix data types, rename fields, or expand SharePoint lookup columns. Choose <strong>Load</strong> only when the source already has clean, report-ready data.</p>

</div>
</div>
<div id="faq-question-1787810404913" class="rank-math-list-item">
<h3 class="rank-math-question ">What is the difference between a Power BI report and dashboard?</h3>
<div class="rank-math-answer ">

<p>A <strong>report</strong> can contain multiple pages, visuals, filters, and detailed analysis. A <strong>dashboard</strong> exists in Power BI Service and combines pinned visuals into a single-page view. Most people build the report first in Power BI Desktop.</p>

</div>
</div>
<div id="faq-question-1787810414861" class="rank-math-list-item">
<h3 class="rank-math-question ">Why are my Power BI visuals showing blank values?</h3>
<div class="rank-math-answer ">

<p>Blank values often appear when source data has missing fields or relationships use mismatched keys. Check the data in Power Query, confirm relationship directions in Model view, and remove unwanted blanks through visual filters.</p>

</div>
</div>
<div id="faq-question-1787810425872" class="rank-math-list-item">
<h3 class="rank-math-question ">Can I refresh a Power BI report automatically?</h3>
<div class="rank-math-answer ">

<p>Yes. Publish the report to <strong>Power BI Service</strong>, open the semantic model settings, configure credentials, and set a refresh schedule. SharePoint Online sources are typically easier to refresh than files stored only on a local computer.</p>

</div>
</div>
</div>
</div>


<p>Report in <strong>Power BI Desktop</strong> becomes straightforward when you begin with clean Excel or SharePoint data, build a simple data model, and use measures for your business KPIs. Start with one focused sales dashboard, validate the numbers, and improve the design as your users ask better questions. I hope you found this article helpful.</p>



<p>You May Also Like</p>



<ul class="wp-block-list">
<li><a href="https://www.spguides.com/power-bi-date-slicer-only-shows-dates-with-data/">Power BI date slicer only shows dates with data</a></li>



<li><a href="https://www.spguides.com/power-bi-date-slicer-by-month/">Power BI Date Slicer by Month</a></li>



<li><a href="https://www.spguides.com/sort-slicer-by-measure-in-power-bi/">How to sort a Power BI slicer by measure</a></li>



<li><a href="https://www.spguides.com/power-bi-dax-min-date/">Power BI DAX MIN date function with examples</a></li>



<li><a href="https://www.spguides.com/power-bi-export-to-excel/">Export a Power BI report to Excel</a></li>



<li><a href="https://www.spguides.com/power-bi-free-vs-pro-vs-premium/">Power BI Free vs Pro vs Premium comparison</a></li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://www.spguides.com/create-a-report-in-power-bi-desktop/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
