Functions/Backup-BsgPbiWorkspace.ps1
<#
.SYNOPSIS Backup a Power BI workspace to a local directory. .DESCRIPTION The workspace objects and definitions are saved to files in a local directory. .PARAMETER Source_WorkspaceName The name of the workspace you would like to backup. .PARAMETER Path The path to the root folder, where the temporary files will be saved. Subfolders will be created automatically. .PARAMETER PbiConnection The connection to the Power BI Tenant. .PARAMETER MetadataOnly Backup only Metadata excluding actual Dataset files (PBIX Files). .EXAMPLE # Backup workspace Backup-BsgPbiWorkspace -Source_WorkspaceName "BSGroup DA - Test Workspace" -Path "C:\temp\BSG PBI Administration" .EXAMPLE # Backup personal workspace Backup-BsgPbiWorkspace -Source_WorkspaceName "My Workspace" -Path "C:\temp\BSG PBI Administration" .INPUTS .OUTPUTS .NOTES This script uses the Power BI Management module for Windows PowerShell. If this module is not installed, install it by using the command 'Install-Module -Name MicrosoftPowerBIMgmt -Scope CurrentUser'. #> function Backup-BsgPbiWorkspace{ param ( [Parameter(Mandatory=$true)][string]$Source_WorkspaceName ,[Parameter(Mandatory=$true)][string]$Path ,[Parameter(Mandatory=$false)][Microsoft.PowerBI.Common.Abstractions.Interfaces.IPowerBIProfile] $PbiConnection = $null ,[Parameter(Mandatory=$false)][switch]$MetadataOnly = $false ) try { # Info message Write-Host Write-Host Write-PSFHostColor -Level Host -DefaultColor white -String "---------------------------------------------------------------------------------------------" Write-Host Write-PSFHostColor -Level Host -DefaultColor white -String " Backup workspace... " Write-Host Write-PSFHostColor -Level Host -DefaultColor gray -String " Name: <c='green'>$Source_WorkspaceName</c>" Write-Host Write-PSFHostColor -Level Host -DefaultColor white -String "---------------------------------------------------------------------------------------------" Write-Host # PBI connection if ($null -eq $PbiConnection){ Write-Host Write-PSFHostColor -Level Host -DefaultColor gray -String "Choose the tenant and user to start the backup..." Write-PSFHostColor -Level Host -DefaultColor gray -String "You need to be a <c='white'>Power BI Administrator</c> to backup a workspace." try{ Logout-PowerBI $PbiLogin = Login-PowerBI -ErrorAction Stop } catch{ throw "Error trying to connect to Power BI tenant." } $TenantId = $PbiLogin.TenantId $UserName = $PbiLogin.UserName Write-Host Write-PSFHostColor -Level Host -DefaultColor gray -String "You are logged in as user <c='white'>$userName</c> in tenant <c='white'>$TenantId</c>." Write-PSFHostColor -Level Host -DefaultColor gray -String "<c='white'>Would you like to start the backup? [y/n]: </c>" $confirmTenant = Read-Host Write-Host if ($confirmTenant -eq 'y'){ # continue... } else{ Write-PSFHostColor -Level Host -DefaultColor gray -String "Backup stopped." Exit } } # === # Get workspace # = try{ if ($Source_WorkspaceName -in 'Mein Arbeitsbereich', 'Personal Workspace', 'My Workspace', 'mein arbeitsbereich', 'personal workspace', 'my workspace'){ $Source_WorkspaceName = 'My Workspace' $Source_WorkspaceId = 'me' } else{ $Source_Workspace = Get-PowerBIWorkspace -Name $Source_WorkspaceName if (!$Source_Workspace){ throw "Ensure that the workspace exists." } $Source_WorkspaceId = $Source_Workspace.id } } catch{ throw "Error trying to get Power BI workspace. $_" } # Create base paths and filenames $Path_Backup = Join-Path -Path $Path -ChildPath "Backup" $Path_Workspaces = Join-Path -Path $Path_Backup -ChildPath "Workspaces" $Path_Workspace = Join-Path -Path $Path_Workspaces -ChildPath $Source_WorkspaceName $Path_Datasets = Join-Path -Path $Path_Workspace -ChildPath "Datasets" $Path_Reports = Join-Path -Path $Path_Workspace -ChildPath "Reports" $FileName_ReportDatasetMapping = "Mapping_ReportDataset.json" $FileName_WorkspaceMapping = "Mapping_Workspace.json" $Path_ReportDatasetMapping = Join-Path -Path $Path_Workspace -ChildPath $FileName_ReportDatasetMapping # $Path_WorkspaceMapping = Join-Path -Path $Path_Workspace -ChildPath $FileName_WorkspaceMapping # === # Delete old workspace backup if exists? # = if (Test-Path $Path_Workspace){ Write-Host Write-Warning "A backup for workspace `"$Source_WorkspaceName`" already exists." Write-Host Write-PSFHostColor -Level Host -DefaultColor gray -String "Location: <c='white'>$Path_Workspace</c>" Write-PSFHostColor -Level Host -DefaultColor gray -String "It is recommended to delete or rename the old backup." Write-Host $confirmation = Read-Host "Would you like to delete the old workspace backup? [y/n]" Write-Host if ($confirmation -eq 'y'){ Remove-Item -Path $Path_Workspace -Force -Recurse } else{ Write-PSFHostColor -Level Host -DefaultColor gray -String "Backup stopped." Exit } } # === # Export workspace # = # Export Workspace metadata Export-BsgPbiWorkspaceMetadata -Source_WorkspaceId $Source_WorkspaceId -Path_Workspace $Path_Workspace # === # Export dataset # = # Define base URLs and path if ($Source_WorkspaceId -eq 'me'){ $Source_WorkspaceUrl = "https://api.powerbi.com/v1.0/myorg" } else{ $Source_WorkspaceUrl = "https://api.powerbi.com/v1.0/myorg/groups/" + $Source_WorkspaceId } $Source_DatasetUrl = $Source_WorkspaceUrl + "/datasets" $RequestUrl = $Source_DatasetUrl $Path_Datasets = Join-Path -Path $Path_Workspace -ChildPath "Datasets" # Get datasets (API-Request) try{ $Source_Datasets = Invoke-PowerBIRestMethod -Url $RequestUrl -Method Get | ConvertFrom-Json } catch{ throw "Error after calling request URL: `"$RequestUrl`"." } # Export each dataset Foreach ($dataset in $Source_Datasets.value) { $Source_DatasetId = [guid]$dataset.id Export-BsgPbiDataset -Source_WorkspaceId $Source_WorkspaceId -Source_DatasetId $Source_DatasetId -Path_Workspace $Path_Workspace } # === # Export reports and datasets # = # Keep track of the report and dataset IDs $Mapping_ReportDataset = @() # Get the reports from workspace $reports = if ($Source_WorkspaceId -eq "me") { Get-PowerBIReport } else { Get-PowerBIReport -WorkspaceId $Source_WorkspaceId } # === # Export reports (pbix) and create mapping file # = Foreach ($report in $reports) { $Source_ReportId = [guid]$report.id $Source_DatasetId = [guid]$report.datasetId $Source_ReportName = $report.name # Check if report and dataset have different name $Source_Dataset = $Source_Datasets.value | Where-Object {$_.id -eq $Source_DatasetId} if ($Source_Dataset -eq $null){ $datasetIsInDifferentWorkspace = $true $datasetHasDifferentName = $true $Target_DatasetName = '' # dataset is in other workspace } else { $datasetIsInDifferentWorkspace = $false if ($Source_ReportName -eq $Source_Dataset.name){ $datasetHasDifferentName = $false $Target_DatasetName = $Source_ReportName } else { $datasetHasDifferentName = $true $Target_DatasetName = $Source_Dataset.name } } # Export report $ReportExported = Export-BsgPbiReport -Source_WorkspaceId $Source_WorkspaceId -Source_ReportId $Source_ReportId -Path_Workspace $Path_Workspace -MetadataOnly:$MetadataOnly.IsPresent if ($ReportExported -and !$MetadataOnly.IsPresent){ # Get report connection $Filename = $Source_ReportName + "_" + $Source_ReportId $PbiReportConnection = Get-BsgPbiReportConnection -Filename $Filename -Path $Path_Reports # find reports with shared datasets # If report is using a PBI Service dataset (Live connection): mark with 'isUsingSharedDataset = true' if ($PbiReportConnection.Connections.ConnectionType -eq 'pbiServiceLive'){ $isUsingSharedDataset = $true } else { $isUsingSharedDataset = $false } # define dataset owner and restore action # If multiple reports are assossiated with a dataset: Mark the report with the same name as dataset owner, otherwise mark the first one. if ($datasetIsInDifferentWorkspace){ $restoreAction = 'Wait_UpdatePbiConnection' $restoreDescription = 'Wait until all workspace have been processed, then update the report connection and import the report without dataset.' } else { if ($isUsingSharedDataset){ $isDatasetOwner = $false $restoreAction = 'UpdatePbiConnection' $restoreDescription = 'Update the report connection and reestablish connection.' } else { $OwnerAlreadyExists = @($Mapping_ReportDataset | Where-Object {$_.datasetId -eq $Source_DatasetId -and $_.isDatasetOwner -eq $true}).Count -ge 1 if ($OwnerAlreadyExists){ $isDatasetOwner = $false $restoreAction = 'RebindReport' $restoreDescription = 'Rebind the report to another dataset.' } elseif ($datasetHasDifferentName) { $isDatasetOwner = $true $restoreAction = 'CreateDataset_RebindReport' $restoreDescription = 'Create the dataset and rebind the report.' } else{ $isDatasetOwner = $true $restoreAction = 'CreateDataset' $restoreDescription = 'Create the dataset.' } } } } else { $isDatasetOwner = $false if ($Source_ReportName -eq "Report Usage Metrics Report"){ $restoreAction = 'Skip_UsageMetrics' $restoreDescription = 'Report was skipped (not exported). The Usage Metric Report can be recreated in new workspace.' } else{ $restoreAction = 'Skip' $restoreDescription = 'Report was skipped (not exported).' } } # add mapping object $Mapping_ReportDataset += [PSCustomObject]@{ reportId = $Source_ReportId reportName = $Source_ReportName datasetId = $Source_DatasetId isDatasetOwner = $isDatasetOwner isUsingSharedDataset = $isUsingSharedDataset datasetIsInDifferentWorkspace = $datasetIsInDifferentWorkspace datasetHasDifferentName = $datasetHasDifferentName restoreAction = $restoreAction restoreDescription = $restoreDescription new_reportName = $Source_ReportName new_datasetName = $Target_DatasetName } } # === # Save dataset and report mapping in a JSON file # = $Mapping_ReportDataset | ConvertTo-Json -depth 1 | Out-File -FilePath $Path_ReportDatasetMapping if ($Mapping_ReportDataset.restoreAction -contains 'Skip'){ Write-Host Write-Warning "At least one report was skipped." Write-Host Write-PSFHostColor -Level Host -DefaultColor gray -String "<c='white'>Info</c>: Reports marked as <c='white'>restoreAction = 'Skip'</c> in the mapping file will not be restored." } # Info message Write-Host Write-PSFHostColor -Level Host -DefaultColor white -String "---------------------------------------------------------------------------------------------" Write-Host Write-PSFHostColor -Level Host -DefaultColor gray -String " <c='green'>Workspace backup finished.</c>" Write-Host Write-PSFHostColor -Level Host -DefaultColor gray -String " Name: <c='white'>$Source_WorkspaceName</c>" Write-PSFHostColor -Level Host -DefaultColor gray -String " Location: <c='white'>$Path_Workspace</c>" Write-Host Write-PSFHostColor -Level Host -DefaultColor gray -String " <c='white'>Info</c>: To change the name of the workspace or a report, you can edit following mapping files:" Write-PSFHostColor -Level Host -DefaultColor gray -String " Workspace: <c='white'>$FileName_WorkspaceMapping</c>" Write-PSFHostColor -Level Host -DefaultColor gray -String " Reports: <c='white'>$FileName_ReportDatasetMapping</c>" Write-Host Write-PSFHostColor -Level Host -DefaultColor gray -String " Developed by <c='white'>BSGroup Data Analytics AG</c>" Write-PSFHostColor -Level Host -DefaultColor white -String "---------------------------------------------------------------------------------------------" Write-Host Write-Host } catch { if ($Source_WorkspaceName){ Write-Host Stop-PSFFunction -Message "Could not export workspace `"$Source_WorkspaceName`"." -EnableException $False -Errorrecord $_ return } else{ Write-Host Stop-PSFFunction -Message "Could not export workspace `"$Source_WorkspaceId`"." -EnableException $False -Errorrecord $_ return } } } |