PowerBIManager.psm1
#region Exported Core Functions function Connect-PBI{ [CmdletBinding(DefaultParameterSetName = 'default')] param( [Parameter(Mandatory = $true, ParameterSetName = 'credential')] [System.Management.Automation.CredentialAttribute()] [PSCredential] $Credential, [Parameter(Mandatory = $true)] [string] $ClientId ) if (!$Script:AuthenticationContext) { Write-Verbose -Message 'Initialize AuthenticationContext' $AuthorityAddress = "https://login.microsoftonline.com/common/oauth2/authorize" $script:AuthenticationContext = New-Object -TypeName Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext -ArgumentList ($AuthorityAddress) } Write-Verbose -Message 'Acquire Access Token' $TargetResourceIdentifier = "https://analysis.windows.net/powerbi/api" $AuthorizationRedirectUri = "https://login.live.com/oauth20_desktop.srf" if ($PSCmdlet.ParameterSetName -eq 'credential') { Write-Verbose -Message 'Using credential parameter' $UserCredential = New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.UserPasswordCredential($Credential.UserName, $Credential.Password) $AuthenicationResult = [Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContextIntegratedAuthExtensions]::AcquireTokenAsync($script:AuthenticationContext , $TargetResourceIdentifier , $ClientId , $UserCredential).Result } else { Write-Verbose -Message 'Microsoftonline login' $ADPlatformParams= New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters([Microsoft.IdentityModel.Clients.ActiveDirectory.PromptBehavior]::Auto) $AuthenicationResult = $script:AuthenticationContext.AcquireTokenAsync($TargetResourceIdentifier , $ClientId, [Uri] $AuthorizationRedirectUri , $ADPlatformParams).Result } if(!$AuthenicationResult) { Write-Warning -Message "NOT Authenticated" } else { Write-Information "Authenticated as $($AuthenicationResult.UserInfo.DisplayableId)" $script:TokenExpiresOn = $AuthenicationResult.ExpiresOn $script:AccessToken = $AuthenicationResult.AccessToken $script:AuthenticatedUser = $AuthenicationResult.UserInfo.DisplayableId } } function Invoke-PBIMethod{ [CmdletBinding(DefaultParameterSetName = 'default')] param( [Parameter(Mandatory=$true)] [string] $ApiOperation, [Parameter(Mandatory=$false)] [string] $RequestMethod="GET", [Parameter(Mandatory=$false)] [object] $RequestBody, [Parameter(Mandatory=$false)] [switch] $IgnoreGroup = $false ) $requestUrl = "https://api.powerbi.com/v1.0/myorg/" if (!$IgnoreGroup){ if(![string]::IsNullOrWhiteSpace($script:GroupId)) { $requestUrl = "$($requestUrl)groups/$($script:GroupId)/" } } $requestUrl = $requestUrl + $ApiOperation $requestHeaders = Get-PBIRequestHeader Write-Verbose "Invoke $($RequestMethod) $($requestUrl)" if ($RequestBody) { $jsonBody = ConvertTo-Json $RequestBody -Depth 10 Write-Verbose -Message "Body $($jsonBody)" $result=Invoke-RestMethod -Uri $requestUrl -Headers $requestHeaders -Method $RequestMethod -Body $jsonBody } else { $result=Invoke-RestMethod -Uri $requestUrl -Headers $requestHeaders -Method $RequestMethod } return $result } function Use-PBIGroup{ [CmdletBinding(DefaultParameterSetName = 'default')] param( [Parameter(Mandatory=$false)] [string] $Name, [Parameter(Mandatory=$false)] [string] $Id, [Parameter(Mandatory=$false)] [switch] $Clear = $false ) if (![string]::IsNullOrWhiteSpace($Id)) { $script:GroupId=$Id Write-Information "Using Group $($script:GroupId)" } elseif (![string]::IsNullOrWhiteSpace($Name)) { Write-Verbose -Message "Get Group Id for $($Name)" $grps = Invoke-PBIMethod -ApiOperation groups -IgnoreGroup $groupId = $grps.value |Where-Object name -eq $Name | Select-Object -ExpandProperty id if ([string]::IsNullOrWhiteSpace($groupId)) { Write-Warning "Could not find group $($Name)" } else { $script:GroupId=$groupId Write-Information "Using Group $($Name) ($($script:GroupId))" } } elseif ($Clear) { $script:GroupId=$null } else { Write-Warning "Use-PBIGroup requires either Id or Name parameter or Clear switch." } } #endregion #region Exported Upload Functions Function Import-PBIContent{ [CmdletBinding()] param( [Parameter(ValueFromPipelineByPropertyName,Mandatory=$true)][Alias('PSPath')] [string]$FilePath, [Parameter(Mandatory=$false)] [string] $Dataset, [Parameter(Mandatory=$false)] [object[]] $ConnectionReplacement ) Begin { if ($PSCmdlet.MyInvocation.ExpectingInput -and $PSBoundParameters.ContainsKey(‘Dataset’)) { Write-Warning "Dataset parameter will be ignored." } } Process { $File = Convert-Path $FilePath $requestUrl = "https://api.powerbi.com/v1.0/myorg/" if (!$IgnoreGroup){ if(![string]::IsNullOrWhiteSpace($script:GroupId)) { $requestUrl = "$($requestUrl)groups/$($script:GroupId)/" } } if ([string]::IsNullOrEmpty($Dataset) -or $PSCmdlet.MyInvocation.ExpectingInput) { $Dataset = [IO.Path]::GetFileNameWithoutExtension($File) } $requestUrl = "$($requestUrl)imports?datasetDisplayName=$($Dataset)" #Check if the dataset already exists, detemine nameConflict value $matchDatset = Get-PBIDataset | Where-Object name -eq $Dataset if (!$matchDatset){ $requestUrl = "$($requestUrl)&nameConflict=Abort" } else { $requestUrl = "$($requestUrl)&nameConflict=Overwrite" } $requestHeaders = Get-PBIRequestHeader Write-Verbose "Invoke POST $($requestUrl)" $fileName = [IO.Path]::GetFileName($File) $boundary = "--QQQwwQqqydfdsfhjdjdhsgRRfdfdfd545623529ZMNSJYTkkKKKgfddssereewrw5435345bnvv" $fileBin = [IO.File]::ReadAllBytes($File) $ansiEncoding = [System.Text.Encoding]::GetEncoding("iso-8859-1") $bodyLines = ( $boundary, "Content-Disposition: form-data; name=`"file0`"; filename=`"$($fileName)`"", "Content-Type: application/x-zip-compressed", "", $ansiEncoding.GetString($fileBin), "$($boundary)--", "" ) -join [System.Environment]::NewLine try { $result = Invoke-RestMethod -Uri $requestUrl -Headers $requestHeaders -Method Post -ContentType "multipart/form-data; boundary=$($boundary)" -Body $bodyLines Write-Information "$($File) uploaded." if ($ConnectionReplacement) { #get the new Dataset id from the import id $WaitCount=0 $ImportState = "none" while (($WaitCount -lt 60) -and ($ImportState -ne "Succeeded")) { Start-Sleep -Seconds 1 $ImportedAssets = $null $ImportedAssets = Invoke-PBIMethod "imports/$($result.id)" $ImportState = $ImportedAssets.importState $WaitCount++ } Write-Verbose "Import state $($ImportState) waited $($WaitCount) * seconds" if ($ImportState -eq "Succeeded") { $ImportedDatasetId = $ImportedAssets.datasets[0].id Write-Verbose "Imported dataset id $($ImportedDatasetId)" #replace datasource connections Set-PBIDatasource -DatasetId $ImportedDatasetId -ConnectionReplacement $ConnectionReplacement } else { Write-Warning "Cannot replace connections Import $($result.id) State $($ImportState)" } } } catch [System.Exception] { Write-Error $_.Exception.Message } } } #endregion #region Exported Wrapper Functions function Get-PBIGroup { $result = Invoke-PBImethod "groups" -IgnoreGroupuse-pbi $result.Value } function Get-PBIReport { $result = Invoke-PBImethod "reports" $result.Value } function Get-PBIDataset { $result = Invoke-PBImethod "datasets" $result.Value } function Get-PBIDashboard { $result = Invoke-PBImethod "dashboards" $result.Value } function Get-PBITile { [CmdletBinding(DefaultParameterSetName = 'default')] param( [Parameter(Mandatory=$true)] [string] $DashboardId ) $result = Invoke-PBImethod "dashboards/$($DashboardId)/tiles" $result.Value } #endregion #region Datasource functions function Get-PBIDatasource { [CmdletBinding(DefaultParameterSetName = 'default')] param( [Parameter(Mandatory=$true)] [string] $DatasetId ) # get dataset check owner $dataset = Invoke-PBImethod "datasets/$($DatasetId)" if ($dataset.configuredBy -ne $script:AuthenticatedUser) { Write-Information "Dataset $($DatasetId) transfer ownership from $($dataset.configuredBy) to $($script:AuthenticatedUser)" $result = Invoke-PBImethod -ApiOperation "datasets/$($DatasetId)/takeover" -RequestMethod POST } $result = Invoke-PBImethod "datasets/$($DatasetId)/datasources" $result.value } function Set-PBIDatasource { [CmdletBinding(SupportsShouldProcess = $true ,ConfirmImpact='Medium',DefaultParameterSetName = 'default')] param( [Parameter(Mandatory=$true)] [string] $DatasetId, [Parameter(Mandatory=$true)] [object[]] $ConnectionReplacement ) if ($PSCmdlet.ShouldProcess("Dataset $($DatasetId)")) { $datasources = Get-PBIDatasource -DatasetId $DatasetId $ServerReplacement = $ConnectionReplacement | Where-Object {$_.CurrentServer -and !$_.CurrentDatabase} Foreach ($datasource in $datasources) { if ($datasource.connectionDetails) { if($datasource.connectionDetails.server) { $matchval = $null $matchval = $ServerReplacement | Where-Object CurrentServer -eq $datasource.connectionDetails.server if ($matchval) { Write-Information "Replace dataset $($DatasetId) server $($datasource.connectionDetails.server) with $($ServerReplacement[0].NewServer)" $Newconnection = $datasource.connectionDetails.PSObject.Copy() $Newconnection.server = $ServerReplacement[0].NewServer $RequestBody = @{ updateDetails = @( @{connectionDetails=$Newconnection datasourceSelector=$datasource} ) } Invoke-PBIMethod -ApiOperation "datasets/$($DatasetId)/updatedatasources" -RequestMethod POST -RequestBody $RequestBody } } } } } } #endregion #region private functions function Get-PBIAccesstoken{ #todo check connected, expiry $script:AccessToken } Function Get-PBIRequestHeader { $accesToken = Get-PBIAccesstoken $requestHeaders = @{ 'Content-Type'='application/json' 'Authorization'= "Bearer $accesToken" } $requestHeaders } #endregion |