pbiTools.psm1

# Power BI Tools and Library
# (v) Bob, Prodata Sept 2019
# History
# 24/09/2019 First Version
#
# Install-Module MicrosoftPowerBIMgmt



function New-pbiReport
{
    param(
        [Parameter(Mandatory=$True)]$Path,   #File OR Folder. Can be Array
        [Parameter(Mandatory=$True)]$Workspace, #Single Workspace
        [Parameter(Mandatory=$False)]$Name, #Name of DataSet/Report. If Blank will take from File
        [Parameter(Mandatory=$False)]$SqlServer, #Name of SQL Servr to Map Model to (If a Model )
        [Parameter(Mandatory=$False)]$UserName #Name of Current User. If specified will take over dataset if needed )
    )
    $ErrorActionPreference = "Stop"
    $WorkspaceID = Get-PowerBIWorkspace -Name  $Workspace
    $BaseName =$Name

    foreach ($p in $Path) { 
       Get-ChildItem -Path $p| Where {$_.extension -like ".pbix"}| foreach  {
                 Write-Verbose -Message $_
                 if (!($BaseName)) {$Name=$_.BaseName } else {$Name =$BaseName}
                 $Id= New-PowerBIReport -Path $_.FullName -Name $Name -Workspace  $WorkspaceID -ConflictAction:"CreateOrOverwrite" | Select -ExpandProperty "Id"
                 $count =@(Get-PowerBIReport -Workspace $WorkspaceID  -Name $_.BaseName ).Count
                # if ($count -gt 1) {
                # Invoke-pbiReportBindFix -ReportId $Id
                # }
                 if ($SqlServer) {
                    Set-pbiSqlServer -Workspace $Workspace -Name  $Name -SqlServer $SqlServer -UserName $UserName 
                 }   else {
                    Set-pbiDatasetGateway -Workspace $Workspace -Dataset  $Name
                 }

            }

    }
     
}

#Check if Linked Report has Duplicate and fix by
#
#
function Invoke-pbiReportBindFix {

}

function Check-pbiModules {
    Load-Module "SqlServer" 
    Load-Module "MicrosoftPowerBIMgmt"
}


function Remove-pbiReport
{
    param(
           [Parameter(Mandatory=$True)]$Workspace #Single Workspace
    )
    $WorkspaceId=(Get-PowerBIWorkspace -Name  $Workspace) | Select -ExpandProperty "Id"
   Get-PowerBIReport -Workspace (Get-PowerBIWorkspace -Name  $Workspace) | foreach {
        $Id =$_.Id
        $Url = "groups/$WorkspaceId/reports/$Id"
        Invoke-PowerBIRestMethod -Url $Url -Method Delete 
   }

   Get-PowerBIDataSet -Workspace (Get-PowerBIWorkspace -Name  $Workspace) | foreach {
        $Id =$_.Id
        $Url = "groups/$WorkspaceId/datasets/$Id"
        Invoke-PowerBIRestMethod -Url $Url -Method Delete 
    }
}


function Set-pbiRebindReport
{
    param(
        [Parameter(Mandatory=$True)][object]$Workspace , #Single Workspace
        [Parameter(Mandatory=$False)][String]$Report="*", #Name of Report. Can be *
        [Parameter(Mandatory=$False)][String]$ForceDatasetId   #DataSetId to force a Bind To
       
    )
    $ErrorActionPreference = "Stop"
    $WorkspaceId=(Get-PowerBIWorkspace -Name  $Workspace) | Select -ExpandProperty "Id"
    Get-PowerBIReport -WorkspaceId $WorkspaceId | Where {$_.Name -like $Report} | foreach  {
       $DatasetId=$_.DatasetId
       $reportId=$_.Id
       $Url ="groups/$WorkspaceId/datasets/$DatasetId"

       $Id=Get-PowerBIDataset -WorkspaceId $WorkspaceId  -Id $DatasetId | Select -ExpandProperty "Id"
       if (!$Id) {
           Write-Verbose -Message ("Rebind Report: " + $_.Name)
           #Get Name of Dataset in Remote Workspace
           $Url ="groups/$WorkspaceId/datasets/$DatasetId"
           $json=Invoke-PowerBIRestMethod -Url $Url -Method Get
           $data =   ConvertFrom-Json $json
           $DatasetName =$data.name
     
           if ($ForceDatasetId) {
                $newDatasetId=$ForceDatasetId
           } else {
                   #Get Dataset ID for Local Workspace Dataset
                $newDatasetId=Get-PowerBIDataset -WorkspaceId $WorkspaceId | Where {$_.Name -eq $DatasetName } | Select -ExpandProperty "Id"
           }
           #Rebind Report to Dataset specified
           $url="groups/$WorkspaceId/reports/$reportId/Rebind"
          
           $Body=@"
           {datasetId: "$newDatasetId"}
"@

           Invoke-PowerBIRestMethod -Url $Url -Method Post -Body $Body
       }
    }  
}


function Set-pbiDatasetTakeOver
{
    param(
        [Parameter(Mandatory=$True)]$Workspace , #Single Workspace
        [Parameter(Mandatory=$False)]$Dataset="*",  #Name of Dataset. Can by Array
        [Parameter(Mandatory=$False)]$UserName=""  #Username of USer to Change Ownership to

        
    )
    $ErrorActionPreference = "Stop"
    $WorkspaceId=(Get-PowerBIWorkspace -Name  $Workspace) | Select -ExpandProperty "Id"
    Get-PowerBIDataset -WorkspaceId $WorkspaceId | Where {$_.Name -like $Dataset} |foreach  {
        $Id =$_.Id
        $Url ="groups/$WorkspaceId/datasets/$Id/Default.TakeOver"
        Invoke-PowerBIRestMethod -Url $Url -Method Post -Body ""
        
    }  

}


function Set-pbiDatasetGateway
{
    param(
        [Parameter(Mandatory=$True)]$Workspace , #Single Workspace
        [Parameter(Mandatory=$False)]$Dataset="*"  #Name of Dataset. Can by Array
        
    )
    $ErrorActionPreference = "Stop"
    $WorkspaceId=(Get-PowerBIWorkspace -Name  $Workspace) | Select -ExpandProperty "Id"
    Get-PowerBIDataset -WorkspaceId $WorkspaceId | Where {$_.Name -like $Dataset} |foreach  {
        $Id =$_.Id
        $DataSet = $_.Name
        $IsOnPremGatewayRequired =$_.IsOnPremGatewayRequired
        if ($IsOnPremGatewayRequired) {
            $Url ="groups/$WorkspaceId/datasets/$Id/DiscoverGateways"
            $json=Invoke-PowerBIRestMethod -Url $Url -Method Get 
            $data =   ConvertFrom-Json $json
            $GatewayId=$data.value.Id
           
            $Body=@"
{"gatewayObjectId": "$GatewayId"}
"@

            $url="groups/$WorkspaceId/datasets/$Id/BindToGateway"
            Write-Verbose -Message $url
            try {
                Invoke-PowerBIRestMethod -Url $Url -Method Post -Body $Body 
            }
            catch [System.Net.Http.HttpRequestException],[AggregateException] {
               write-Error ("Could Not Bind Gateway on DataSet $DataSet . Check SqlServer Name and Gateway Configuration.") 
               write-Error (Resolve-PowerBIError -Last | Out-String) 
            
            }
            catch {
                #write-Error ( $_.Exception)
                write-Error (Resolve-PowerBIError -Last | Out-String) 
            }
        }
    }  
}

function Get-pbiStatus
{
    param(
        [Parameter(Mandatory=$True)] [object] $Workspace ,  #WorkSpaces. Can be Array
        [Parameter(Mandatory=$False)][string] $Dataset="*",#Name of Dataset. Can by WildCard
        [Parameter(Mandatory=$False)][hashtable] $Errors =@{},     #Hash Table of Errors During Processing to tag them on Status returned
        [Parameter(Mandatory=$False)][hashtable] $StartTimes =@{}     #Hash Table of Start Times if Processing Failures not logged

    )
   # $ErrorActionPreference = "Stop"
    $DataSetList=@()
    ForEach ($w in $Workspace ) {
        $WorkspaceId=(Get-PowerBIWorkspace -Name  $w) | Select -ExpandProperty "Id"
        Get-PowerBIDataset -WorkspaceId $WorkspaceId | Where {$_.Name -like $Dataset -and $_.IsRefreshable -eq $True} | Where-Object {$_.Name -NotIn "Usage Metrics Report"}|foreach  {
           $Id=$_.Id 
           $obj=$null
           $obj = New-Object System.Object  
           $obj | Add-Member -type NoteProperty -Name WorkSpace -Value $w 
           $obj | Add-Member -type NoteProperty -Name DataSetId -Value $_.id
           $obj | Add-Member -type NoteProperty -Name DataSet -Value $_.Name
           $obj | Add-Member -type NoteProperty -Name ConfiguredBy $_.ConfiguredBy
           $obj | Add-Member -type NoteProperty -Name IsOnPremGatewayRequired  $_.IsOnPremGatewayRequired 
    
           #Get last Refresh History
           $url="groups/$WorkspaceId/datasets/$Id/refreshes?" + '$top=1'
           $json=Invoke-PowerBIRestMethod -Url $Url -Method Get
           $data =   ConvertFrom-Json $json
           $status =(&{if ($data.value.status -eq "Unknown") {"InProgress"} else {$data.value.status}})
           if ($Errors.item($Id).length -gt 0) {$status = "Failed" } 
           if ($Errors.item($Id).length -gt 0) {
            $startTime = $StartTimes.item($Id)
            $endTime =$startTime
           } else {
            if ($data.value.startTime) {$startTime = [datetime]$data.value.startTime}
            if ($data.value.EndTime){$endTime =[datetime]$data.value.EndTime}
           }

           $obj | Add-Member -type NoteProperty -Name refreshStatus $status
           $obj | Add-Member -type NoteProperty -Name refreshType $data.value.refreshType
           $obj | Add-Member -type NoteProperty -Name startTime $startTime
           $obj | Add-Member -type NoteProperty -Name EndTime  $endTime
           $obj | Add-Member -type NoteProperty -Name Error ($Errors.item($Id) + $data.value.serviceExceptionJson ) 
    
           $DataSetList+=$obj
        }
    }
    $DataSetList
}


function Invoke-pbiRefresh
{
    param(
        [Parameter(Mandatory=$True)] [object]$Workspace ,                     #Array of Workspaces
        [Parameter(Mandatory=$False)] [String]$Dataset="*",                   #Name of Dataset. Can by Array
        [parameter(Mandatory=$false)] [switch]$MailOnFailure=$False,  #Email on Failure
        [parameter(Mandatory=$false)] [switch]$Wait=$False,           #Wait for Failure (Synchronous)
        [parameter(Mandatory=$false)] [int]$WaitTime=10,          #Time to wait for checking Processing Progress
        [parameter(Mandatory=$false)] [String]$SqlServer ,#Sql Server to Log Results. Will Log if this is present
        [parameter(Mandatory=$false)] [String]$SqlDatabaseName  ,           #Sql Server to Log Results to. Will Log if this is present
        [parameter(Mandatory=$false)] [String]$SqlSchemaName ="dbo" ,           #Sql Server to Log Schema to. Will Log if this is present
        [parameter(Mandatory=$false)] [String]$SqlTableName ="ProcessView", #Sql Table to Log Results to
        [parameter(Mandatory=$false)] [switch]$IgnoreProcessError,          #Raise Error if any Process Errors. Default is to Fail on any Error
        [parameter(Mandatory=$false)] [switch]$NoOutput                  #Output Status When Finished

    )
    $ErrorActionPreference = "Stop"
    $errormessage=""
    #Check-pbiModules
    $Datasets =@()
    $Errors =@{}
    $ProcessKeys =@{}
    $StartTimes =@{}

    $Failed =$false
    ForEach ($w in $Workspace) {
        $wDatasets=@()
        $WorkspaceId=(Get-PowerBIWorkspace -Name  $w) | Select -ExpandProperty "Id"
        $wDatasets=Get-PowerBIDataset -WorkspaceId $WorkspaceId | Where {$_.Name -like $Dataset -and $_.IsRefreshable -eq $True } | Where-Object {$_.Name -NotIn "Usage Metrics Report"}
        [Int]$percentComplete=0
        [Int]$maxPercent=0
        $DatasetCount=$wDatasets.Count
        $progressTime =$false

        #Het Ladt Refresh Timing for Progress Bar
        forEach ($d in $wDatasets) { 
           $id=$d.Id
            $url="groups/$WorkspaceId/datasets/$Id/refreshes?" + '$top=1'
            $json=Invoke-PowerBIRestMethod -Url $Url -Method Get
            $data =   ConvertFrom-Json $json
            if (($data.value)) {
                $progressTime =$false
                $maxPercent=$DatasetCount
                Break
            } else {
                if ($data.value.endTime) {$maxPercent+= (NEW-TIMESPAN â€“Start $data.value.startTime  â€“End $data.value.endTime ).Seconds}
                if ($maxPercent -gt 0) {$progressTime=$true}
            }     
        }
        if ($maxPercent -eq 0) {$progressTime=$false; $maxPercent=$DatasetCount}
       
        forEach ($d in $wDatasets) { 
            $Id =$d.Id
            $Datasets+=$Id
            $DatasetName=$d.Name
            $IsOnPremGatewayRequired =$d.IsOnPremGatewayRequired


            if ($MailOnFailure.IsPresent) {$notifyOption="MailOnFailure"} else { $notifyOption="NoNotification" }
            $Body='{"notifyOption": "' + $notifyOption+ '"}'
            $url="groups/$WorkspaceId/datasets/$Id/refreshes"
                try {
                    if ($SqlServer) {
                        $Query="INSERT INTO [{0}].[{1}].[{2}] (Workspace,DataSetId,DataSet, IsOnPremGatewayRequired, refreshStatus, refreshType, startTime) VALUES ('{3}','{4}','{5}','{6}','{7}','{8}', getdate()); SELECT SCOPE_IDENTITY()" -f $SqlDatabaseName,$SqlSchemaName,  $sqlTableName,  $w, $id,$DatasetName, $IsOnPremGatewayRequired, "Starting","ViaApi"
                        Write-Verbose $Query
                        $Key= (invoke-sqlcmd -ServerInstance  $SqlServer   -Database $SqlDatabaseName -Query $Query)[0]
                        $ProcessKeys.Add($id,$key)
                    }
                    Write-Progress -Activity "PBI Refresh" -CurrentOperation "$DatasetName" -Status "Starting"
                    $res=Invoke-PowerBIRestMethod -Url $Url -Method Post -Body $Body 
                    if ($SqlServer) {
                        $Query="UPDATE [{0}].[{1}].[{2}] SET EndTime=getdate(), refreshStatus='InProgress' WHERE ProcessKey={3}" -f $SqlDatabaseName,$SqlSchemaName,  $sqlTableName,  $key
                        invoke-sqlcmd -ServerInstance  $SqlServer   -Database $SqlDatabaseName -Query $Query
                    }

                }
                catch [AggregateException] {   
                    $failed=$True
                     $error=$_.Exception.innerexception.message
                    if ($error.Contains("Bad Request")) {$error ="Refresh Limit Reached" }
                    write-Warning ("`r`nRefresh $w\$DatasetName. $error "   ) 
                    Write-Verbose -Message $_.Exception
                    $Errors.Add($id,$error)
                    $StartTimes.Add($id,(Get-Date))
                    if ($SqlServer) {
                        $Query="UPDATE [{0}].[{1}].[{2}] SET EndTime=getdate(), refreshStatus='Failed', Error = '{3}' WHERE ProcessKey={4}" -f $SqlDatabaseName,$SqlSchemaName,  $sqlTableName, $error, $key
                        invoke-sqlcmd -ServerInstance  $SqlServer   -Database $SqlDatabaseName -Query $Query
                    }
                    continue
                }
                catch {
                    $failed=$True
                    write-Warning ("`r`nRefresh $w\$DatasetName. $_.Exception.message"   ) 
                    Write-Verbose -Message $_.Exception
                    $Errors.Add($id,$_.Exception )  
                    $StartTimes.Add($id,(Get-Date)) 
                    if ($SqlServer) {
                        $Query="UPDATE [{0}].[{1}].[{2}] SET EndTime=getdate(), refreshStatus='Failed' WHERE ProcessKey={2}" -f $SqlDatabaseName,$SqlSchemaName,  $sqlTableName, $_.Exception, $key
                        invoke-sqlcmd -ServerInstance  $SqlServer   -Database $SqlDatabaseName -Query $Query
                    }
                    continue    
                }
               
            }
       

            #Synchrous Processing if Required. By Default is Asyncronous
            $startTime=get-date
            if ($Wait.IsPresent) {
                While (!($Complete)) {
                    $InProgress=$false
                    $i=0
                    $errorCount=$Errors.Count
                    $lastError=""
                    $errorDataset=""
                    ForEach ($d  in $wDataSets) {
                        $id=$d.Id
                        if ($Errors.Contains( $id)) {
                           $i+=1  
                           $lastError=$Errors[$id]     
                           $errorDataset=$d.Name + ":"
                        }
                        else {
                            $url="groups/$WorkspaceId/datasets/$Id/refreshes?" + '$top=1'
                            $json=Invoke-PowerBIRestMethod -Url $Url -Method Get
                            $data =   ConvertFrom-Json $json
                            $status=$data.value.status
                            if ($status -eq "Failed" -or $status -eq "Completed") {$i+=1 }
                            if ($status -eq "Unknown") {$InProgress=$true }
                            if ($status -eq "InProgress") {$InProgress =$true }
                            if ($status -eq "Failed") {$failed=$true ; $errorCount +=1; $lastError =$data.value.serviceExceptionJson;   $errorDataset=$d.Name + ":"}
                        }
                     }
                     $seconds= (new-timespan $startTime (get-date) ).TotalSeconds
                     if ($progressTime) {$percentComplete = $seconds / $maxPercent* 100  } else { $percentComplete = $i/$maxPercent  *100}
                     Write-Progress -activity ("PBI Refresh. {0}/{1} Complete. {2} Errors." -f $i, $DatasetCount , $errorCount) -status ("In Progress. $errorDataset$lastError") -PercentComplete $percentComplete
                     if ($i -lt$DatasetCount) { $InProgress = $true; } else {$InProgress =$false ;$Complete=$true }
                     if (!($Complete))  {Start-Sleep -Seconds $WaitTime}
                }

        }
    } 


     #Log Results to Table
    if ((($SqlServer) -and ($SqlDatabaseName)) -or (!($NoOutput ))) {
        $status=Get-pbiStatus -Workspace $Workspace -Dataset $Dataset  -Errors $Errors -StartTimes $StartTimes 
    }

    if  (!($NoOutput )) {
        $Status | Select
    }

    if (($SqlServer) -and ($SqlDatabaseName)) {
        Write-Verbose -Message "Logging to $SqlServer.$SqlDatabaseName.$SqlSchemaName.$SqlTableName"
        $Status | Select-Object -Property * | ForEach-Object {
            $key=$ProcessKeys[$_.DataSetId]  
            if ($SqlServer -and $_.refresshStatus -ne "Failed") {
                $Query="UPDATE [{0}].[{1}].[{2}] SET EndTime=getdate(), refreshStatus='{3}' , Error='{4}' WHERE ProcessKey={5}" -f $SqlDatabaseName,$SqlSchemaName,  $sqlTableName, $_.refreshStatus, $_.Error , $key
                invoke-sqlcmd -ServerInstance  $SqlServer   -Database $SqlDatabaseName -Query $Query
            }
          }
        $Status | ForEach-Object {
            if (!($_.Error)) {
                $s="Workspace: " + $_.WorkSpace + ",Dataset:" + $_.DataSet + ",Error:" + $_.Error 
               if ($_.refreshStatus -eq "Failed") {
                    Write-Error -Message $s
                   }
            } 
        }
    }


    if ($failed) {
        EXIT 1
    }




}


function Set-pbiSqlServer
{
    param(
        [Parameter(Mandatory=$True)]$Workspace , #Single Workspace
        [Parameter(Mandatory=$False)]$Name="*",  #Name of Dataset. Can by Array
        [Parameter(Mandatory=$True)]$SqlServer="",  #New SqlServer Name
        [Parameter(Mandatory=$False)]$UserName=""  #Username of validated user. If this is set then updaet will TakeOver Dataset if required

        
    )
    $ErrorActionPreference = "Stop"
    $WorkspaceId=(Get-PowerBIWorkspace -Name  $Workspace) | Select -ExpandProperty "Id"
    Get-PowerBIDataset -WorkspaceId $WorkspaceId | Where {$_.Name -like $Name} |foreach  {
        $ConfiguredBy=$_.ConfiguredBy
        $Id =$_.Id
        $Dataset = $_.Name
        $Url = "groups/$WorkspaceId/datasets/$Id/datasources"
        $ds=Invoke-PowerBIRestMethod -Url $Url -Method Get 
        $data =   ConvertFrom-Json $ds
        $datasourceType=$data.value.datasourceType
        $oldServer =$data.value.connectionDetails.server
        $Database =$data.value.connectionDetails.database

        if ($Username) {
            if ($ConfiguredBy -ne $UserName) {
                Set-pbiDatasetTakeOver -Workspace $Workspace -Dataset $Dataset 
                $ConfiguredBy=(Get-PowerBIDataset -WorkspaceId $WorkspaceId  -Id $Id) | Select -ExpandProperty "ConfiguredBy"
                 if ($ConfiguredBy -ne $UserName) {
                    Write-Error "Cannot Updatedatasource. Owner=$ConfiguredBy,Current UserName=$UserName"
                 }
            }
        }

        if ($datasourceType -eq "sql") {
           $Url = "groups/$WorkspaceId/datasets/$Id/updatedatasources"
            $Body=@"
    {"updateDetails":[
        {"connectionDetails":
          {
            "server": "$SqlServer",
            "database": "$Database"
          },
          "datasourceSelector":
          {
            "datasourceType": "Sql",
            "connectionDetails":
            {
              "server": "$OldServer",
              "database": "$Database"
            }
          }
        }
      ]
    }
"@

        Write-Verbose -Message $Url
        Write-Verbose -Message $Body
        Write-Host "Updating Datasource for $Workspace\$Dataset. Server $oldServer=>$SqlServer..."
        try {
            Invoke-PowerBIRestMethod -Url $Url -Method Post -Body $Body 
        }
        catch [System.Net.Http.HttpRequestException],[AggregateException] {
            write-Error ("Update for $Workspace\$Dataset.PBIX Failed. Check PowerQuery has no MERGE operations. See https://docs.microsoft.com/en-us/rest/api/power-bi/datasets/updatedatasources") 
            write-Error (Resolve-PowerBIError -Last | Out-String) 
            
        }
        catch {
            write-Error ( $_.Exception)           
        }

      } else {      
        Write-Host "datasourceType=$datasourceType. Not Updated as not Sql"
      }


      #Set PBI Gateway (not Required as it now Auto Sets)
      Set-pbiDatasetGateway -Workspace $Workspace -Dataset $Dataset 

     }     
}

function Load-Module ($m) {

    # If module is imported say that and do nothing
    if (Get-Module | Where-Object {$_.Name -eq $m}) {
        return
    }
    else {

        # If module is not imported, but available on disk then import
        if (Get-Module -ListAvailable | Where-Object {$_.Name -eq $m}) {
            Import-Module $m 
        }
        else {

            # If module is not imported, not available on disk, but is in online gallery then install and import
            if (Find-Module -Name $m | Where-Object {$_.Name -eq $m}) {
                Install-Module -Name $m -Force -Verbose -Scope CurrentUser -AllowClobber
                Import-Module $m 
            }
            else {

                # If module is not imported, not available and not in online gallery then abort
                write-host "Module $m not imported, not available and not in online gallery, exiting."
                Exit 1
            }
        }
    }
}


function Remove-pbiBackupBlob
{
    Param(
    [parameter(Mandatory=$true)] [string]$StorageAccountName,
    [parameter(Mandatory=$true)] [string]$SAS,
    [parameter(Mandatory=$false)] [object]$Containers,
    [parameter(Mandatory=$true)] [int]$Cleanup =7
    )

    $ctx=New-AzStorageContext -StorageAccountName $StorageAccountName -SasToken $SAS -ErrorAction Stop
    Write-Host $("Connecting to {0}" -f $StorageAccountName)


    foreach ($container in $Containers) {
        $blobs =Get-AzStorageBlob   -Context $ctx -Container $Containers | Where-Object {$_.Name -like "*.pbix"} 
        if ($blobs ){
            foreach ($blob in $blobs) {
                $LastModified=$blob.LastModified
                $DaysOld = ([DateTime]::Now.Date - [DateTime]$LastModified.Date)
                if ($DaysOld.Days -gt $Cleanup){
                    Write-Host $("Deleting the blob file {0}" -f $blob.Name)
                    Remove-AzStorageBlob -Context $ctx -Container $container -Blob $blob.Name 
                }
            }
        }

    }
}


function Download-pbiReport
{
    param(
        [Parameter(Mandatory=$False)] [object]$Workspace ="" ,                     #Array of Workspaces
        [Parameter(Mandatory=$False)] [String]$Report="*",                   #Name of Dataset. Can by Array
        [Parameter(Mandatory=$False)] [String]$Path="C:\temp\PowerBI",                   #Path to Export to. Can be a Temp Path if
        [parameter(Mandatory=$false)] [string]$StorageAccountName="",         #Azure StorageAccountName for Upload
        [parameter(Mandatory=$false)] [string]$Container="powerbi",           #Azure $Container eg "PowerBI"
        [parameter(Mandatory=$false)] [string]$SAS=""                         #SAS Token for upload
    )
    $ErrorActionPreference = "Stop"

    if (-Not $workspace) {
        [System.Collections.ArrayList]$workspace=Get-PowerBIWorkspace | select-Object  Name
    }
    if ($SAS) {
        if ((Get-Command "AzCopy.exe" -ErrorAction SilentlyContinue) -eq $null) 
        { 
           Write-Error "Unable to find AzCopy in your PATH. Please download to C:\Windows or in Path"
        }
    }

  
    New-Item -ItemType Directory -Force -Path $Path | Out-Null
    Remove-Item -Path $Path  -Force -Recurse -Filter "*.pbix"

    ForEach ($w in $Workspace) {
         $WorkspaceId=Get-PowerBIWorkspace -Name $w  | Select -ExpandProperty "Id"
         if (-Not  $WorkspaceId) { write-Error "Workspace '$w' Not Found. Check Workspaces and Permissions"}  
         Get-PowerBIReport -WorkspaceId $WorkspaceId | Where {$_.Name -like $Report} | ForEach-Object  {
            $ds=Get-PowerBIDataset -Id $_.DataSetId -WorkspaceId $WorkspaceId 
            if ($ds.IsEffectiveIdentityRequired -eq $True) {
                $pbix=$_.Name + "_"+  (Get-Date -Format "yyyyMMdd") + ".pbix"
                $file=Join-Path -Path $Path -ChildPath  $w
                New-Item -ItemType Directory -Force -Path $file | Out-Null
                $file=Join-Path -Path $file -ChildPath ${pbix}
                if (Test-Path $file) { Remove-Item $file  }
                "Downloading {0}" -f $file
                $Id =$_.Id
                $Url = "groups/$WorkspaceId/reports/$Id/Export"

                Invoke-PowerBIRestMethod -Url $Url -Method get -OutFile $file
                #Export-PowerBIReport -Id $_.Id -OutFile $file
           }
         }
    }

    if ($SAS) {
    $uri = "https://${StorageAccountName}.blob.core.windows.net/${Container}?$SAS"
    AzCopy cp $Path $uri --recursive=true
    }

   
 }

export-modulemember -function New-pbiReport
export-modulemember -function Remove-pbiReport
export-modulemember -function Set-pbiRebindReport
export-modulemember -function Set-pbiDatasetTakeOver
export-modulemember -function Set-pbiDatasetGateway
export-modulemember -function Get-pbiStatus
export-modulemember -function Invoke-pbiRefresh
export-modulemember -function Set-pbiSqlServer
export-modulemember -function Download-pbiReport
export-modulemember -function Remove-pbiBackupBlob