Tag: powershell

  • Search for PowerBI parameters with PowerShell

    Search for PowerBI parameters with PowerShell

    Thanks for bearing with me! First blog post of 2026, lets just say I have been busy! To start us off, I have another PowerShell script I put together the end of last year, a way to search for PowerBI parameters with PowerShell.

    When running:

    • multiple workspaces
    • with multiple reports
    • all with multiple parameters

    Sometimes, it seems you need an easy solution to find where you have set parameters to specific items.

    My use case for example, I have a number of reports pointing to a specific data source. When migrating that data source I wanted to discover which reports where still attached so this script made it super simple!

    This PowerShell script, similar to a few of my others will loop through all the PowerBI workspaces you have access to, and at the same time loop through every report and parameter, logging where it finds the defined parameter as it goes.

    The parameters endpoint for every dataset makes it simple to find this information and we can simply loop through each. It will naturally log results in a custom object showing the:

    • Workspace Name
    • Report Name
    • Parameter Name
    • Current Parameter Value
    • Dataset ID

    This is one is a real simple one, just change the searchValue to the actual value of the parameter itself you want to discover, for example

    Parameter Name : Source

    Parameter Value : SQLDB1

    So in this case we search for SQLDB1

    So hopefully this will help you! It certainly saved a huge amount of time for me! code below!

    Connect-PowerBIServiceAccount
    
    # The value you are searching for
    $SearchValue = "Domain"
    
    $workspaces = Get-PowerBIWorkspace -All
    
    Write-Host "Starting Scan" -ForegroundColor Yellow
    
    
    $Results = $workspaces | ForEach-Object -Parallel {
        $Target = $using:SearchValue
        $workspace = $_
        
        try {
            # Get reports in this workspace
            $Reportlist = Get-PowerBIReport -WorkspaceId $workspace.Id
            
            foreach ($Report in $Reportlist) {
                if (-not $Report.DatasetId) { continue }
    
                # Fetch parameters for the dataset
                $Url = "https://api.powerbi.com/v1.0/myorg/groups/$($workspace.Id)/datasets/$($Report.DatasetId)/parameters"
                $resp = Invoke-PowerBIRestMethod -Url $Url -Method Get
                $params = (ConvertFrom-Json $resp).value
    
                foreach ($p in $params) {
                    if ($p.currentValue -eq $Target) {
                        
                        Write-Host "[MATCH] Workspace: $($workspace.Name)" -ForegroundColor Cyan
                        Write-Host "        Report:    $($Report.Name)" -ForegroundColor White
                        Write-Host "        Parameter: $($p.name)" -ForegroundColor Green
    
    
                        # Collect in Object
                        [PSCustomObject]@{
                            Workspace     = $workspace.Name
                            Report        = $Report.Name
                            ParameterName = $p.name
                            ParameterValue = $p.currentValue
                            DatasetId     = $Report.DatasetId
                        }
                    }
                }
            }
        } catch {
            # Skip datasets that cannot be accessed
        }
    } -ThrottleLimit 8
    
    if ($Results) {
        $Results | Out-GridView -Title "Search Results: Parameter Value '$SearchValue'"
    } else {
        Write-Host "No reports found with parameter value: $SearchValue" -ForegroundColor Red
    }
  • Prevent scheduled refresh from being disabled due to inactivity

    Prevent scheduled refresh from being disabled due to inactivity

    When you use scheduled refresh in the Power BI service, it pauses if nobody accesses the report or dataset for 60 days. That’s fine from a load perspective, but if you want your datasets to always have the latest data and be available on demand, you’ll need to stop that from happening.

    Now there are some manual approaches such as creating a Subscription on each dataset within the service UI, but if we are putting our reports out at scale that just isn’t viable. So what we can do is build a PowerShell script that will loop through each of our datasets and prevent scheduled refresh from being disabled due to inactivity.

    The best way to achieve this is to trigger activity on the dataset. I do this by querying a table and column I know exist and discarding the result.

    For the full script, scroll down, but I’ll cover what it does and what bits you can change.

    When building the script, permissions can make it harder to directly access certain tables with PowerShell. If we know the exact name of the tables to try and a column within them then we access it directly.

    Firstly within this part of the code, choose a table within your dataset. When I was building I was checking for a couple of tables that had the same column name.

    #Select which tables exist in the datasets
    $tablesToTry = @(
        "######",
        "######"
    )

    Next, pick a column to query. I used [id] in my example.

    #Column to try within the tables
    $columnSuffix = "####"

    Lastly I had certain named datasets I wanted to query and some I wanted to exclude so that can be covered in this part of the code

    #Dataset names to include or exclude - blank returns all, separate with | for multiple
    $include = ""
    $exclude = ""

    Connect to Power BI

    The script starts by connecting to your Power BI account using:

    Connect-PowerBIServiceAccount
    

    If the connection fails, it stops and logs a warning. Once connected, the script can access all workspaces and datasets your account has permission to see.

    Gather Workspaces and Datasets

    It retrieves all your workspaces using:

    Get-PowerBIWorkspace -All
    

    Then, for each workspace, it lists all datasets. You can filter which datasets to include or exclude with simple patterns. For example:

    $include = "Sales|Finance"
    $exclude = "Test"
    

    Test Tables and Columns

    For each dataset, the script loops through a list of tables you specify:

    $tablesToTry = @("Orders", "Customers")
    $columnSuffix = "id"
    

    It builds a DAX query for each table/column combination, using TOPN to return a small sample. This keeps the load low while confirming the table exists and is accessible.

    Example query generated for the “Orders” table:

    EVALUATE
    TOPN(10, 'Orders', 'Orders'[id], DESC)

    Execute Queries via REST API

    The script runs the queries using the Power BI REST API:

    Invoke-PowerBIRestMethod -Url "datasets/$($dataset.Id)/executeQueries" -Method Post
    
    • If the query succeeds, it logs the dataset and table in $successfulDatasets and prints the rows.
    • If it fails, it captures the error and logs it in $failedDatasets for troubleshooting later.

    Trigger Report Usage

    For each dataset, the script looks for reports linked to it:

    Get-PowerBIReport -WorkspaceId $workspace.Id | Where-Object { $_.DatasetId -eq $dataset.Id }
    

    If a report is linked, the script exports it to PDF and discards the file. The export counts as usage and keeps the dataset active.

    Invoke-PowerBIRestMethod -Url "reports/$($report.Id)/ExportTo" -Method Post -Body (@{ format = "PDF" } | ConvertTo-Json)
    

    Logging and Results

    At the end, the script prints three tables:

    • ✅ Successfully queried datasets
    • ❌ Datasets that failed
    • 📊 Datasets where usage was triggered

    # ============================
    # POWERBI Trigger Usage
    # James Mounsey-Moran
    # ============================
    
    
    # ============================
    # CONFIGURATION
    # ============================
    
    
    #Select which tables exist in the datasets
    $tablesToTry = @(
        "######",
        "######"
    )
    
    
    #Column to try within the tables
    $columnSuffix = "####"
    
    #Dataset names to include or exclude - blank returns all, separate with | for multiple
    $include = ""
    $exclude = ""
    
    
    
    # ============================
    
    $daxQueryTemplate = @"
    EVALUATE
    TOPN(10, {0}, {1}, DESC)
    "@
    
    $failedDatasets     = @()
    $successfulDatasets = @()
    $usageTriggered     = @()
    
    # ============================
    # CONNECT TO POWER BI SERVICE
    # ============================
    try {
        Connect-PowerBIServiceAccount -ErrorAction Stop
    }
    catch {
        Write-Warning "Failed to connect to Power BI Service: $($_.Exception.Message)"
        return
    }
    
    # ============================
    # GET ALL WORKSPACES
    # ============================
    try {
        $workspaces = Get-PowerBIWorkspace -All -ErrorAction Stop
    }
    catch {
        Write-Warning "Failed to retrieve workspaces: $($_.Exception.Message)"
        return
    }
    
    foreach ($workspace in $workspaces) {
        Write-Output "`nProcessing workspace: $($workspace.Name) ($($workspace.Id))"
    
        try {
              $datasets = Get-PowerBIDataset -WorkspaceId $workspace.Id -ErrorAction Stop |
        Where-Object {
            (-not $include -or $_.Name -match "(?i)$include") -and
            (-not $exclude -or $_.Name -notmatch "(?i)$exclude")
        }
        }
        catch {
            Write-Warning "Failed to list datasets in workspace $($workspace.Name): $($_.Exception.Message)"
            continue
        }
    
        if ($datasets.Count -eq 0) {
            Write-Output "  No  datasets found. Skipping."
            continue
        }
    
        foreach ($dataset in $datasets) {
            Write-Output "  Processing dataset: $($dataset.Name) ($($dataset.Id))"
    
            $queriedSuccessfully = $false
            $tableErrors = @()
    
            foreach ($tableName in $tablesToTry) {
            $quotedTable = "'$tableName'"
            $columnName  = "$quotedTable[$columnSuffix]"
            $daxQuery    = [string]::Format($daxQueryTemplate, $quotedTable, $columnName)
    
                Write-Output "    Trying DAX: $daxQuery"
                try {
                    $response = Invoke-PowerBIRestMethod `
                        -Url "datasets/$($dataset.Id)/executeQueries" `
                        -Method Post `
                        -Body (@{
                            queries = @(@{ query = $daxQuery })
                            serializerSettings = @{ includeNulls = $true }
                        } | ConvertTo-Json -Depth 5) `
                        -ErrorAction Stop
    
                    $json = $response | ConvertFrom-Json
                    Write-Output "    ✅ Query successful for table $tableName."
                    $json.results[0].tables[0].rows | Format-Table
    
                    $queriedSuccessfully = $true
                    $successfulDatasets += [PSCustomObject]@{
                        Workspace = $workspace.Name
                        Dataset   = $dataset.Name
                        DatasetId = $dataset.Id
                        Table     = $tableName
                        Timestamp = Get-Date
                    }
    
                    break
                }
                catch {
                    $fullMsg = $_.Exception.ToString()
                    if ($fullMsg -match '"value":\s*"([^"]+)"') {
                        $errorMessage = $matches[1]
                    }
                    else {
                        $errorMessage = $_.Exception.Message
                    }
    
                    Write-Warning "    ❌ Failed for table $tableName $errorMessage"
                    $tableErrors += "Table $tableName $errorMessage"
                }
            }
    
            if (-not $queriedSuccessfully) {
                Write-Warning "  Could not query dataset $($dataset.Name) with any table."
                $failedDatasets += [PSCustomObject]@{
                    Workspace = $workspace.Name
                    Dataset   = $dataset.Name
                    DatasetId = $dataset.Id
                    Error     = ($tableErrors -join "; ")
                    Timestamp = Get-Date
                }
            }
    
            # ============================
            # REGISTER USAGE (EXPORT REPORT)
            # ============================
            try {
                $reports = Get-PowerBIReport -WorkspaceId $workspace.Id | Where-Object { $_.DatasetId -eq $dataset.Id }
                if ($reports.Count -gt 0) {
                    $report = $reports[0]  # just pick the first linked report
                    Write-Output "    Triggering usage via ExportTo on report: $($report.Name) ($($report.Id))"
    
                    $null = Invoke-PowerBIRestMethod `
                        -Url "reports/$($report.Id)/ExportTo" `
                        -Method Post `
                        -Body (@{ format = "PDF" } | ConvertTo-Json -Depth 3) `
                        -ErrorAction Stop
    
                    Write-Output "    ✅ Usage registered for dataset $($dataset.Name) via report $($report.Name)."
                    $usageTriggered += [PSCustomObject]@{
                        Workspace = $workspace.Name
                        Dataset   = $dataset.Name
                        Report    = $report.Name
                        Timestamp = Get-Date
                    }
                }
                else {
                    Write-Warning "    ⚠️ No report found linked to dataset $($dataset.Name). Cannot register usage."
                }
            }
            catch {
                Write-Warning "    ❌ Failed to trigger usage for dataset $($dataset.Name): $($_.Exception.Message)"
            }
        }
    }
    
    # ============================
    # REPORT SUCCESSFUL DATASETS
    # ============================
    if ($successfulDatasets.Count -gt 0) {
        Write-Output "`n✅ Successfully queried datasets:"
        $successfulDatasets | Format-Table -AutoSize
    }
    
    # ============================
    # REPORT FAILED DATASETS
    # ============================
    if ($failedDatasets.Count -gt 0) {
        Write-Output "`n❌ Datasets failed to query:"
        $failedDatasets | Format-Table -AutoSize
    }
    
    # ============================
    # REPORT USAGE REGISTRATION
    # ============================
    if ($usageTriggered.Count -gt 0) {
        Write-Output "`n📊 Usage registered for datasets:"
        $usageTriggered | Format-Table -AutoSize
    }
    else {
        Write-Output "`n⚠️ No usage registrations were made."
    }
    

    Once this is running, you won’t need to worry about refresh being paused after 60 days. The script keeps everything ticking over by simulating activity, so your datasets stay active and ready whenever you need them.

  • Updating PowerBI reports with Powershell Version 2

    Updating PowerBI reports with Powershell Version 2

    After a recent update for Prism, I needed to make some changes to my PowerBI update script. This is updating PowerBI reports with Powershell version 2!

    My Powershell scripts have served me well for some time, but after a couple of errors popped up, I found a few extra ways to make it more efficient and also improve how it was handling parameters!

    One thing I noticed was that parameters occasionally failed to load correctly after a PBIX upload. This was due to the upload automatically triggering a dataset refresh, which temporarily blocked the API from accepting new parameter values.

    Turns out this was due to having scheduled refreshes enabled on the existing dataset. The solution, temporarily disable the scheduled refresh!

    So if you have the requirement where you have multiple instances of the same report across the same or different workspaces and need to update them with your latest pbix file this script is for you!

    What the script does?

    This script will pick up a pbix file then scan over your workspaces (based on when filters you apply). When it finds reports that again match your criteria, it will take a copy of the current parameters, temporarily disable the scheduled refresh, update the pbix file and then load the parameters back in. When its finished, it will reenable the scheduled refresh. In some cases I’ve then set it to perform a refresh straight after to load the data back into the report.

    So this means you can upload to the PowerBI service with your pbix file from a single source file, the parameters all remain the same as before, and then we pull in the data based on those parameters and all good to go!

    As part of version control, I currently use a parameter to record the current version of my reports. This adds an extra benefit, as I can then use this script to check if the reports are already on the expected version. Just in case the script is interrupted, or we need to deploy in stages.

    I then add in the new version number as part of the script and this becomes a great way to track report versions when updating PowerBI reports with PowerShell.

    How it works?

    • Connects to Power BI Service using Connect-PowerBIServiceAccount.
    • Retrieves all Power BI workspaces using Get-PowerBIWorkspace.
    • Loops through each workspace and identifies reports whose names start with “AETHER”. (or however best for you)
    • For each report:
    • Retrieves the dataset parameters.
    • Updates the first parameter’s value to a specified version if it doesn’t already match.
    • Disables scheduled refresh
    • Overwrites the report in the workspace using a specified PBIX file ($FilePath).
    • Takes over the dataset to ensure permissions are set correctly.
    • Updates the dataset parameters.
    • Re-enables the scheduled refresh

    Key things you can change

    • $DeployVer: The new deployment version to update the first parameter to (e.g., “2025Q2”).
    • The reports to find matching a name convention e.g. $_.Name -LIKE ‘*AETHER*’
    • $FilePath: The path to the PBIX file used for updating reports.
    • Parameter 0 is assumed to represent a version parameter (e.g., “2025Q2”)
    # Connect to Power BI Service Account
    Connect-PowerBIServiceAccount
    
    # Set the deployment version to be used for updating parameters
    $DeployVer = "2025Q2"
    
    # Define the path to the PBIX file to be used for report updates
    $FilePath = "C:\MYFILEPATH\REPORT.pbix"
    
    # Define the conflict action for updating reports (e.g., Create or Overwrite existing reports)
    $Conflict = "CreateOrOverwrite"
    
    # Retrieve all Power BI workspaces
    $workspaces = Get-PowerBIWorkspace -all
    
    # Loop through each workspace
    foreach ($workspace in $workspaces) {
    
        # Get all reports in the current workspace with names starting with "AETHER" - adjust the filter as needed
        $Reportlist = Get-PowerBIReport -WorkspaceId $workspace.Id | Where-Object -FilterScript {
            $_.Name -LIKE '*AETHER*'
        }
    
        # Check if any reports were found in the workspace
        if ($Reportlist) {
            Write-Host "Workspace: $($workspace.Name)" # Log the workspace name
    
            # Loop through each report in the report list
            foreach ($Report in $Reportlist) {
                Write-Host "  Report: $($Report.Name)" # Log the report name
    
                try {
                    # Retrieve the parameters of the dataset associated with the report
                    $ParametersJsonString = Invoke-PowerBIRestMethod -Url "https://api.powerbi.com/v1.0/myorg/groups/$($workspace.Id)/datasets/$($Report.DatasetId)/parameters" -Method Get
                    $Parameters = (ConvertFrom-Json $ParametersJsonString).value # Convert JSON response to PowerShell object
                } catch {
                    Write-Host "Error retrieving parameters: $($_.Exception.Message)"
                    continue
                }
    
                $JsonBase = @{}
                $JsonString = $null # Initialize JSON string variable
    
                # Initialize an empty array to hold parameter updates
                $UpdateParameterList = New-Object System.Collections.ArrayList
    
                # Loop through each parameter and prepare the update list
                foreach ($Parameter in $Parameters) {
                    $UpdateParameterList.add(@{"name" = $Parameter.name; "newValue" = $Parameter.currentValue})
                }
    
                # Check if there are any parameters to update
                if ($UpdateParameterList.Count -gt 0) {
                    # Get the current value of the Version parameter
                    $currentparam = $UpdateParameterList[0].newValue
    
                    Write-Host "Current Parameter Version Value: $currentparam" # Log the current parameter value
    
                    # Check if the current parameter value matches the deployment version
                    if ($currentparam -ne $DeployVer) {
                        Write-Host "Version does not match. Updating..." # Log the update action
    
                        # Display current parameters
                        $UpdateParameterList.newValue
    
                        # Update the first parameter to the new deployment version
                        $UpdateParameterList[0].newValue = $DeployVer
    
                        # Prepare the JSON payload for updating parameters
                        $JsonBase.Add("updateDetails", $UpdateParameterList)
                        $JsonString = $JsonBase | ConvertTo-Json
    
                        # Define the report name
                        $ReportName = $Report.Name
    
                        # Disable refresh schedule for the dataset
                        $disableRefreshBody = @"
    {
    "value": {"enabled": false}
    }
    "@
    
                        try {
                            Invoke-PowerBIRestMethod -Url "https://api.powerbi.com/v1.0/myorg/groups/$($workspace.Id)/datasets/$($Report.DatasetId)/refreshSchedule" -Method Patch -Body ("$disableRefreshBody")
                            Write-Host "Refresh schedule disabled for dataset: $($Report.DatasetId)"
                        } catch {
                            Write-Host "Failed to disable refresh schedule: $($_.Exception.Message)"
                        }
    
                        try {
                            # Take over the dataset to ensure permissions are set correctly
                            Invoke-PowerBIRestMethod -Url "https://api.powerbi.com/v1.0/myorg/groups/$($workspace.Id)/datasets/$($Report.DatasetId)/Default.TakeOver" -Method Post
                        } catch {
                            Write-Host "Error taking over dataset: $($_.Exception.Message)"
                            continue
                        }
    
                        try {
                            # Update the existing report in the workspace
                            New-PowerBIReport -Path $FilePath -Name $ReportName -WorkspaceId $workspace.Id -ConflictAction $Conflict
                        } catch {
                            Write-Host "Error uploading report: $($_.Exception.Message)"
                            continue
                        }
    
                        try {
                            # Update the parameters of the dataset
                            Start-Sleep 5
                            Invoke-PowerBIRestMethod -Url "https://api.powerbi.com/v1.0/myorg/groups/$($workspace.Id)/datasets/$($Report.DatasetId)/Default.UpdateParameters" -Method Post -Body $JsonString
                        } catch {
                            Write-Host "Error updating parameters: $($_.Exception.Message)"
                            continue
                        }
    
                        # Reenable refresh schedule for the dataset
                        $enableRefreshBody = @"
    {
    "value": {"enabled": true}
    }
    "@
    
                        try {
                            Invoke-PowerBIRestMethod -Url "https://api.powerbi.com/v1.0/myorg/groups/$($workspace.Id)/datasets/$($Report.DatasetId)/refreshSchedule" -Method Patch -Body ("$enableRefreshBody")
                            Write-Host "Refresh schedule Enabled for dataset: $($Report.DatasetId)"
                        } catch {
                            Write-Host "Failed to Enable refresh schedule: $($_.Exception.Message)"
                        }
    
                        Remove-Variable UpdateParameterList, JsonString -ErrorAction SilentlyContinue
                    } else {
                        Write-Host "Version already matches. Skipping update." # Log if no update is needed
                    }
                } else {
                    Write-Host "No parameters found for this dataset." # Log if no parameters are found
                }
            }
        } else {
            Write-Host "No reports found in workspace: $($workspace.Name)" # Log if no reports are found in the workspace
        }
    }
    
    # Log the completion of the script
    Write-Host "Script completed."

    This approach has saved me hours on PBIX deployment.
    If you discover any useful additions or tweaks, feel free to reach out on LinkedIn or by email I’d love to hear how you’ve adapted it.

    Thanks for reading!

    https://github.com/AetherAdv/powerbi_powershell_updatereports