Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 

Preparing for a certification exam? Ask exam experts all your questions on May 15th. Register now.

Reply
A111462
Regular Visitor

How to batch update CSV data source paths in deployed Power BI Report Server .pbix files?

Hi all,

I'm currently working with Power BI Report Server and have over 300 deployed .pbix reports that connect to CSV files via UNC paths like:
    \\hostnameA\folder\subfolder\file.csv
I need to batch update all these paths to:
    \\hostnameB\folder\subfolder\file.csv

🔍 What I've tried:

  • I used the Report Server REST API (/PowerBIReports(id)/DataSources) to list and patch the data sources.

  • For embedded .pbix reports, I can retrieve the ConnectionString, but fields like Name, Path, and DataSourceType are null.

  • When I try to send a PATCH request with updated connection strings, I get: 「400 Bad Request」

My Questions:

  1. Is there an official or recommended way to update CSV file paths in deployed .pbix reports in bulk?

  2. Is it possible to do this without manually opening each .pbix in Power BI Desktop and re-saving?

  3. Would converting the embedded data sources to shared data sources (.rsds) help with future automation?

Thanks in advance for any advice or workaround!

 

 

PowerShell Script

#script

$server = "http://hostname" # Do not include /reports
$apiBase = "$server/reports/api/v2.0"
$oldHost = "\\hostnameA"
$newHost = "\\hostnameB"


# Get all Power BI reports
$reports = Invoke-RestMethod -Uri "$apiBase/PowerBIReports" -UseDefaultCredentials


foreach ($report in $reports.value) {
    $reportId = $report.Id
    $reportName = $report.Name

    # Get data sources for the report
    $dataSourcesUrl = "$apiBase/PowerBIReports($reportId)/DataSources"
    $dataSources = Invoke-RestMethod -Uri $dataSourcesUrl -UseDefaultCredentials

    $updateNeeded = $false
    $updateDetails = @()


    foreach ($ds in $dataSources.value) {
        $oldConnStr = $ds.ConnectionString


        # Check if old hostname is present
        if ($oldConnStr -like "*$oldHost*") {
            $newConnStr = $oldConnStr -replace [regex]::Escape($oldHost), $newHost
            Write-Host "`n[$reportName] Updating connection string:" -ForegroundColor Cyan
            Write-Host "Original: $oldConnStr"
            Write-Host "Updated : $newConnStr" -ForegroundColor Yellow


            # Prepare updated datasource object
            $updateDetails += @{
                DataSourceType = $ds.DataSourceType
                ConnectionString = $newConnStr
                Name = $ds.Name
                Path = $ds.Path
            }
            $updateNeeded = $true
        }
    }

    if ($updateNeeded) {
        $body = @{ value = $updateDetails } | ConvertTo-Json -Depth 10

        # Send PATCH request to update the data source
        $patchUrl = "$apiBase/PowerBIReports($reportId)/DataSources"
        Invoke-RestMethod -Method Patch -Uri $patchUrl -UseDefaultCredentials -Body $body -ContentType "application/json"

        Write-Host "✔ Data sources updated for report [$reportName]" -ForegroundColor Green
    }
}

1 ACCEPTED SOLUTION

Hi @A111462 , Thank you for reaching out to the Microsoft Community Forum.

 

The reason you're running into trouble with the REST API is that it doesn't support editing embedded data sources within .pbix files. Those connections are stored in a binary format inside a file called DataMashup (not DataModel and there’s no "Connections" file), which isn’t exposed through the API. So, any attempt to use /PowerBIReports(id)/DataSources on embedded sources results in a 400 error, this is expected behaviour. This limitation is documented by Microsoft and unfortunately doesn’t have a workaround using built-in tools alone.

 

To solve this, you can use Tabular Editor, a lightweight, scriptable and supported tool in the Power BI ecosystem, to batch-edit M queries inside your .pbix files. With a small PowerShell script and a Tabular Editor command, you can programmatically open each report, search for old UNC paths in the query definitions, and replace them with the new path. This avoids the need to automate the Power BI Desktop UI.

 

Once that update is complete, you should consider migrating your reports to use shared data sources, called .rsds files in Report Server. These allow connection strings and credentials to be managed independently of the report files. That means that if paths or credentials change again in the future, you can update a single .rsds file, via the web portal or the API, without touching hundreds of .pbix files again. This is the most sustainable way to manage data source connections at scale.

 

Lastly, regarding credentials for embedded data sources, credentials cannot be set via the REST API. They're stored within the .pbix and must be configured in Power BI Desktop. However, .rsds files do support credential submission via the API, including Windows credentials or stored usernames/passwords. This is another reason why migrating to shared data sources is highly recommended for enterprise environments.

 

If this helped solve the issue, please consider marking it 'Accept as Solution' so others with similar queries may find it more easily. If not, please share the details, always happy to help.
Thank you.

View solution in original post

3 REPLIES 3
v-hashadapu
Community Support
Community Support

Hi @A111462 , Thank you for reaching out to the Microsoft Community Forum.

 

There is no official method to update paths, as the REST API cannot modify embedded data sources. The recommended workaround is to use the PowerShell script Develop with the REST APIs for Power BI Report Server to download .pbix files, update their Connections file and re-upload them.

 

Yes, it is possible without manual edits. The script automates updates by editing .pbix files’ internal JSON, eliminating the need to open each in Power BI Desktop.

 

Yes, converting to shared data sources (.rsds) simplifies future updates by centralizing path management. Create .rsds and update reports to reference them, enabling REST API changes. Plan this migration after the immediate update.
Create, Modify, and Delete Shared Data Sources (SSRS)

 

Example PowerShell script:

$server = "http://hostname"

$apiBase = "$server/reports/api/v2.0"

$oldHost = "\\hostnameA"

$newHost = "\\hostnameB"

$tempFolder = "C:\Temp\PBIX_Processing"

$logFile = "C:\Temp\PBIX_Update_Log.txt"

$backupFolder = "C:\Temp\PBIX_Backups"

 

function Write-Log {

    param($Message, $Color = "White")

    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"

    "$timestamp - $Message" | Out-File -FilePath $logFile -Append

    Write-Host "$timestamp - $Message" -ForegroundColor $Color

}

 

if (-not (Test-Path $tempFolder)) { New-Item -ItemType Directory -Path $tempFolder | Out-Null }

if (-not (Test-Path $backupFolder)) { New-Item -ItemType Directory -Path $backupFolder | Out-Null }

Write-Log "Starting .pbix update process" "Green"

 

try {

    $reports = (Invoke-RestMethod -Uri "$apiBase/PowerBIReports" -UseDefaultCredentials).value

    Write-Log "Retrieved $($reports.Count) reports"

} catch {

    Write-Log "Error retrieving reports: $_" "Red"

    exit

}

 

foreach ($report in $reports) {

    $reportId = $report.Id

    $reportName = $report.Name

    Write-Log "Processing report: $reportName" "Cyan"

    $backupPath = Join-Path $backupFolder "$reportName`_$(Get-Date -Format 'yyyyMMdd_HHmmss').pbix"

    $pbixPath = Join-Path $tempFolder "$reportName.pbix"

 

    try {

        $downloadUrl = "$apiBase/PowerBIReports($reportId)/Content/`$value"

        Invoke-RestMethod -Uri $downloadUrl -UseDefaultCredentials -OutFile $pbixPath

        Copy-Item -Path $pbixPath -Destination $backupPath

        Write-Log "Backed up $reportName to $backupPath"

 

        $extractPath = Join-Path $tempFolder "$reportName"

        Expand-Archive -Path $pbixPath -DestinationPath $extractPath -Force

        $connectionsFile = Join-Path $extractPath "Connections"

 

        if (Test-Path $connectionsFile) {

            $connections = Get-Content $connectionsFile | ConvertFrom-Json

            $updated = $false

            foreach ($conn in $connections.Connections) {

                if ($conn.ConnectionString -like "*$oldHost*") {

                    $newConnStr = $conn.ConnectionString -replace [regex]::Escape($oldHost), $newHost

                    Write-Log "Updating: $($conn.ConnectionString) -> $newConnStr" "Yellow"

                    $conn.ConnectionString = $newConnStr

                    $updated = $true

                }

            }

            if ($updated) {

                $connections | ConvertTo-Json -Depth 10 | Set-Content $connectionsFile

                $newPbixPath = Join-Path $tempFolder "$reportName`_updated.pbix"

                Compress-Archive -Path "$extractPath\*" -DestinationPath $newPbixPath -Force

                $uploadUrl = "$apiBase/PowerBIReports($reportId)/Content"

                $headers = @{ "Content-Type" = "application/octet-stream" }

                $pbixContent = [System.IO.File]::ReadAllBytes($newPbixPath)

                Invoke-RestMethod -Method Put -Uri $uploadUrl -UseDefaultCredentials -Headers $headers -Body $pbixContent

                Write-Log "✔ Report [$reportName] updated and uploaded" "Green"

            } else {

                Write-Log "No changes needed for [$reportName]" "Gray"

            }

        } else {

            Write-Log "Connections file not found for [$reportName]" "Red"

        }

    } catch {

        Write-Log "Error processing [$reportName]: $_" "Red"

    } finally {

        Remove-Item -Path $pbixPath, $extractPath, $newPbixPath -Recurse -Force -ErrorAction SilentlyContinue

    }

}

Write-Log "Processing complete!" "Green"

 

If this helped solve the issue, please consider marking it 'Accept as Solution' so others with similar queries may find it more easily. If not, please share the details, always happy to help.
Thank you.

Thank you for your response. I ran the sample script you provided, but when executing the following line:
    Expand-Archive -Path $pbixPath -DestinationPath $extractPath -Force
I encountered an error indicating that .pbix is not a supported archive format. I worked around this by modifying the script as follows:
    $zipPath = Join-Path $tempFolder "$reportName.zip"
    Copy-Item -Path $pbixPath -Destination $zipPath -Force
    Expand-Archive -Path $zipPath -DestinationPath $extractPath -Force

This allowed the file to be extracted successfully. However, there was no file named "Connections" in the extracted folder. Every report processed resulted in the log message: Connections file not found for [$reportName].

I checked the other extracted files that the connection information might be embedded within the "DataModel" file, but I haven’t been able to open it.

Additionally, I’d like to ask: is there a way to use the API to submit connection string credentials (e.g., username and password) when updating data sources?

Thanks again for your help!

Hi @A111462 , Thank you for reaching out to the Microsoft Community Forum.

 

The reason you're running into trouble with the REST API is that it doesn't support editing embedded data sources within .pbix files. Those connections are stored in a binary format inside a file called DataMashup (not DataModel and there’s no "Connections" file), which isn’t exposed through the API. So, any attempt to use /PowerBIReports(id)/DataSources on embedded sources results in a 400 error, this is expected behaviour. This limitation is documented by Microsoft and unfortunately doesn’t have a workaround using built-in tools alone.

 

To solve this, you can use Tabular Editor, a lightweight, scriptable and supported tool in the Power BI ecosystem, to batch-edit M queries inside your .pbix files. With a small PowerShell script and a Tabular Editor command, you can programmatically open each report, search for old UNC paths in the query definitions, and replace them with the new path. This avoids the need to automate the Power BI Desktop UI.

 

Once that update is complete, you should consider migrating your reports to use shared data sources, called .rsds files in Report Server. These allow connection strings and credentials to be managed independently of the report files. That means that if paths or credentials change again in the future, you can update a single .rsds file, via the web portal or the API, without touching hundreds of .pbix files again. This is the most sustainable way to manage data source connections at scale.

 

Lastly, regarding credentials for embedded data sources, credentials cannot be set via the REST API. They're stored within the .pbix and must be configured in Power BI Desktop. However, .rsds files do support credential submission via the API, including Windows credentials or stored usernames/passwords. This is another reason why migrating to shared data sources is highly recommended for enterprise environments.

 

If this helped solve the issue, please consider marking it 'Accept as Solution' so others with similar queries may find it more easily. If not, please share the details, always happy to help.
Thank you.

Helpful resources

Announcements
PBIApril_Carousel

Power BI Monthly Update - April 2025

Check out the April 2025 Power BI update to learn about new features.

Notebook Gallery Carousel1

NEW! Community Notebooks Gallery

Explore and share Fabric Notebooks to boost Power BI insights in the new community notebooks gallery.

April2025 Carousel

Fabric Community Update - April 2025

Find out what's new and trending in the Fabric community.

Top Kudoed Authors