Functions/Restore-BsgPbiWorkspace.ps1

<#
    .SYNOPSIS
        Restore a Power BI workspace from a local directory.
         
    .DESCRIPTION
        The workspace must be stored in a local directory.
 
    .PARAMETER Source_WorkspaceName
        The name of the workspace you would like to restore.
        You can find it in the mapping file after using our export Module.
 
    .PARAMETER Path
        The path to the root folder, where the temporary files will be saved.
        Subfolders will be created automatically.
 
    .PARAMETER ImportTenantLevelReports
        Import reports, which are based on datasets from other workspaces.
        Warning: If you import tenant level reports, the dependant workspaces need to be restored beforehand.
        You can manually run this function after all workspaces have been restored using "Import-BsgPbiReportsTenantLevel ...".
 
    .PARAMETER PbiConnection
        The connection to the Power BI Tenant.
 
    .EXAMPLE
        # Restore Workspace
        Restore-BsgPbiWorkspace -Source_WorkspaceName "BSGroup DA - Test Workspace" -Path "C:\temp\BSG PBI Administration"
 
    .EXAMPLE
        # Restore workspace - My Workspace
        Restore-BsgPbiWorkspace -Source_WorkspaceName "My Workspace" -Path "C:\temp\BSG PBI Administration"
 
    .EXAMPLE
        # Restore a single Workspace having reports connected to other workspaces
        Restore-BsgPbiWorkspace -Source_WorkspaceName "BSGroup DA - Test Workspace" -Path "C:\temp\BSG PBI Administration" -ImportTenantLevelReports $true
         
    .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 Restore-BsgPbiWorkspace{

    param
    (
        [Parameter(Mandatory=$true)][string]$Source_WorkspaceName
        ,[Parameter(Mandatory=$true)][string]$Path
        ,[Parameter(Mandatory=$false)][bool]$ImportTenantLevelReports = $false
        ,[Parameter(Mandatory=$false)][Microsoft.PowerBI.Common.Abstractions.Interfaces.IPowerBIProfile] $PbiConnection = $null
    )

    try {

        # Info message
        Write-Host
        Write-Host
        Write-PSFHostColor -Level Host -DefaultColor white -String "---------------------------------------------------------------------------------------------"
        Write-Host
        Write-PSFHostColor -Level Host -DefaultColor white -String " Restoring workspace... "
        Write-Host
        Write-PSFHostColor -Level Host -DefaultColor gray -String " Original 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 restoration..."
            Write-PSFHostColor -Level Host -DefaultColor gray -String "You need to be a <c='white'>Power BI Administrator</c> to restore the tenant."
            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 with user <c='white'>$userName</c> in tenant <c='white'>$TenantId</c>."
            Write-PSFHostColor -Level Host -DefaultColor white -String "Would you like to start the restoration? [y/n]: "
            $confirmTenant = Read-Host
            Write-Host
            if ($confirmTenant -eq 'y'){
                # continue...
            } else{
                Write-PSFHostColor -Level Host -DefaultColor gray -String "Restoration stopped."
                Logout-PowerBI
                return
            }
        }

        # Define base path 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
        $FileName_WorkspaceMapping = "Mapping_Workspace.json"
        $FileName_ReportDatasetMapping = "Mapping_ReportDataset.json"
        $Path_WorkspaceMapping = Join-Path -Path $Path_Workspace -ChildPath $FileName_WorkspaceMapping
        $Path_ReportDatasetMapping = Join-Path -Path $Path_Workspace -ChildPath $FileName_ReportDatasetMapping

        # ===
        # Get Workspace mapping
        # =

        try{
            
            # Check path
            if ((Test-Path $Path_Workspace) -eq $false){
                throw "Path `"$Path_Workspace`" does not exist."
            }

            # Check metadata file
            if ((Test-Path $Path_WorkspaceMapping) -eq $false){
                throw "File does not exist is folder `"$Path_Workspace`"."
            }

            # Get old workspace mapping
            $WorkspaceMapping = Get-Content -Path $Path_WorkspaceMapping | ConvertFrom-Json -ErrorAction Stop

            # Check if workspace already exists
            $WorkspaceExists = [bool]($WorkspaceMapping.PSobject.Properties.name -match "new_id");

        } catch{
            throw "Error while getting workspace metadata file `"$FileName_WorkspaceMapping`". `n$_"   
        }


        # ===
        # Import workspace
        # =
        if ($WorkspaceExists){
            $Target_WorkspaceId = $WorkspaceMapping.new_id
            $Target_WorkspaceName = $WorkspaceMapping.new_name
            Write-Host
            Write-PSFHostColor -Level Host -DefaultColor gray -String "Workspace <c='white'>$Target_WorkspaceName</c> already exists. Skip workspace creation."
        } else {

            # Create new workspace, add new_id to mapping file
            Import-BsgPbiWorkspace -Path_Workspace $Path_Workspace

            # Get new workspace metadata from mapping file
            try{
                $WorkspaceMapping = Get-Content -Path $Path_WorkspaceMapping | ConvertFrom-Json -ErrorAction Stop
                $Target_WorkspaceId = $WorkspaceMapping.new_id
                $Target_WorkspaceName = $WorkspaceMapping.new_name
            } catch {
                throw "Error while getting workspace metadata file `"$FileName_WorkspaceMapping`". `n$_"
            }

        }

        
        # ===
        # Import User-Permissions of workspace (personal workspaces cannot be shared)
        # =

        if ($Target_WorkspaceId -ne 'me'){
            try{

                if ($null -ne $Target_WorkspaceId){
                    Import-BsgPbiWorkspaceUsers -Path_Workspace $Path_Workspace -Target_WorkspaceId $Target_WorkspaceId
                }
    
            } catch {
                throw "Error while getting workspace metadata file `"$FileName_WorkspaceMapping`". `n$_"
            }
        }


        # ===
        # Get report and dataset metadata from file
        # =

        try{
            
            # Check path
            if ((Test-Path $Path_Workspace) -eq $false){
                throw "Path `"$Path_Workspace`" does not exist."
            }

            # Check mapping file
            if ((Test-Path $Path_ReportDatasetMapping) -eq $false){
                throw "File does not exist is folder `"$Path_Workspace`"."
            }
            $DatasetReport_Mapping = Get-Content -Path $Path_ReportDatasetMapping | ConvertFrom-Json -ErrorAction Stop
            $NumberOfReportsWithSharedDataset =  @($DatasetReport_Mapping | Where-Object {$_.restoreAction -eq 'Wait_UpdatePbiConnection'}).count

        } catch{
            throw "Error while getting report-dataset mapping file `"$FileName_ReportDatasetMapping`". `n$_"   
        }


        # ===
        # 1. loop through reports, which own a dataset
        # =
        $DatasetReport_Mapping_WithDataset = $DatasetReport_Mapping | Where-Object {$_.isDatasetOwner -eq $true}
        Foreach ($mappingObject in $DatasetReport_Mapping_WithDataset) {

            # ===
            # Import reports including datasets
            # =

            Import-BsgPbiReport -Target_WorkspaceId $Target_WorkspaceId -Source_ReportId $mappingObject.reportId -Path_Workspace $Path_Workspace  -ErrorAction Stop

        }


        # ===
        # 2. loop through reports, which do not have an own dataset
        # =
        $DatasetReport_Mapping_WithoutDataset = $DatasetReport_Mapping | Where-Object {$_.isDatasetOwner -eq $false}
        Foreach ($mappingObject in $DatasetReport_Mapping_WithoutDataset) {

            # ===
            # Import reports
            # =

            Import-BsgPbiReport -Target_WorkspaceId $Target_WorkspaceId -Source_ReportId $mappingObject.reportId -Path_Workspace $Path_Workspace  -ErrorAction Stop

        }


        # ===
        # Loop through mapping file (again because new data was written in last step and better logging-readability)
        # =
        $DatasetReport_Mapping = Get-Content -Path $Path_ReportDatasetMapping | ConvertFrom-Json -ErrorAction Stop
        Foreach ($mappingObject in $DatasetReport_Mapping) {

            # ===
            # Import DatasetMetadata
            # =

            if ($mappingObject.isDatasetOwner){
                Import-BsgPbiDataset -Target_WorkspaceId $Target_WorkspaceId -Target_DatasetId $mappingObject.new_datasetId -Source_DatasetId $mappingObject.datasetId -Path_Workspace $Path_Workspace -ErrorAction Stop
            }
            
        }


        # ===
        # TODO: Import Apps
        # ! Microsoft API does not exist at the moment.
        # =


        # ===
        # Import Tenant Level Reports
        # =

        if ($ImportTenantLevelReports){
            Import-BsgPbiReportsTenantLevel -Path $Path -PbiConnection $PbiLogin
        } else {
            if ($NumberOfReportsWithSharedDataset -eq 1){
                Write-Host
                Write-Warning "$NumberOfReportsWithSharedDataset report need to wait for other workspaces to complete."
                Write-Host
                Write-PSFHostColor -Level Host -DefaultColor gray -String "<c='white'>Info</c>: The connection of the report is based on a dataset in another workspace."
                Write-PSFHostColor -Level Host -DefaultColor gray -String " The report is marked as <c='white'>restoreAction = 'Wait_UpdatePbiConnection'</c> in the mapping file."
                Write-PSFHostColor -Level Host -DefaultColor gray -String " Use '<c='white'>Import-BsgPbiReportsTenantLevel</c>...' to update this report after the restoration."
            } elseif ($NumberOfReportsWithSharedDataset -ge 2){
                Write-Host
                Write-Warning "$NumberOfReportsWithSharedDataset reports need to wait for other workspaces to complete."
                Write-Host
                Write-PSFHostColor -Level Host -DefaultColor gray -String "<c='white'>Info</c>: The connections of these reports are based on datasets in other workspaces."
                Write-PSFHostColor -Level Host -DefaultColor gray -String " The reports are marked as <c='white'>restoreAction = 'Wait_UpdatePbiConnection'</c> in the mapping file."
                Write-PSFHostColor -Level Host -DefaultColor gray -String " Use '<c='white'>Import-BsgPbiReportsTenantLevel</c>...' to import these reports after all workspaces have been restored."
            }
        }

        

        # Info message
        Write-Host
        Write-PSFHostColor -Level Host -DefaultColor white -String "---------------------------------------------------------------------------------------------"
        Write-Host
        Write-PSFHostColor -Level Host -DefaultColor green -String " Workspace restored."
        Write-Host
        Write-PSFHostColor -Level Host -DefaultColor gray -String " Old name: <c='white'>$Source_WorkspaceName</c>"
        Write-PSFHostColor -Level Host -DefaultColor gray -String " New name: <c='white'>$Target_WorkspaceName</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 {
        Write-Host
        Stop-PSFFunction -Message "Could not completely restore workspace `"$Source_WorkspaceName`"." -EnableException $False -Errorrecord $_
        return
    }

}