Optimized.Aza.psm1
#region main function Connect-Aza { <# .LINK https://github.com/baswijdenes/Optimized.Aza/tree/main .SYNOPSIS Connect-Aza will retreive a RefreshToken from Microsoft Graph. .DESCRIPTION By selecting one of these parameters you log on with the following: ClientSecret: Will log you on with a ClientSecret. Certificate: Will log you on with a Certificate. Thumbprint: Will search for a Certificate under thumbprint on local device and log you on with a Certificate. UserCredentials: Will log you on with basic authentication. RedirectUri: Will log you on with MFA Authentication. The OauthToken is automatically renewed when you use cmdlets. .PARAMETER Thumbprint Use a certificate thumbprint to log on with. Connec-Aza will search for the certificate in the cert store. .PARAMETER Certificate Use a Cert to log on. you can use where X's is the certificate thumbprint: $Cert = get-ChildItem 'Cert:\LocalMachine\My\XXXXXXXXXXXXXXXXXXX' Connect-Aza -Certificate $Cert -ApplicationID 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX' -Tenant 'XXXXXXXX.onmicrosoft.com' .PARAMETER ClientSecret Parameter description .PARAMETER RedirectUri Use the RedirectUri in your AzureAD app to connect with MFA. RedirectUri should look something like this: 'msalXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX://auth' If you want to know more about how to log in via MFA with a RedirectUri, go to my blog: https://bwit.blog/how-to-start-with-microsoft-graph-in-powershell/#I_will_use_credentials .PARAMETER UserCredentials Use Get-Credential to log on with Basic Authentication. .PARAMETER ApplicationID ApplicationID is the ID for the AzureAD application. It should look like this: 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX' .PARAMETER Tenant Tenant is the TenantID or onmicrosoft.com address. Don't confuse this with ApplicationID. I should look like this: 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX' Or XXXXXXX.onmicrosoft.com .PARAMETER Resource Default Resource is the Azure REST API. -Resource accepts other Azure REST APIs like the Azure Storage API: https://docs.microsoft.com/en-us/rest/api/storageservices/. Resource URL is:'https://storage.azure.com/.default'. .PARAMETER LoginScope You can only use LoginScope with RedirectUri, but unfortunately the token will always include all permissions the app has. .PARAMETER Force Use -Force when you want to overwrite another connection (or Accept the confirmation). .EXAMPLE Connect-Aza -ClientSecret '1yD3h~.KgROPO.K1sbRF~XXXXXXXXXXXXX' -ApplicationID 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX' -Tenant 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX' .EXAMPLE $Cert = get-ChildItem 'Cert:\LocalMachine\My\XXXXXXXXXXXXXXXXXXX' Connect-Aza -Certificate $Cert -ApplicationID 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX' -Tenant 'XXXXXXXX.onmicrosoft.com' .EXAMPLE Connect-Aza -Thumbprint '3A7328F1059E9802FAXXXXXXXXXXXXXX' -ApplicationID 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX' -Tenant 'XXXXXXXX.onmicrosoft.com' .EXAMPLE Connect-Aza -UserCredentials $Cred -Tenant 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX' -ApplicationID 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX' .EXAMPLE Connect-Aza -redirectUri 'msalXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX://auth' -Tenant 'XXXXXXXX.onmicrosoft.com' -ApplicationID 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX' .EXAMPLE Connect-Aza -ClientSecret '1yD3h~.KgROPO.K1sbRF~XXXXXXXXXXXXX' -ApplicationID 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX' -Tenant 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXX' -Resource 'https://storage.azure.com/.default' #> [CmdletBinding()] param ( [Parameter(Mandatory = $true, ParameterSetName = 'Thumbprint')] [ValidateScript( { $_.length -eq 40 })] [string] $Thumbprint, [Parameter(Mandatory = $true, ParameterSetName = 'Certificate')] $Certificate, [Parameter(Mandatory = $true, ParameterSetName = 'ClientSecret')] [string] $ClientSecret, [Parameter(Mandatory = $true, ParameterSetName = 'RedirectUri')] [String] $RedirectUri, [Parameter(Mandatory = $true, ParameterSetName = 'Credentials')] [System.Net.ICredentials] $UserCredentials, [Parameter(Mandatory = $true)] [String] $ApplicationID, [Parameter(Mandatory = $true)] [String] $Tenant, [Parameter(Mandatory = $false)] [string] $Resource = 'https://management.azure.com/.default', [Parameter(Mandatory = $false)] [Switch] $Force ) begin { if ($Force) { Write-Verbose 'Connect-Aza: -Force parameter found. Running Disconnect-Aza to force a log on.' $null = Disconnect-Aza } else { Initialize-AzaConnect } } process { if ($Thumbprint) { Write-Verbose "Connect-Aza: Thumbprint: Logging in with Thumbprint." Receive-AzaOauthToken ` -ApplicationID $ApplicationID ` -Tenant $Tenant ` -Thumbprint $Thumbprint ` -Resource $Resource } elseif ($Certificate) { Write-Verbose "Connect-Aza: Certificate: Logging in with certificate." Receive-AzaOauthToken ` -ApplicationID $ApplicationID ` -Tenant $Tenant ` -Certificate $Certificate ` -Resource $Resource } elseif ($ClientSecret) { Write-Verbose "Connect-Aza: ClientSecret: Logging in with ClientSecret." Receive-AzaOauthToken ` -ApplicationID $ApplicationID ` -Tenant $Tenant ` -ClientSecret $ClientSecret ` -Resource $Resource } elseif ($RedirectUri) { Write-Verbose "Connect-Aza: MFA UserCredentials: Logging in with MFA UserCredentials." Receive-AzaOauthToken ` -ApplicationID $ApplicationID ` -Tenant $Tenant ` -RedirectUri $RedirectUri ` -Resource $Resource } elseif ($UserCredentials) { Write-Verbose "Connect-Aza: Basic UserCredentials: Logging in with Basic UserCredentials." Receive-AzaOauthToken ` -ApplicationID $ApplicationID ` -Tenant $Tenant ` -UserCredentials $UserCredentials ` -Resource $Resource } } end { return "You've successfully logged in to $Resource." } } function Disconnect-Aza { <# .LINK https://github.com/baswijdenes/Optimized.Aza/tree/main .SYNOPSIS Use this to log off Azure Service Management API. .DESCRIPTION To update the OauthToken I fill the global scope with a number of properties. The properties are emptied by Disconnect-Aza. .EXAMPLE Disconnect-Aza #> [CmdletBinding()] param ( ) begin { if ($global:AzaLoginType.length -ge 1) { Write-Verbose "Disconnect-Aza: Disconnecting from $global:AzaResource." } } process { try { $Null = Get-Variable -Name "Aza*" -Scope Global | Remove-Variable -Force -Scope Global } catch { throw $_.Exception.Message } } end { return "You've successfully logged out." } } function Get-Aza { <# .LINK https://github.com/baswijdenes/Optimized.Aza/tree/main .SYNOPSIS Get-Aza speaks for itself. All you have to provide is the URL. .DESCRIPTION You can grab the URL via the browser developer tools, Fiddler, or from the Azure Service Management API docs. It will automatically use the Next Link when there is one in the returned request. .PARAMETER URL The URL to get data from Microsoft Graph. .PARAMETER Once If you only want to retrieve data once, you can use the -Once parameter. .PARAMETER CustomHeader Use -CustomHeader to add extra headers, after the cmdlet ran it will convert back to original header. .EXAMPLE Get-Aza -URL 'https://management.azure.com/subscriptions/81bdb7e0-2010-4c36-ba35-71c560e3b317/resourceGroups/RG-2019/providers/Microsoft.Automation/automationAccounts/AA-2019-01/runbooks/POST-DC-2019-01?api-version=2015-10-31' #> [CmdletBinding()] param ( [Parameter(Mandatory = $true, Position = 0)] [string] $URL, [Parameter(Mandatory = $false)] [switch] $Once, [Parameter(Mandatory = $false)] [object] $CustomHeader ) begin { Update-AzaOauthToken if ($CustomHeader) { Enable-AzaCustomHeader -CustomHeader $CustomHeader } } process { try { Write-Verbose "Get-Aza: Getting results from $URL." $Result = Invoke-WebRequest -UseBasicParsing -Headers $global:AzaHeaderParameters -Uri $URL -Method get if ($result.Headers.'Content-Type' -like "application/octet-stream*") { Write-Verbose "Get-Aza: Result is in Csv format. Converting to Csv and returning end result." $EndResult = ConvertFrom-Csv -InputObject $Result } elseif ($result.Headers.'Content-Type' -like "application/xml*") { Write-Verbose "Get-Aza: Result is in XML format. Converting to XML and returning end result." $Content = $Result.Content.Substring(3, $Response.Content.Length - 3) $XmlToJSon = [Newtonsoft.Json.JsonConvert]::SerializeXmlNode([xml]$Content) $JsonToObject = ConvertFrom-Json -InputObject $XmlToJson Write-Warning 'Get-Mga: To see different data types (Object, JSON, XML, Original) use $global:AzaDataType.' $global:AzaDataType = [PSCustomObject]@{ Object = $JsonToObject JSON = $XmlToJSon XML = $Content OG = $Result } $Result = $JsonToObject | Format-Custom -Depth 1000000000 $EndResult = $Result } elseif ($result.Headers.'Content-Type' -like "application/json*") { Write-Verbose "Get-Aza: Result is in JSON format. Converting to JSON." $Result = ConvertFrom-Json -InputObject $Result if ($Result.'@odata.nextLink') { if (!($Once)) { Write-Verbose "Get-Aza: There is an @odata.nextLink for more output. We will run Get-Aza again with the next data link." $EndResult = @() foreach ($Line in ($Result).value) { $EndResult += $Line } While ($Result.'@odata.nextLink') { Write-Verbose "Get-Aza: There is another @odata.nextLink for more output. We will run Get-Aza again with the next data link." Update-AzaOauthToken $Result = (Invoke-WebRequest -UseBasicParsing -Headers $global:AzaHeaderParameters -Uri $Result.'@odata.nextLink' -Method Get).Content | ConvertFrom-Json foreach ($Line in ($Result).value) { $EndResult += $Line } Write-Verbose "Get-Aza: Count is: $($EndResult.count)." } } else { $EndResult = @() foreach ($Line in ($Result).value) { $EndResult += $Line } Write-Verbose 'Get-Aza: Parameter -Once found. Even if there is an @odata.nextLink for more output, we will not extract more data.' } } elseif ($Result.value) { Write-Verbose "Get-Aza: There is no @odata.nextLink. We will add the data to end result." $EndResult = $Result.value } else { Write-Verbose "Get-Aza: There is no @odata.nextLink. We will add the data to end result." $EndResult = $Result } } else { $EndResult = $Result throw "Result is in an unrecognizable format: $($Result.Headers)." } } catch [System.Net.WebException] { Write-Warning "WebException Error message! This could be due to throttling limit." $WebResponse = $_.Exception.Response if ($WebResponse.StatusCode -eq 429) { [int]$RetryValue = $WebResponse.Headers['Retry-After'] Write-Warning "WebException Error message! Throttling error. Retry-After header value: $($RetryValue) seconds. Sleeping for $($RetryValue + 1)s" Start-Sleep -Seconds $($RetryValue + 1) if ($Result.'@odata.nextLink') { Get-Aza -URL $Result.'@odata.nextLink' } else { Get-Aza -URL $URL } } else { throw $_.Exception.Message } } catch { if ($CustomHeader) { Disable-AzaCustomHeader } throw $_.Exception.Message } } end { if ($CustomHeader) { Disable-AzaCustomHeader } return $EndResult } } function Post-Aza { <# .LINK https://github.com/baswijdenes/Optimized.Aza/tree/main .SYNOPSIS Post-Aza can be seen as the 'new' Verb. With this cmdlet you can create objects in Azure. .PARAMETER URL URL to 'POST' to. .PARAMETER InputObject -InputObject will accept a PSObject or JSON. .PARAMETER CustomHeader Use -CustomHeader to add extra headers, after the cmdlet ran it will convert back to original header. .PARAMETER Put Use the -Put switch when the method is a Put instead of Post. .PARAMETER KeepFormat By default the InputObject is converted to JSON. With the -KeepFormat switch it will keep the original format. .EXAMPLE Post-Aza ` -URL 'https://management.azure.com/subscriptions/81bdb7e0-2010-4c36-ba35-71c560e3b317/resourceGroups/RG-2019/providers/Microsoft.Automation/automationAccounts/AA-2019-01/runbooks/New-PUT-PSScript?api-version=2015-10-31' ` -InputObject $InputObject ` -Put #> [CmdletBinding()] param ( [Parameter(Mandatory = $true, Position = 0)] [string] $URL, [Parameter(Mandatory = $false)] [object] $InputObject, [Parameter(Mandatory = $false)] [switch] $Put, [Parameter(Mandatory = $false)] [object] $CustomHeader, [Parameter(Mandatory = $false)] [switch] $KeepFormat ) begin { Update-AzaOauthToken if ($KeepFormat -ne $true) { $InputObject = ConvertTo-AzaJson -InputObject $InputObject } else { Write-verbose 'Post-Aza: begin: KeepFormat switch found. Data will not be converted to JSON.' } if ($CustomHeader) { Enable-AzaCustomHeader -CustomHeader $CustomHeader } } process { try { if ($InputObject) { if (!($Put -eq $true)) { Write-Verbose "Post-Aza: Posting InputObject to $global:AzaResource." $Result = Invoke-RestMethod -Uri $URL -Headers $global:AzaHeaderParameters -Method post -Body $InputObject -ContentType application/json } else { Write-Verbose "Post-Aza: Putting InputObject to $global:AzaResource." $Result = Invoke-RestMethod -Uri $URL -Headers $global:AzaHeaderParameters -Method put -Body $InputObject -ContentType application/json } } else { if (!($Put -eq $true)) { $Result = Invoke-RestMethod -Uri $URL -Headers $global:AzaHeaderParameters -Method post -ContentType application/json } else { $Result = Invoke-RestMethod -Uri $URL -Headers $global:AzaHeaderParameters -Method put -ContentType application/json } } } catch [System.Net.WebException] { Write-Warning "WebException Error message! This could be due to throttling limit." $WebResponse = $_.Exception.Response if ($WebResponse.StatusCode -eq 429) { [int]$RetryValue = $WebResponse.Headers['Retry-After'] Write-Warning "WebException Error message! Throttling error. Retry-After header value: $($RetryValue) seconds. Sleeping for $($RetryValue + 1)s" Start-Sleep -Seconds $($RetryValue + 1) $Result = Post-Aza -URL $URL -InputObject $InputObject } else { if ($CustomHeader) { Disable-AzaCustomHeader } throw $_.Exception.Message } } catch { if ($CustomHeader) { Disable-AzaCustomHeader } throw $_.Exception.Message } } end { if ($CustomHeader) { Disable-AzaCustomHeader } Write-Verbose "Post-Aza: We've successfully Posted the data to $global:AzaResource." return $Result } } function Put-Aza { <# .LINK https://github.com/baswijdenes/Optimized.Aza/tree/main .SYNOPSIS Put-Aza can be seen as the 'new' Verb. With this cmdlet you can create objects in Azure. .PARAMETER URL URL to 'PUT' to. .PARAMETER InputObject -InputObject will accept a PSObject or JSON. .PARAMETER CustomHeader Use -CustomHeader to add extra headers, after the cmdlet ran it will convert back to original header. .PARAMETER KeepFormat By default the InputObject is converted to JSON. With the -KeepFormat switch it will keep the original format. .EXAMPLE $CustomHeader = @{ 'x-ms-blob-type' = 'BlockBlob' 'content-type' = 'application/octet-stream' } $StorageAccount = 'baswijdenes' $Container = 'testblob' $Blob = 'cert.cer' #$Test = Get-Content C:\Temp\10days.cer -Raw $test = [System.IO.File]::OpenRead('C:\Temp\10days.cer') $URL = 'https://{0}.blob.core.windows.net/{1}/{2}' -f $StorageAccount, $Container, $blob Put-Aza -URL $URL -CustomHeader $CustomHeader -InputObject $test -KeepFormat -Verbose #> [CmdletBinding()] param ( [Parameter(Mandatory = $true, Position = 0)] [string] $URL, [Parameter(Mandatory = $false)] [object] $InputObject, [Parameter(Mandatory = $false)] [object] $CustomHeader, [Parameter(Mandatory = $false)] [switch] $KeepFormat ) begin { Write-Verbose 'Put-Aza: begin: Put-Aza uses Post-Aza in the backend.' $Splatting = @{ URL = $URL Put = $true } if ($InputObject) { $Splatting.Add('InputObject', $InputObject) } if ($CustomHeader) { $Splatting.Add('CustomHeader', $CustomHeader) } if ($KeepFormat) { $Splatting.Add('KeepFormat', $KeepFormat) } } process { try { Post-Aza @Splatting <# if ($InputObject) { if ($CustomHeader) { $Result = Post-Aza -URL $URL -InputObject $InputObject -put -CustomHeader $CustomHeader } else { $Result = Post-Aza -URL $URL -InputObject $InputObject -put } } else { if ($CustomHeader) { $Result = Post-Aza -URL $URL -Put -CustomHeader $CustomHeader } else { $Result = Post-Aza -URL $URL -Put } } #> } catch { throw $_.Exception.Message } } end { return $Result } } function Patch-Aza { <# .LINK https://github.com/baswijdenes/Optimized.Aza/tree/main .SYNOPSIS Patch-Aza can be seen as the 'Update' Verb. In the below example I change the description of a runbook in Azure Automation. .PARAMETER URL URL to 'PATCH' to. .PARAMETER InputObject -InputObject will accept a PSObject or JSON. .PARAMETER CustomHeader Use -CustomHeader to add extra headers, after the cmdlet ran it will convert back to original header. .EXAMPLE $InputObject = [PSCustomObject]@{ properties = [PSCustomObject]@{ description = 'Update description with Patch-Aza' } } Patch-Aza ` -URL 'https://management.azure.com/subscriptions/81bdb7e0-2010-4c36-ba35-71c560e3b317/resourceGroups/RG-2019/providers/Microsoft.Automation/automationAccounts/AA-2019-01/runbooks/New-PUT-PSScript?api-version=2015-10-31' ` -InputObject $InputObject #> [CmdletBinding()] param ( [Parameter(Mandatory = $true, Position = 0)] [string] $URL, [Parameter(Mandatory = $true)] [object] $InputObject, [Parameter(Mandatory = $false)] [object] $CustomHeader ) begin { Update-AzaOauthToken $ValidateJson = ConvertTo-AzaJson -InputObject $InputObject -Validate if ($CustomHeader) { Enable-AzaCustomHeader -CustomHeader $CustomHeader } } process { try { $InputObject = ConvertTo-AzaJson -InputObject $InputObject Write-Verbose "Patch-Aza: Patching InputObject to $global:AzaResource." $Result = Invoke-RestMethod -Uri $URL -Headers $global:AzaHeaderParameters -Method Patch -Body $InputObject -ContentType application/json } catch [System.Net.WebException] { Write-Warning "WebException Error message! This could be due to throttling limit." $WebResponse = $_.Exception.Response if ($WebResponse.StatusCode -eq 429) { [int]$RetryValue = $WebResponse.Headers['Retry-After'] Write-Warning "WebException Error message! Throttling error. Retry-After header value: $($RetryValue) seconds. Sleeping for $($RetryValue + 1)s" Start-Sleep -Seconds $($RetryValue + 1) $Result = Patch-Aza -URL $URL -InputObject $InputObject } else { throw $_.Exception.Message } } catch { if ($CustomHeader) { Disable-AzaCustomHeader } throw $_.Exception.Message } } end { if ($CustomHeader) { Disable-AzaCustomHeader } Write-Verbose "Patch-Aza: We've successfully Patched the data to $global:AzaResource." return $Result } } function Delete-Aza { <# .LINK https://github.com/baswijdenes/Optimized.Aza/tree/main .SYNOPSIS Delete speaks for itself. With this cmdlet you can remove objects from Azure. .PARAMETER URL -URL is the URL for the item to delete. .PARAMETER InputObject -InputObject will accept a PSObject or JSON. .PARAMETER CustomHeader Use -CustomHeader to add extra headers, after the cmdlet ran it will convert back to original header. .EXAMPLE Delete-Aza ` -URL 'https://management.azure.com/subscriptions/81bdb7e0-2010-4c36-ba35-71c560e3b317/resourceGroups/RG-2019/providers/Microsoft.Automation/automationAccounts/AA-2019-01/runbooks/New-PUT-PSScript?api-version=2015-10-31' #> [CmdletBinding()] param ( [Parameter(Mandatory = $true, Position = 0)] [string] $URL, [Parameter(Mandatory = $false)] [string] $InputObject, [Parameter(Mandatory = $false)] [object] $CustomHeader ) begin { Update-AzaOauthToken if ($CustomHeader) { Enable-AzaCustomHeader -CustomHeader $CustomHeader } } process { try { if ($InputObject) { Write-Verbose "Delete-Aza: Deleting InputObject on $URL to $global:AzaResource." $InputObject = ConvertTo-AzaJson -InputObject $InputObject $Result = Invoke-RestMethod -Uri $URL -body $InputObject -Headers $global:AzaHeaderParameters -Method Delete -ContentType application/json } else { Write-Verbose "Delete-Aza: Deleting conent on $URL to $global:AzaResource." $Result = Invoke-RestMethod -Uri $URL -Headers $global:AzaHeaderParameters -Method Delete -ContentType application/json } } catch [System.Net.WebException] { Write-Warning "WebException Error message! This could be due to throttling limit." $WebResponse = $_.Exception.Response if ($WebResponse.StatusCode -eq 429) { [int]$RetryValue = $WebResponse.Headers['Retry-After'] Write-Warning "WebException Error message! Throttling error. Retry-After header value: $($RetryValue) seconds. Sleeping for $($RetryValue + 1)s" Start-Sleep -Seconds $($RetryValue + 1) if ($InputObject) { $Result = Delete-Aza -URL $URL -InputObject $InputObject } else { $Result = Delete-Aza -URL $URL } } else { throw $_.Exception.Message } } catch { if ($CustomHeader) { Disable-AzaCustomHeader } throw $_.Exception.Message } } end { if ($CustomHeader) { Disable-AzaCustomHeader } Write-Verbose "Delete-Aza: We've successfully deleted the data on $global:AzaResource." return $Result } } #endregion main #region internal function Initialize-AzaConnect { [CmdletBinding()] param ( ) if ($global:AzaLoginType.length -ge 1) { Write-Verbose "Initialize-AzaConnect: You're already logged on." $Confirmation = Read-Host 'You already logged on. Are you sure you want to proceed? Type (Y)es to continue.' if (($Confirmation -eq 'y') -or ($Confirmation -eq 'yes') -or ($Confirmation -eq 'true') -or ($Confirmation -eq '(Y)es')) { Write-Verbose "Initialize-AzaConnect: We will continue logging in." $null = Disconnect-Aza } else { Write-Verbose "Initialize-AzaConnect: Aborting log in." throw 'Login aborted.' } } } function Update-AzaOauthToken { [CmdletBinding()] param ( ) if ($null -ne $global:AzaAppPass) { Receive-AzaOauthToken ` -ApplicationID $global:AzaApplicationID ` -Tenant $global:AzaTenant ` -ClientSecret $global:AzaSecret ` -Resource $($global:AzaResource) } elseif ($null -ne $global:AzaCert) { Receive-AzaOauthToken ` -ApplicationID $global:AzaApplicationID ` -Tenant $global:AzaTenant ` -Certificate $global:AzaCertificate ` -Resource $($global:AzaResource) } elseif ($null -ne $global:AzaTPrint) { Receive-AzaOauthToken ` -ApplicationID $global:AzaApplicationID ` -Tenant $global:AzaTenant ` -Thumbprint $global:AzaThumbprint ` -Resource $($global:AzaResource) } elseif ($null -ne $global:AzaRU) { Receive-AzaOauthToken ` -ApplicationID $global:AzaApplicationID ` -Tenant $global:AzaTenant ` -RedirectUri $global:AzaRedirectUri ` -Resource $($global:AzaResource) } elseif ($null -ne $global:AzaBasic) { Receive-AzaOauthToken ` -ApplicationID $global:AzaApplicationID ` -Tenant $global:AzaTenant ` -UserCredentials $global:AzaUserCredentials ` -Resource $($global:AzaResource) } else { Throw "You need to run Connect-Aza before you can continue. Exiting script..." } } function Receive-AzaOauthToken { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string] $ApplicationID, [Parameter(Mandatory = $true)] [string] $Tenant, [Parameter(Mandatory = $true, ParameterSetName = 'Thumbprint')] [string] $Thumbprint, [Parameter(Mandatory = $true, ParameterSetName = 'Certificate')] $Certificate, [Parameter(Mandatory = $true, ParameterSetName = 'ClientSecret')] $ClientSecret, [Parameter(Mandatory = $true, ParameterSetName = 'Redirecturi')] [string] $RedirectUri, [Parameter(Mandatory = $true)] $Resource, [Parameter(Mandatory = $true, ParameterSetName = 'UserCredentials')] [System.Net.ICredentials] $UserCredentials ) begin { try { $global:AzaResource = $Resource [System.Collections.Generic.List[String]]$Resource = @($Resource) $global:AzaTenant = $Tenant $global:AzaApplicationID = $ApplicationID [datetime]$UnixDateTime = '1970-01-01 00:00:00' $Date = Get-Date $UTCDate = [System.TimeZoneInfo]::ConvertTimeToUtc($Date) if ($thumbprint.length -gt 5) { Write-Verbose "Receive-AzaOauthToken: Certificate: We will continue logging in with Certificate." if (($null -eq $global:AzaTPCertificate) -or ($Thumbprint -ne ($global:AzaTPCertificate).Thumbprint)) { Write-Verbose "Receive-AzaOauthToken: Certificate: Starting search in CurrentUser\my." $TPCertificate = Get-Item Cert:\CurrentUser\My\$Thumbprint -ErrorAction SilentlyContinue if ($null -eq $TPCertificate) { Write-Verbose "Receive-AzaOauthToken: Certificate not found in CurrentUser. Continuing in LocalMachine\my." $TPCertificate = Get-Item Cert:\localMachine\My\$Thumbprint -ErrorAction SilentlyContinue } if ($null -eq $TPCertificate) { throw "We did not find a certificate under: $Thumbprint. Exiting script..." } } else { $TPCertificate = $global:AzaTPCertificate Write-Verbose "Receive-AzaOauthToken: Certificate: We already obtained a certificate from a previous login. We will continue logging in." } } } catch { throw $_.Exception.Message } } process { try { if ($ClientSecret) { if ($clientsecret.gettype().name -ne 'securestring') { $Secret = $ClientSecret | ConvertTo-SecureString -AsPlainText -Force } else { $Secret = $ClientSecret } $TempPass = [PSCredential]::new(".", $Secret).GetNetworkCredential().Password if (!($global:AzaAppPass)) { Write-Verbose "Receive-AzaOauthToken: ApplicationSecret: This is the first time logging in with a ClientSecret." $Builder = [Microsoft.Identity.Client.ConfidentialClientApplicationBuilder]::Create($ApplicationID).WithTenantId($Tenant).WithClientSecret($TempPass).Build() $global:AzaAppPass = $Builder.AcquireTokenForClient($Resource).ExecuteAsync() if ($null -eq $global:AzaAppPass.result.AccessToken) { throw 'We did not retrieve an Oauth access token to continue script. Exiting script...' } else { if (($($Resource) -like "*storage.azure.com*") -or ($($Resource) -like "*blob.core.windows.net*")) { Write-Verbose "Receive-OauthToken: ApplicationSecret: Resource is $Resource. Creating the header parameter." $global:AzaHeaderParameters = @{ Authorization = $global:AzaAppPass.result.CreateAuthorizationHeader() 'x-ms-version' = '2019-02-02' 'x-ms-date' = $([datetime]::UtcNow.ToString('R')) Accept = 'application/xml;charset=utf8' } } else { $global:AzaHeaderParameters = @{ Authorization = $global:AzaAppPass.result.CreateAuthorizationHeader() } } $global:AzaLoginType = 'ClientSecret' $global:AzaSecret = $Secret } } else { Write-Verbose "Receive-AzaOauthToken: ApplicationSecret: Oauth token already exists from previously running cmdlets." Write-Verbose "Receive-AzaOauthToken: ApplicationSecret: Running test to see if Oauth token expired." $OauthExpiryTime = $global:AzaAppPass.Result.ExpiresOn.UtcDateTime if ($OauthExpiryTime -le $UTCDate) { Write-Verbose "Receive-AzaOauthToken: ApplicationSecret: Oauth token expired. Emptying Oauth variable and re-running function." $global:AzaAppPass = $null Receive-AzaOauthToken ` -ApplicationID $ApplicationID ` -Tenant $Tenant ` -ClientSecret $ClientSecret } else { Write-Verbose "Receive-AzaOauthToken: ApplicationSecret: Oauth token from last run is still active." } } } elseif ($Certificate) { if (!($global:AzaCert)) { Write-Verbose "Receive-AzaOauthToken: Certificate: This is the first time logging in with a Certificate." $Builder = [Microsoft.Identity.Client.ConfidentialClientApplicationBuilder]::Create($ApplicationID).WithTenantId($tenant).WithCertificate($Certificate).Build() $global:AzaCert = $Builder.AcquireTokenForClient($Resource).ExecuteAsync() if ($null -eq $global:AzaCert.result.AccessToken) { throw 'We did not retrieve an Oauth access token to continue script. Exiting script...' } else { $global:AzaHeaderParameters = @{ Authorization = $global:AzaCert.result.CreateAuthorizationHeader() } $global:AzaLoginType = 'Certificate' $global:AzaCertificate = $Certificate } } else { Write-Verbose "Receive-AzaOauthToken: Certificate: Oauth token already exists from previously running cmdlets." Write-Verbose "Receive-AzaOauthToken: Certificate: Running test to see if Oauth token expired." $OauthExpiryTime = $global:AzaCert.Result.ExpiresOn.UtcDateTime if ($OauthExpiryTime -le $UTCDate) { Write-Verbose "Receive-AzaOauthToken: Certificate: Oauth token expired. Emptying Oauth variable and re-running function." $global:AzaCert = $null Receive-AzaOauthToken ` -ApplicationID $ApplicationID ` -Certificate $Certificate ` -Tenant $Tenant } else { Write-Verbose "Receive-AzaOauthToken: Certificate: Oauth token from last run is still active." } } } elseif ($Thumbprint) { if (!($global:AzaTPrint)) { Write-Verbose "Receive-AzaOauthToken: Certificate: This is the first time logging in with a Certificate." $Builder = [Microsoft.Identity.Client.ConfidentialClientApplicationBuilder]::Create($ApplicationID).WithTenantId($tenant).WithCertificate($TPCertificate).Build() $global:AzaTPrint = $Builder.AcquireTokenForClient($Resource).ExecuteAsync() if ($null -eq $global:AzaTPrint.result.AccessToken) { throw 'We did not retrieve an Oauth access token to continue script. Exiting script...' } else { $global:AzaHeaderParameters = @{ Authorization = $global:AzaTPrint.result.CreateAuthorizationHeader() } $global:AzaLoginType = 'Thumbprint' $global:AzaThumbprint = $Thumbprint $global:AzaTPCertificate = $TPCertificate } } else { Write-Verbose "Receive-AzaOauthToken: Certificate: Oauth token already exists from previously running cmdlets." Write-Verbose "Receive-AzaOauthToken: Certificate: Running test to see if Oauth token expired." $OauthExpiryTime = $global:AzaTPrint.Result.ExpiresOn.UtcDateTime if ($OauthExpiryTime -le $UTCDate) { Write-Verbose "Receive-AzaOauthToken: Certificate: Oauth token expired. Emptying Oauth variable and re-running function." $global:AzaTPrint = $null Receive-AzaOauthToken ` -ApplicationID $ApplicationID ` -Thumbprint $Thumbprint ` -Tenant $Tenant } else { Write-Verbose "Receive-AzaOauthToken: Certificate: Oauth token from last run is still active." } } } elseif ($RedirectUri) { if (!($global:AzaRU)) { $Builder = [Microsoft.Identity.Client.PublicClientApplicationBuilder]::Create($ApplicationID).WithTenantId($Tenant).WithRedirectUri($RedirectUri).Build() $global:AzaRU = $Builder.AcquireTokenInteractive($Resource).ExecuteAsync() if ($null -eq $global:AzaRU.result.AccessToken) { throw 'We did not retrieve an Oauth access token to continue script. Exiting script...' } else { $global:AzaHeaderParameters = @{ Authorization = $global:AzaRU.Result.CreateAuthorizationHeader() } $global:AzaLoginType = 'RedirectUri' $global:AzaRedirectUri = $RedirectUri } } else { Write-Verbose "Receive-AzaOauthToken: MFA UserCredentials: Oauth token already exists from previously running cmdlets." Write-Verbose "Receive-AzaOauthToken: MFA UserCredentials: Running test to see if Oauth token expired." $OauthExpiryTime = $global:AzaRU.Result.ExpiresOn.UtcDateTime if ($OauthExpiryTime -le $UTCDate) { Write-Verbose "Receive-AzaOauthToken: MFA UserCredentials: Oauth token expired. Emptying Oauth variable and re-running function." $global:AzaRU = $null Receive-AzaOauthToken ` -ApplicationID $ApplicationID ` -Tenant $Tenant ` -RedirectUri $RedirectUri } else { Write-Verbose "Receive-AzaOauthToken: MFA UserCredentials: Oauth token from last run is still active." } } } elseif ($userCredentials) { $loginURI = "https://login.microsoft.com" if ($Resource -like "*.default*") { $Resource = $Resource.Replace('.default', '') } $Body = @{ grant_type = 'password' resource = $($Resource) username = $($userCredentials.UserName) password = $($UserCredentials.Password) client_id = $ApplicationID } if (!($global:AzaBasic)) { $global:AzaBasic = Invoke-RestMethod -Method Post -Uri $loginURI/$Tenant/oauth2/token?api-version=1.0 -Body $Body -UseBasicParsing if ($null -eq $global:AzaBasic.access_token) { throw 'We did not retrieve an Oauth access token to continue script. Exiting script...' } else { $global:AzaHeaderParameters = @{ Authorization = "$($global:AzaBasic.token_type) $($global:AzaBasic.access_token)" } $global:AzaLoginType = 'UserCredentials' $global:AzaUserCredentials = $UserCredentials } } else { Write-Verbose "Receive-AzaOauthToken: Basic UserCredentials: Oauth token already exists from previously running cmdlets." Write-Verbose "Receive-AzaOauthToken: Basic UserCredentials: Running test to see if Oauth token expired." $OauthExpiryTime = $UnixDateTime.AddSeconds($global:AzaBasic.expires_on) if ($null -ne $global:AzaBasic.refresh_token) { Write-Verbose "Receive-AzaOauthToken: Using the refresh token to get a new Oauth Token." $Body = @{ refresh_token = $global:AzaBasic.refresh_token grant_type = 'refresh_token' } $global:AzaBasic = Invoke-RestMethod -Method Post -Uri $loginURI/$Tenant/oauth2/token?api-version=1.0 -Body $Body -UseBasicParsing if ($null -eq $global:AzaBasic.access_token) { Write-Warning 'We did not retrieve an Oauth access token from the refresh_token. Re-trying to log in with new token.' } else { $global:AzaHeaderParameters = @{ Authorization = "$($global:AzaBasic.token_type) $($global:AzaBasic.access_token)" } $global:AzaLoginType = 'UserCredentials' $global:AzaUserCredentials = $UserCredentials } } if ($OauthExpiryTime -le $UTCDate) { $global:AzaBasic = $null Receive-AzaOauthToken ` -UserCredentials $UserCredentials ` -Tenant $Tenant ` -ApplicationID $ApplicationID ` -Resource $Resource } else { Write-Verbose "Receive-AzaOauthToken: Basic UserCredentials: Oauth token from last run is still active." } } } } catch { throw $_.Exception.Message } } end { } } function ConvertTo-AzaJson { [CmdletBinding()] param ( [Parameter(Mandatory = $false)] $InputObject, [Parameter(Mandatory = $false)] [switch] $Validate ) begin { } process { try { $null = ConvertFrom-Json -InputObject $InputObject -ErrorAction Stop $ValidateJson = $true } catch { if ($Validate -ne $true) { $InputObject = ConvertTo-Json -InputObject $InputObject -Depth 100 } else { $ValidateJson = $false } } } end { if ($Validate -ne $true) { return $InputObject } else { return $ValidateJson } } } function Enable-AzaCustomHeader { [CmdletBinding()] param ( $CustomHeader ) begin { Write-Verbose 'Enable-AzaCustomHeader: begin: saving original header.' $global:AzaOriginalHeader = @{} foreach ($Header in $global:AzaHeaderParameters.GetEnumerator()) { $global:AzaOriginalHeader.Add($Header.Key, $Header.Value) } } process { Write-Verbose 'Enable-AzaCustomHeader: begin: Merging headers.' # $global:AzaHeaderParameters = $global:AzaOriginalHeader + $CustomHeader foreach ($Header in $CustomHeader.GetEnumerator()) { try { if ($null -ne $global:AzaHeaderParameters[$Header.Key]) { $global:AzaHeaderParameters[$item.Key] = $Header.Value } else { $global:AzaHeaderParameters.Add($Header.key, $Header.Value) } } catch { throw $_.Exception.Message } } } end { Write-Verbose 'Enable-AzaCustomHeader: end: CustomHeader created.' } } function Disable-AzaCustomHeader { [CmdletBinding()] param ( ) begin { Write-Verbose 'Disable-AzaCustomHeader: begin: Changing back to original header.' } process { try { if ($global:AzaHeaderParameters -ne $global:AzaOriginalHeader) { Write-Verbose 'Disable-AzaCustomHeader: process: Reverting header.' $global:AzaHeaderParameters = @{} $global:AzaHeaderParameters += $global:AzaOriginalHeader Remove-Variable -Name 'AzaOriginalHeader' -Scope Global } else { Write-Verbose "Disable-AzaCustomHeader: process: Header is already original header." } } catch { throw 'Something went wrong with reverting back to original header. Re-login with Connect-Aza to continue.' } } end { Write-Verbose 'Disable-AzaCustomHeader: end: Header changed back to original header.' } } #endregion internal |