WhiteboardAdmin.psm1
# # Constants # $Script:ServiceEndpoint = 'https://whiteboard.microsoft.com/api/v1.0' # Set Resource URI to Whiteboard $Script:ResourceAppIdUri = 'https://whiteboard.microsoft.com' # Azure AD tenant authority $Script:AuthUri = 'https://login.microsoftonline.com/common/' $Script:UserAgent = 'WhiteboardAdminModule/1.0' # # Dependencies # function Invoke-LoadDependencies { $adal = Join-Path $PSScriptRoot 'Microsoft.IdentityModel.Clients.ActiveDirectory.dll' $adalPlatform = Join-Path $PSScriptRoot 'Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll' [System.Reflection.Assembly]::LoadFrom($adal) | Out-Null [System.Reflection.Assembly]::LoadFrom($adalPlatform) | Out-Null } Invoke-LoadDependencies # # Cmdlets # <# .SYNOPSIS Gets one or more Whiteboards from the Microsoft Whiteboard service and returns them as objects. .PARAMETER Token The Azure AD bearer token corresponding to the specified credentials. If unspecified, a new token will be generated. .PARAMETER UserId (Optional) The ID of the user account to query Whiteboards for. All Whiteboards this account has access to will be returned. .PARAMETER WhiteboardId (Optional) The ID of a specific Whiteboard. .EXAMPLE Get-Whiteboard -UserId 00000000-0000-0000-0000-000000000001 Get all of a user's Whiteboards. #> function Get-Whiteboard { [CmdletBinding()] param( [Parameter(Mandatory=$false)] [Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult]$Token, [Parameter(Mandatory=$true, ParameterSetName='ForUser')] [Parameter(Mandatory=$true, ParameterSetName='SingleWhiteboard')] [Guid]$UserId, [Parameter(Mandatory=$true, ParameterSetName='SingleWhiteboard')] [Guid]$WhiteboardId) if ($null -eq $Token) { $Token = Get-AuthToken } $headers = Get-RequestHeader $Token if ($null -ne $UserId) { $uri = "$ServiceEndpoint/users/$UserId/whiteboards" } else { $uri = "$ServiceEndpoint/whiteboards" } $requestResult = Invoke-RestMethod -Headers $headers -Method 'GET' -Uri $uri -ContentType 'application/json' -UserAgent $UserAgent if ($null -ne $WhiteboardId) { return $requestResult | ? { [Guid]::Parse($_.id) -eq $WhiteboardId } } else { return $requestResult } } <# .SYNOPSIS Sets the owner for a Whiteboard. .PARAMETER Token The Azure AD bearer token corresponding to the specified credentials. If unspecified, a new token will be generated. .PARAMETER WhiteboardId The Whiteboard for which the owner is being changed. .PARAMETER OldOwnerId The ID of the previous owner. .PARAMETER NewOwnerId The ID of the new owner. .EXAMPLE Set-WhiteboardOwner -OldOwnerId 00000000-0000-0000-0000-000000000001 -NewOwnerId 00000000-0000-0000-0000-000000000002 Move a Whiteboard from one user to another. #> function Set-WhiteboardOwner { [CmdletBinding( SupportsShouldProcess = $true, ConfirmImpact='High')] param( [Parameter(Mandatory=$false)] [Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult]$Token, [Parameter(Mandatory=$true)] [Guid]$WhiteboardId, [Parameter(Mandatory=$true)] [Guid]$OldOwnerId, [Parameter(Mandatory=$true)] [Guid]$NewOwnerId) if ($PSCmdlet.ShouldProcess("Whiteboard: $WhiteboardId")) { if ($null -eq $Token) { $Token = Get-AuthToken } $headers = Get-RequestHeader $Token $uri = "$ServiceEndpoint/users/$OldOwnerId/whiteboards/$WhiteboardId" # Manually convert the body to JSON (Invoke-RestMethod tries to URL encode the body if you pass it a hash) $body = ConvertTo-Json -Compress @(@{"op"="replace"; "path"="/OwnerId"; "value"=$NewOwnerId }) $requestResult = Invoke-RestMethod -Headers $headers -Method 'PATCH' -Uri $uri -ContentType 'application/json-patch+json' -UserAgent $UserAgent ` -Body $body return $requestResult } } <# .SYNOPSIS Transfer ownership of all Whiteboards owned by a user to another user. .PARAMETER Token The Azure AD bearer token corresponding to the specified credentials. If unspecified, a new token will be generated. .PARAMETER OldOwnerId The ID of the previous owner. .PARAMETER NewOwnerId The ID of the new owner. .PARAMETER WhatIf Execute the command without making any actual changes. Only calls read methods on the REST service. .EXAMPLE Invoke-TransferAllWhiteboards -OldOwnerId 00000000-0000-0000-0000-000000000001 -NewOwnerId 00000000-0000-0000-0000-000000000002 -WhatIf Check how many Whiteboards will be transferred without transferring them. .EXAMPLE Invoke-TransferAllWhiteboards -OldOwnerId 00000000-0000-0000-0000-000000000001 -NewOwnerId 00000000-0000-0000-0000-000000000002 Transfer (and prompt before performing any write actions). #> function Invoke-TransferAllWhiteboards { [CmdletBinding( SupportsShouldProcess = $true, ConfirmImpact='High')] param( [Parameter(Mandatory=$false)] [Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult]$Token, [Parameter(Mandatory=$true)] [Guid]$OldOwnerId, [Parameter(Mandatory=$true)] [Guid]$NewOwnerId) if ($null -ne $Token) { $Token = Get-AuthToken } # Only transfer Whiteboards actually owned by this Id $whiteboardsOwned = @(Get-Whiteboard -Token $Token -UserId $OldOwnerId | Where-Object { [Guid]::Parse($_.ownerId) -eq $OldOwnerId }) Write-Verbose "Found $($whiteboardsOwned.Length) Whiteboards for owner $($OldOwnerId)" if ($PSCmdlet.ShouldProcess("Whiteboards for Owner: $OldOwnerId")) { $whiteboardsOwned | ForEach-Object { Set-WhiteboardOwner -Token $Token -OldOwnerId $OldOwnerId -NewOwnerId $NewOwnerId -WhiteboardId $_.id -Confirm:$false } } } # # Private methods # function Get-AuthToken() { # Use the clientId from the Azure PowerShell module $clientId = '1950a258-227b-4e31-a9cf-717495945fc2' $redirectUri = 'urn:ietf:wg:oauth:2.0:oob' Write-Verbose "Authority URI: $AuthUri" $authContext = [Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext]::new($AuthUri) $platformParameters = [Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters]::new([Microsoft.IdentityModel.Clients.ActiveDirectory.PromptBehavior]::Auto) $userIdentifier = [Microsoft.IdentityModel.Clients.ActiveDirectory.UserIdentifier]::AnyUser $token = $authContext.AcquireTokenAsync($ResourceAppIdUri, $clientId, $redirectUri, $platformParameters, $userIdentifier).Result try { Invoke-RestMethod $ServiceEndpoint -Headers (Get-RequestHeader $token) -UserAgent $UserAgent Write-Verbose "No need to rebuild token" } catch [System.Net.WebException] { Write-Verbose "Rebuilding token from claims" $stream = $_.Exception.Response.GetResponseStream() $streamReader = [System.IO.StreamReader]::new($stream) $content = $streamReader.ReadToEnd() $claims = ($content | ConvertFrom-Json).claims # The second token acquisition should trigger MFA in the cases where it is enabled $token = $authContext.AcquireTokenAsync($ResourceAppIdUri, $clientId, $redirectUri, $platformParameters, $userIdentifier, "claims=$claims").Result } return $token } function Get-RequestHeader( [Parameter(Mandatory=$true)] [Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult]$Token) { return @{'Authorization' = $Token.CreateAuthorizationHeader()} } |