AdoAzureHelper.psm1
|
# Private module-scope variables. $script:jsonContentType = "application/json;charset=utf-8" $script:formContentType = "application/x-www-form-urlencoded;charset=utf-8" $script:certificateAccessToken = $null $script:defaultEnvironmentMSALAuthUri = "https://login.microsoftonline.com/" # MsalInstance $script:msalClientInstance = $null # Connection Types $certificateConnection = 'Certificate' $usernameConnection = 'UserNamePassword' $spnConnection = 'ServicePrincipal' $MsiConnection = 'ManagedServiceIdentity' $wifConnection = 'WorkloadIdentityFederation' # API-Version(s) $apiVersion = "2014-04-01" $azureStackapiVersion = "2015-06-15" # Constants $azureStack = "AzureStack" # Override the DebugPreference. if ($global:DebugPreference -eq 'Continue') { Write-Verbose '$OVERRIDING $global:DebugPreference from ''Continue'' to ''SilentlyContinue''.' $global:DebugPreference = 'SilentlyContinue' } function Print-MSALObsoleteMessage { [CmdletBinding()] param() process { $callerName = (Get-PSCallStack)[1].Command Write-Host "INFO: The command '$callerName' is obsolete. We are updating the tasks to use 'Get-AccessTokenMSAL' instead. No action needed on end users." } } function Get-AzureUri { param([object] [Parameter(Mandatory = $true)] $endpoint) $url = $endpoint.url if ($url -ne $null -and $url[-1] -eq '/') { return $url.Substring(0, $url.Length - 1) } return $url } function Get-AzureActiverDirectoryResourceId { param([object] [Parameter(Mandatory = $true)] $endpoint) $activeDirectoryResourceid = $null; if (($endpoint.Data.Environment) -and ($endpoint.Data.Environment -eq $azureStack)) { if (!$endpoint.Data.ActiveDirectoryServiceEndpointResourceId) { $endpoint = Add-AzureStackDependencyData -Endpoint $endpoint } $activeDirectoryResourceid = $endpoint.Data.ActiveDirectoryServiceEndpointResourceId } else { $activeDirectoryResourceid = $endpoint.url if ($activeDirectoryResourceid -ne $null -and $activeDirectoryResourceid[-1] -ne '/') { $activeDirectoryResourceid = $activeDirectoryResourceid + "/" } } return $activeDirectoryResourceid } # Check if Azure connection type is classic or not. function IsLegacyAzureConnection { param([Parameter(Mandatory = $true)] $connectionType) Write-Verbose "Connection type used is $connectionType" if ($connectionType -eq $certificateConnection -or $connectionType -eq $usernameConnection) { return $true } else { return $false } } # Check if Azure connection is RM type or not. function IsAzureRmConnection { param([Parameter(Mandatory = $true)] $connectionType) Write-Verbose "Connection type used is $connectionType" switch ($connectionType) { $spnConnection { $true } $MsiConnection { $true } $wifConnection { $true } Default { $false } } } # Get connection Type function Get-ConnectionType { param([Object] [Parameter(Mandatory = $true)] $serviceEndpoint) $connectionType = $serviceEndpoint.Auth.Scheme Write-Verbose "Connection type used is $connectionType" return $connectionType } function Get-EnvironmentAuthUrl { [CmdletBinding()] param( [Parameter(Mandatory = $true)] $endpoint, [Parameter(Mandatory = $false)] $useMSAL = $false ) $envAuthUrl = if ($useMSAL) { $endpoint.Data.activeDirectoryAuthority } else { $endpoint.Data.environmentAuthorityUrl } if ([string]::IsNullOrEmpty($envAuthUrl)) { if (($endpoint.Data.Environment) -and ($endpoint.Data.Environment -eq $azureStack)) { Write-Verbose "MSAL - Get-EnvironmentAuthUrl - azureStack is used" $endpoint = Add-AzureStackDependencyData -Endpoint $endpoint $envAuthUrl = $endpoint.Data.environmentAuthorityUrl } else { Write-Verbose "MSAL - Get-EnvironmentAuthUrl - fallback is used" # fallback $envAuthUrl = if ($useMSAL) { $script:defaultEnvironmentMSALAuthUri } else { $script:defaultEnvironmentADALAuthUri } } } Write-Verbose "MSAL - Get-EnvironmentAuthUrl - endpoint=$endpoint" Write-Verbose "MSAL - Get-EnvironmentAuthUrl - useMSAL=$useMSAL" Write-Verbose "MSAL - Get-EnvironmentAuthUrl - envAuthUrl=$envAuthUrl" return $envAuthUrl } <# Adds Azure Stack environment to use with AzureRM command-lets when targeting Azure Stack #> function Add-AzureStackDependencyData { param ( [Parameter(mandatory = $true, HelpMessage = "The Admin ARM endpoint of the Azure Stack Environment")] $endpoint ) $EndpointURI = $endpoint.Url.TrimEnd("/") $Domain = "" try { $uriendpoint = [System.Uri] $EndpointURI $i = $EndpointURI.IndexOf('.') $Domain = ($EndpointURI.Remove(0, $i + 1)).TrimEnd('/') } catch { Write-Error "The specified Azure Resource Manager endpoint is invalid" } $ResourceManagerEndpoint = $EndpointURI $stackdomain = $Domain $AzureKeyVaultDnsSuffix = "vault.$($stackdomain)".ToLowerInvariant() $AzureKeyVaultServiceEndpointResourceId = $("https://vault.$stackdomain".ToLowerInvariant()) $StorageEndpointSuffix = ($stackdomain).ToLowerInvariant() $azureStackEndpointUri = $EndpointURI.ToString().TrimEnd('/') + "/metadata/endpoints?api-version=2015-01-01" Write-Verbose "Retrieving endpoints from the $ResourceManagerEndpoint" $endpointData = Invoke-RestMethod -Uri $azureStackEndpointUri -Method Get -ErrorAction Stop if ($endpointData) { $graphEndpoint = $endpointData.graphEndpoint $galleryEndpoint = $endpointData.galleryEndpoint $authenticationData = $endpointData.authentication; if ($authenticationData) { $loginEndpoint = $authenticationData.loginEndpoint if ($loginEndpoint) { $activeDirectoryEndpoint = $loginEndpoint.TrimEnd('/') + "/" } $audiences = $authenticationData.audiences if ($audiences.Count -gt 0) { $activeDirectoryServiceEndpointResourceId = $audiences[0] } } if ($Endpoint.Data -ne $null) { if (-not (Has-ObjectProperty $Endpoint.Data "galleryUrl")) { $Endpoint.Data | Add-Member "galleryUrl" $null } if (-not (Has-ObjectProperty $Endpoint.Data "resourceManagerUrl")) { $Endpoint.Data | Add-Member "resourceManagerUrl" $null } if (-not (Has-ObjectProperty $Endpoint.Data "activeDirectoryAuthority")) { $Endpoint.Data | Add-Member "activeDirectoryAuthority" $null } if (-not (Has-ObjectProperty $Endpoint.Data "environmentAuthorityUrl")) { $Endpoint.Data | Add-Member "environmentAuthorityUrl" $null } if (-not (Has-ObjectProperty $Endpoint.Data "graphUrl")) { $Endpoint.Data | Add-Member "graphUrl" $null } if (-not (Has-ObjectProperty $Endpoint.Data "activeDirectoryServiceEndpointResourceId")) { $Endpoint.Data | Add-Member "activeDirectoryServiceEndpointResourceId" $null } if (-not (Has-ObjectProperty $Endpoint.Data "AzureKeyVaultDnsSuffix")) { $Endpoint.Data | Add-Member "AzureKeyVaultDnsSuffix" $null } $Endpoint.Data.galleryUrl = $galleryEndpoint $Endpoint.Data.resourceManagerUrl = $ResourceManagerEndpoint $Endpoint.Data.activeDirectoryAuthority = $activeDirectoryEndpoint $Endpoint.Data.environmentAuthorityUrl = $activeDirectoryEndpoint $Endpoint.Data.graphUrl = $graphEndpoint $Endpoint.Data.activeDirectoryServiceEndpointResourceId = $activeDirectoryServiceEndpointResourceId $Endpoint.Data.AzureKeyVaultDnsSuffix = $AzureKeyVaultDnsSuffix } } else { throw "Unable to fetch Azure Stack Dependency Data." } return $Endpoint } function Has-ObjectProperty { [CmdletBinding()] param([Parameter(Mandatory = $true)] $object, [Parameter(Mandatory = $true)] $propertyName) if (Get-Member -inputobject $object -name $propertyName -Membertype Properties) { return $true } else { return $false } } # Get access token - Main Point function Get-AzureRMAccessToken { [CmdletBinding()] param( [Parameter(Mandatory = $true)] $endpoint, [string][Parameter(Mandatory=$true)] $connectedServiceNameARM, [parameter(Mandatory = $false)] $overrideResourceType = $null, [string][Parameter(Mandatory=$true)] $msalLibraryPath, [string][Parameter(Mandatory=$false)] $openSSLExePath ) $accessToken = @{ token_type = $null access_token = $null expires_on = $null } Write-Verbose "Get-AzureRMAccessToken - started - endpoint=$endpoint - scheme=$($endpoint.Auth.Scheme)" Write-Verbose "MSAL - useMSAL = $useMSAL" # ManagedIdentity - access token if ($endpoint.Auth.Scheme -eq $MsiConnection ) { Write-Verbose "MSAL - ManagedIdentity is used" $accessToken = Get-MsiAccessToken -endpoint $endpoint -retryCount 0 -timeToWait 0 -overrideResourceType $overrideResourceType } # MSAL - access token else { $result = Get-AccessTokenMSAL $endpoint $connectedServiceNameARM $overrideResourceType $msalLibraryPath $openSSLExePath $accessToken.token_type = $result.TokenType $accessToken.access_token = $result.AccessToken $accessToken.expires_on = $result.ExpiresOn.ToUnixTimeSeconds() } return $accessToken; } function Build-MSALInstance { [CmdletBinding()] param( [Parameter(Mandatory = $true)] $endpoint, [string][Parameter(Mandatory=$false)] $connectedServiceNameARM, [string][Parameter(Mandatory=$true)] $msalLibraryPath, [string][Parameter(Mandatory=$false)] $openSSLExePath ) $clientId = $endpoint.Auth.Parameters.ServicePrincipalId $tenantId = $endpoint.Auth.Parameters.TenantId $envAuthUrl = Get-EnvironmentAuthUrl -endpoint $endpoint -useMSAL $true try { # load the MSAL library Add-Type -Path "$msalLibraryPath\Microsoft.Identity.Client.dll" $clientBuilder = [Microsoft.Identity.Client.ConfidentialClientApplicationBuilder]::Create($clientId).WithAuthority($envAuthUrl, $tenantId) if ($Endpoint.Auth.Parameters.AuthenticationType -eq 'SPNCertificate') { Write-Verbose "MSAL - ServicePrincipal - certificate is used."; $pemFileContent = $endpoint.Auth.Parameters.ServicePrincipalCertificate $pfxFilePath, $pfxFilePassword = ConvertTo-Pfx -pemFileContent $pemFileContent -openSSLExePath $openSSLExePath $clientCertificate = Get-PfxCertificate -pfxFilePath $pfxFilePath -pfxFilePassword $pfxFilePassword $msalClientInstance = $clientBuilder.WithCertificate($clientCertificate).Build() } elseif ($endpoint.Auth.Scheme -eq $wifConnection) { Write-Verbose "MSAL - WorkloadIdentityFederation is used"; $vstsEndpoint = Get-VstsEndpoint -Name SystemVssConnection -Require $vstsAccessToken = $vstsEndpoint.auth.parameters.AccessToken $oidc_token = Get-VstsFederatedToken -serviceConnectionId $connectedServiceNameARM -vstsAccessToken $vstsAccessToken -OMDirectory $msalLibraryPath $msalClientInstance = $clientBuilder.WithClientAssertion($oidc_token).Build() } else { Write-Verbose "MSAL - ServicePrincipal - clientSecret is used."; $clientSecret = $endpoint.Auth.Parameters.ServicePrincipalKey $msalClientInstance = $clientBuilder.WithClientSecret($clientSecret).Build() } return $msalClientInstance } catch { $exceptionMessage = $_.Exception.Message.ToString() Write-Error "ExceptionMessage: $exceptionMessage (in function: Build-MSALInstance)" throw "Not able to fetch token for tenant Id $tenantId" } } function Get-MSALInstance { [CmdletBinding()] param( [Parameter(Mandatory = $true)] $endpoint, [string][Parameter(Mandatory=$false)] $connectedServiceNameARM, [string][Parameter(Mandatory=$true)] $msalLibraryPath, [string][Parameter(Mandatory=$false)] $openSSLExePath ) # build MSAL if instance does not exist if ($null -eq $script:msalClientInstance) { $script:msalClientInstance = Build-MSALInstance $endpoint $connectedServiceNameARM $msalLibraryPath $openSSLExePath } return $script:msalClientInstance } # Get the Bearer Access Token - MSAL function Get-AccessTokenMSAL { [CmdletBinding()] param( [Parameter(Mandatory = $true)] $endpoint, [string][Parameter(Mandatory=$false)] $connectedServiceNameARM, [parameter(Mandatory = $false)] $overrideResourceType, [string][Parameter(Mandatory=$true)] $msalLibraryPath, [string][Parameter(Mandatory=$false)] $openSSLExePath ) Get-MSALInstance -endpoint $endpoint -connectedServiceNameARM $connectedServiceNameARM -msalLibraryPath $msalLibraryPath -openSSLExePath $openSSLExePath # prepare MSAL scopes [string] $azureActiveDirectoryResourceId = if ($overrideResourceType) { $overrideResourceType } else { (Get-AzureActiverDirectoryResourceId -endpoint $endpoint) } $azureActiveDirectoryResourceId = $azureActiveDirectoryResourceId + "/.default" $scopes = [Collections.Generic.List[string]]@($azureActiveDirectoryResourceId) $tenantId = $endpoint.Auth.Parameters.TenantId try { Write-Verbose "Fetching Access Token - MSAL" $tokenResult = $script:msalClientInstance.AcquireTokenForClient($scopes).ExecuteAsync().GetAwaiter().GetResult() return $tokenResult } catch { $exceptionMessage = $_.Exception.Message.ToString() $parsedException = Parse-Exception($_.Exception) if ($parsedException) { $exceptionMessage = $parsedException } Write-Error "ExceptionMessage: $exceptionMessage (in function: Get-AccessTokenMSAL)" throw "Not able to fetch token for tenant Id $tenantId" } } # Get the Bearer Access Token from the Endpoint for Managed Identity function Get-MsiAccessToken { [CmdletBinding()] param( [Parameter(Mandatory = $true)] $endpoint, [Parameter(Mandatory = $true)] $retryCount, [Parameter(Mandatory = $true)] $timeToWait, [Parameter(Mandatory = $false)] $overrideResourceType ) $msiClientId = ""; if ($endpoint.Data.msiClientId) { $msiClientId = "&client_id=" + $endpoint.Data.msiClientId; } $tenantId = $endpoint.Auth.Parameters.TenantId $endPointUrl = $endpoint.Url if ($overrideResourceType) { $endPointUrl = $overrideResourceType } # Prepare contents for GET $method = "GET" $apiVersion = "2018-02-01"; $authUri = "http://169.254.169.254/metadata/identity/oauth2/token?api-version=" + $apiVersion + "&resource=" + $endPointUrl + $msiClientId; # Call Rest API to fetch AccessToken Write-Verbose "Fetching Access Token For MSI" try { $retryLimit = 5; $response = Invoke-WebRequest -Uri $authUri -Method $method -Headers @{Metadata = "true" } -UseBasicParsing # Action on the based of response if (($response.StatusCode -eq 429) -or ($response.StatusCode -eq 500)) { if ($retryCount -lt $retryLimit) { $retryCount += 1 $waitedTime = 2000 + $timeToWait * 2 Start-Sleep -m $waitedTime Get-MsiAccessToken -endpoint $endpoint -retryCount $retryCount -waitedTime $waitedTime -overrideEndPointUrl $overrideEndPointUrl } else { throw "Could not fetch access token for Managed Service Principal. Status code: $($response.StatusCode), status message: $($response.StatusDescription)" } } elseif ($response.StatusCode -eq 200) { $accessToken = $response.Content | ConvertFrom-Json return $accessToken } else { throw "Could not fetch access token for Managed Service Principal. Please configure Managed Service Identity (MSI) for virtual machine 'https://aka.ms/azure-msi-docs'. Status code: $($response.StatusCode), status message: $($response.StatusDescription)" } } catch { $exceptionMessage = $_.Exception.Message.ToString() Write-Verbose "ExceptionMessage: $exceptionMessage (in function: Get-MsiAccessToken)" if ($exceptionMessage -match "400") { throw "Could not fetch access token for Managed Service Principal. Please configure Managed Service Identity (MSI) for virtual machine 'https://aka.ms/azure-msi-docs'. Status code: $($response.StatusCode), status message: $($response.StatusDescription)" } else { throw $_.Exception } } } function Get-PfxCertificate { param( [string][Parameter(Mandatory = $true)] $pfxFilePath, [string][Parameter(Mandatory = $true)] $pfxFilePassword ) $clientCertificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 $clientCertificate.Import($pfxFilePath, $pfxFilePassword, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet) return $clientCertificate } # Get the certificate from the Endpoint. function Get-Certificate { [CmdletBinding()] param([Parameter(Mandatory = $true)] $endpoint) $bytes = [System.Convert]::FromBase64String($endpoint.Auth.Parameters.Certificate) $certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 $certificate.Import($bytes) return $certificate } function Get-AzStorageKeys { [CmdletBinding()] param([String] [Parameter(Mandatory = $true)] $storageAccountName, [Object] [Parameter(Mandatory = $true)] $endpoint) try { $subscriptionId = $endpoint.Data.SubscriptionId.ToLower() $azureUri = Get-AzureUri $endpoint $uri = "$azureUri/$subscriptionId/services/storageservices/$storageAccountName/keys" $headers = @{"x-ms-version" = "2016-03-01" } $method = "GET" $certificate = Get-Certificate $endpoint $storageKeys = Invoke-RestMethod -Uri $uri -Method $method -Headers $headers -Certificate $certificate return $storageKeys.StorageService.StorageServiceKeys } catch { $exceptionMessage = $_.Exception.Message.ToString() Write-Verbose "Exception : $exceptionMessage" $parsedException = Parse-Exception($_.Exception) if ($parsedException) { $exceptionMessage = $parsedException } Write-Error "ExceptionMessage: $exceptionMessage (in function: Get-AzStorageKeys)" throw } } function Get-AzRMStorageKeys { [CmdletBinding()] param( [string] [Parameter(Mandatory = $true)] $resourceGroupName, [string] [Parameter(Mandatory = $true)] $storageAccountName, [object] [Parameter(Mandatory = $true)] $endpoint, [string][Parameter(Mandatory=$false)] $connectedServiceNameARM, [string][Parameter(Mandatory=$true)] $msalLibraryPath, [string][Parameter(Mandatory=$false)] $openSSLExePath ) try { $accessToken = Get-AzureRMAccessToken -endpoint $endpoint -connectedServiceNameARM $connectedServiceNameARM -msalLibraryPath $msalLibraryPath -openSSLExePath $openSSLExePath $resourceGroupDetails = Get-AzRmResourceGroup $resourceGroupName $endpoint $accessToken $resourceGroupId = $resourceGroupDetails.id $method = "POST" $uri = "$($endpoint.Url)$resourceGroupId/providers/Microsoft.Storage/storageAccounts/$storageAccountName/listKeys" + '?api-version=2015-06-15' $headers = @{"Authorization" = ("{0} {1}" -f $accessToken.token_type, $accessToken.access_token) } $storageKeys = Invoke-RestMethod -Uri $uri -Method $method -Headers $headers return $storageKeys } catch { $exceptionMessage = $_.Exception.Message.ToString() Write-Verbose "Exception : $exceptionMessage" $parsedException = Parse-Exception($_.Exception) if ($parsedException) { $exceptionMessage = $parsedException } Write-Error "ExceptionMessage: $exceptionMessage (in function: Get-AzRMStorageKeys)" throw } } function Get-AzRmVmCustomScriptExtension { [CmdletBinding()] param([String] [Parameter(Mandatory = $true)] $resourceGroupName, [String] [Parameter(Mandatory = $true)] $vmName, [String] [Parameter(Mandatory = $true)] $Name, [Object] [Parameter(Mandatory = $true)] $endpoint, [string] [Parameter(Mandatory=$false)] $connectedServiceNameARM, [string][Parameter(Mandatory=$true)] $msalLibraryPath, [string][Parameter(Mandatory=$true)] $openSSLExePath) try { $accessToken = Get-AzureRMAccessToken -endpoint $endpoint -connectedServiceNameARM $connectedServiceNameARM -msalLibraryPath $msalLibraryPath -openSSLExePath $openSSLExePath $resourceGroupDetails = Get-AzRmResourceGroup $resourceGroupName $endpoint $accessToken $resourceGroupId = $resourceGroupDetails.id if (($endpoint.Data.Environment) -and ($endpoint.Data.Environment -eq $azureStack)) { $vmExtensionApiVersion = '2015-06-15' } else { $vmExtensionApiVersion = '2016-03-30' } $method = "GET" $uri = "$($endpoint.Url)$resourceGroupId/providers/Microsoft.Compute/virtualMachines/$vmName/extensions/$Name" + '?api-version=' + $vmExtensionApiVersion $headers = @{"accept-language" = "en-US" } $headers.Add("Authorization", ("{0} {1}" -f $accessToken.token_type, $accessToken.access_token)) $customScriptExt = Invoke-RestMethod -Uri $uri -Method $method -Headers $headers return $customScriptExt.properties } catch { $exceptionMessage = $_.Exception.Message.ToString() Write-Verbose "Exception : $exceptionMessage" $parsedException = Parse-Exception($_.Exception) if ($parsedException) { $exceptionMessage = $parsedException } Write-Error "ExceptionMessage: $exceptionMessage (in function: Get-AzRmVmCustomScriptExtension)" throw } } function Remove-AzRmVmCustomScriptExtension { [CmdletBinding()] param([String] [Parameter(Mandatory = $true)] $resourceGroupName, [String] [Parameter(Mandatory = $true)] $vmName, [String] [Parameter(Mandatory = $true)] $Name, [Object] [Parameter(Mandatory = $true)] $endpoint, [string] [Parameter(Mandatory=$false)] $connectedServiceNameARM, [string][Parameter(Mandatory=$true)] $msalLibraryPath, [string][Parameter(Mandatory=$true)] $openSSLExePath) try { $accessToken = Get-AzureRMAccessToken -endpoint $endpoint -connectedServiceNameARM $connectedServiceNameARM -msalLibraryPath $msalLibraryPath -openSSLExePath $openSSLExePath $resourceGroupDetails = Get-AzRmResourceGroup $resourceGroupName $endpoint $accessToken $resourceGroupId = $resourceGroupDetails.id $method = "DELETE" $uri = "$($endpoint.Url)$resourceGroupId/providers/Microsoft.Compute/virtualMachines/$vmName/extensions/$Name" + '?api-version=2016-03-30' $headers = @{"accept-language" = "en-US" } $headers.Add("Authorization", ("{0} {1}" -f $accessToken.token_type, $accessToken.access_token)) $response = Invoke-RestMethod -Uri $uri -Method $method -Headers $headers return $response } catch { $exceptionMessage = $_.Exception.Message.ToString() Write-Verbose "Exception : $exceptionMessage" $parsedException = Parse-Exception($_.Exception) if ($parsedException) { $exceptionMessage = $parsedException } Write-Error "ExceptionMessage: $exceptionMessage (in function: Remove-AzRmVmCustomScriptExtension)" throw } } function Get-AzStorageAccount { [CmdletBinding()] param([String] [Parameter(Mandatory = $true)] $storageAccountName, [Object] [Parameter(Mandatory = $true)] $endpoint) try { $subscriptionId = $endpoint.Data.SubscriptionId.ToLower() $azureUri = Get-AzureUri $endpoint $uri = "$azureUri/$subscriptionId/services/storageservices/$storageAccountName" $headers = @{"x-ms-version" = "2016-03-01" } $method = "GET" $certificate = Get-Certificate $endpoint $storageAccount = Invoke-RestMethod -Uri $uri -Method $method -Headers $headers -Certificate $certificate return $storageAccount.StorageService.StorageServiceProperties } catch { $exceptionMessage = $_.Exception.Message.ToString() Write-Verbose "Exception : $exceptionMessage" $parsedException = Parse-Exception($_.Exception) if ($parsedException) { $exceptionMessage = $parsedException } Write-Error "ExceptionMessage: $exceptionMessage (in function: Get-AzStorageAccount)" throw } } function Get-AzRmStorageAccount { [CmdletBinding()] param([String] [Parameter(Mandatory = $true)] $resourceGroupName, [String] [Parameter(Mandatory = $true)] $storageAccountName, [Object] [Parameter(Mandatory = $true)] $endpoint, [string] [Parameter(Mandatory=$false)] $connectedServiceNameARM, [string][Parameter(Mandatory=$true)] $msalLibraryPath, [string][Parameter(Mandatory=$true)] $openSSLExePath) try { $accessToken = Get-AzureRMAccessToken -endpoint $endpoint -connectedServiceNameARM $connectedServiceNameARM -msalLibraryPath $msalLibraryPath -openSSLExePath $openSSLExePath $resourceGroupDetails = Get-AzRmResourceGroup $resourceGroupName $endpoint $accessToken $resourceGroupId = $resourceGroupDetails.id $method = "GET" $uri = "$($endpoint.Url)$resourceGroupId/providers/Microsoft.Storage/storageAccounts/$storageAccountName" + '?api-version=2016-01-01' $headers = @{"Authorization" = ("{0} {1}" -f $accessToken.token_type, $accessToken.access_token) } $storageAccountUnformatted = Invoke-RestMethod -Uri $uri -Method $method -Headers $headers Write-Verbose "Constructing the storage account object" $storageAccount = New-Object -TypeName PSObject $storageAccount | Add-Member -type NoteProperty -name id -value $storageAccountUnformatted.id $storageAccount | Add-Member -type NoteProperty -name kind -value $storageAccountUnformatted.kind $storageAccount | Add-Member -type NoteProperty -name location -value $storageAccountUnformatted.location $storageAccount | Add-Member -type NoteProperty -name StorageAccountName -value $storageAccountUnformatted.name $storageAccount | Add-Member -type NoteProperty -name tags -value $storageAccountUnformatted.tags $storageAccount | Add-Member -type NoteProperty -name sku -value $storageAccountUnformatted.sku $storageAccount | Add-Member -type NoteProperty -name creationTime -value $storageAccountUnformatted.properties.creationTime $storageAccount | Add-Member -type NoteProperty -name primaryLocation -value $storageAccountUnformatted.properties.primaryLocation $storageAccount | Add-Member -type NoteProperty -name provisioningState -value $storageAccountUnformatted.properties.provisioningState $storageAccount | Add-Member -type NoteProperty -name statusOfPrimary -value $storageAccountUnformatted.properties.statusOfPrimary $storageAccount | Add-Member -type NoteProperty -name primaryEndpoints -value $storageAccountUnformatted.properties.primaryEndpoints return $storageAccount } catch { $exceptionMessage = $_.Exception.Message.ToString() Write-Verbose "Exception : $exceptionMessage" $parsedException = Parse-Exception($_.Exception) if ($parsedException) { $exceptionMessage = $parsedException } Write-Error "ExceptionMessage: $exceptionMessage (in function: Get-AzRmStorageAccount)" throw } } function Get-AzRmResourceGroup { [CmdletBinding()] param([String] [Parameter(Mandatory = $true)] $resourceGroupName, [Object] [Parameter(Mandatory = $true)] $endpoint, [Parameter(Mandatory = $true)] $accessToken) try { $subscriptionId = $endpoint.Data.SubscriptionId.ToLower() $method = "GET" $uri = "$($endpoint.Url)/subscriptions/$subscriptionId/resourceGroups/$resourceGroupName" + '?api-version=2016-02-01' $headers = @{"Authorization" = ("{0} {1}" -f $accessToken.token_type, $accessToken.access_token) } $resourceGroup = Invoke-RestMethod -Uri $uri -Method $method -Headers $headers return $resourceGroup } catch { $exceptionMessage = $_.Exception.Message.ToString() Write-Verbose "Exception : $exceptionMessage" $parsedException = Parse-Exception($_.Exception) if ($parsedException) { $exceptionMessage = $parsedException } Write-Error "ExceptionMessage: $exceptionMessage (in function: Get-AzRmResourceGroup)" throw } } # Get the Azure Resource Id function Get-AzureSqlDatabaseServerResourceId { [CmdletBinding()] param([String] [Parameter(Mandatory = $true)] $serverName, [Object] [Parameter(Mandatory = $true)] $endpoint, [Object] [Parameter(Mandatory = $true)] $accessToken) $serverType = "Microsoft.Sql/servers" $subscriptionId = $endpoint.Data.SubscriptionId.ToLower() Write-Verbose "[Azure Rest Call] Get Resource Groups" $method = "GET" $uri = "$($endpoint.Url)/subscriptions/$subscriptionId/resources?api-version=$apiVersion" $headers = @{Authorization = ("{0} {1}" -f $accessToken.token_type, $accessToken.access_token) } do { Write-Verbose "Fetching Resources from $uri" $ResourceDetails = Invoke-RestMethod -Uri $uri -Method $method -Headers $headers -ContentType $script:jsonContentType foreach ($resourceDetail in $ResourceDetails.Value) { if ($resourceDetail.name -eq $serverName -and $resourceDetail.type -eq $serverType) { return $resourceDetail.id } } $uri = $ResourceDetails.nextLink } until([string]::IsNullOrEmpty($ResourceDetails.nextLink)) throw "No resource found with serverName $serverName, serverType $serverType in subscription $subscriptionId. Specify the correct serverName/serverType and try again." } function Add-AzureRmSqlServerFirewall { [CmdletBinding()] param([Object] [Parameter(Mandatory = $true)] $endpoint, [String] [Parameter(Mandatory = $true)] $startIPAddress, [String] [Parameter(Mandatory = $true)] $endIPAddress, [String] [Parameter(Mandatory = $true)] $serverName, [String] [Parameter(Mandatory = $true)] $firewallRuleName, [string] [Parameter(Mandatory = $false)] $connectedServiceNameARM, [string][Parameter(Mandatory=$true)] $msalLibraryPath, [string][Parameter(Mandatory=$true)] $openSSLExePath) $accessToken = Get-AzureRMAccessToken -endpoint $endpoint -connectedServiceNameARM $connectedServiceNameARM -msalLibraryPath $msalLibraryPath -openSSLExePath $openSSLExePath # get azure sql server resource Id $azureResourceId = Get-AzureSqlDatabaseServerResourceId -endpoint $endpoint -serverName $serverName -accessToken $accessToken $uri = "$($endpoint.Url)/$azureResourceId/firewallRules/$firewallRuleName\?api-version=$apiVersion" $body = "{ 'properties' : { 'startIpAddress':'$startIPAddress', 'endIpAddress':'$endIPAddress' } }" $headers = @{Authorization = ("{0} {1}" -f $accessToken.token_type, $accessToken.access_token) } Invoke-RestMethod -Uri $uri -Method PUT -Headers $headers -Body $body -ContentType $script:jsonContentType } function Remove-AzureRmSqlServerFirewall { [CmdletBinding()] param([Object] [Parameter(Mandatory = $true)] $endpoint, [String] [Parameter(Mandatory = $true)] $serverName, [String] [Parameter(Mandatory = $true)] $firewallRuleName, [string] [Parameter(Mandatory = $false)] $connectedServiceNameARM, [string][Parameter(Mandatory=$true)] $msalLibraryPath, [string][Parameter(Mandatory=$true)] $openSSLExePath) $accessToken = Get-AzureRMAccessToken -endpoint $endpoint -connectedServiceNameARM $connectedServiceNameARM -msalLibraryPath $msalLibraryPath -openSSLExePath $openSSLExePath # Fetch Azure SQL server resource Id $azureResourceId = Get-AzureSqlDatabaseServerResourceId -endpoint $endpoint -serverName $serverName -accessToken $accessToken $uri = "$($endpoint.Url)/$azureResourceId/firewallRules/$firewallRuleName\?api-version=$apiVersion" $headers = @{Authorization = ("{0} {1}" -f $accessToken.token_type, $accessToken.access_token) } Invoke-RestMethod -Uri $uri -Method Delete -Headers $headers } function Parse-Exception($exception) { if ($exception) { try { Write-Verbose "Exception message - $($exception.ToString())" $response = $exception.Response if ($response) { $responseStream = $response.GetResponseStream() $streamReader = New-Object System.IO.StreamReader($responseStream) $streamReader.BaseStream.Position = 0 $streamReader.DiscardBufferedData() $responseBody = $streamReader.ReadToEnd() $streamReader.Close() Write-Verbose "Exception message extracted from response $responseBody" $exceptionMessage = ""; try { if ($responseBody) { $exceptionJson = $responseBody | ConvertFrom-Json $exceptionError = $exceptionJson.error if ($exceptionError) { $exceptionMessage = $exceptionError.Message $exceptionCode = $exceptionError.code } else { $exceptionMessage = $exceptionJson.Message $exceptionCode = $exceptionJson.code } if ($exceptionCode) { Write-VstsTaskError -ErrCode $exceptionCode } } } catch { $exceptionMessage = $responseBody } if ($response.statusCode -eq 404 -or (-not $exceptionMessage)) { $exceptionMessage += " Please verify request URL : $($response.ResponseUri)" } return $exceptionMessage } } catch { Write-verbose "Unable to parse exception: " + $_.Exception.ToString() } } return $null } function Get-AzureNetworkInterfaceDetails { [CmdletBinding()] param([String] [Parameter(Mandatory = $true)] $resourceGroupName, [Object] [Parameter(Mandatory = $true)] $endpoint, [string] [Parameter(Mandatory=$false)] $connectedServiceNameARM, [string][Parameter(Mandatory=$true)] $msalLibraryPath, [string][Parameter(Mandatory=$true)] $openSSLExePath) $accessToken = Get-AzureRMAccessToken -endpoint $endpoint -connectedServiceNameARM $connectedServiceNameARM -msalLibraryPath $msalLibraryPath -openSSLExePath $openSSLExePath $subscriptionId = $endpoint.Data.SubscriptionId.ToLower() Write-Verbose "[Azure Rest Call] Get Network Interface Details" $method = "GET" $uri = "$($endpoint.Url)/subscriptions/$subscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.Network/networkInterfaces?api-version=$azureStackapiVersion" $headers = @{Authorization = ("{0} {1}" -f $accessToken.token_type, $accessToken.access_token) } $networkInterfaceDetails = Invoke-RestMethod -Uri $uri -Method $method -Headers $headers -ContentType $script:jsonContentType if (-not $networkInterfaceDetails) { throw "Unable to fetch network interfaces details" } if ($networkInterfaceDetails.value) { return $networkInterfaceDetails.value | ForEach-Object { Add-PropertiesToRoot -rootObject $_ } } return $networkInterfaceDetails.value } function Get-AzurePublicIpAddressDetails { [CmdletBinding()] param([String] [Parameter(Mandatory = $true)] $resourceGroupName, [Object] [Parameter(Mandatory = $true)] $endpoint, [string] [Parameter(Mandatory = $false)] $connectedServiceNameARM, [string][Parameter(Mandatory=$true)] $msalLibraryPath, [string][Parameter(Mandatory=$true)] $openSSLExePath) $accessToken = Get-AzureRMAccessToken -endpoint $endpoint -connectedServiceNameARM $connectedServiceNameARM -msalLibraryPath $msalLibraryPath -openSSLExePath $openSSLExePath $subscriptionId = $endpoint.Data.SubscriptionId.ToLower() Write-Verbose "[Azure Rest Call] Get Public IP Addresses Details" $method = "GET" $uri = "$($endpoint.Url)/subscriptions/$subscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.Network/publicIPAddresses?api-version=$azureStackapiVersion" $headers = @{Authorization = ("{0} {1}" -f $accessToken.token_type, $accessToken.access_token) } $publicIPAddressesDetails = Invoke-RestMethod -Uri $uri -Method $method -Headers $headers -ContentType $script:jsonContentType if (-not $publicIPAddressesDetails) { throw "Unable to fetch public IP Addresses details" } if ($publicIPAddressesDetails.value) { return $publicIPAddressesDetails.value | ForEach-Object { Add-PropertiesToRoot -rootObject $_ } } return $publicIPAddressesDetails.value } function Get-AzureLoadBalancersDetails { [CmdletBinding()] param([String] [Parameter(Mandatory = $true)] $resourceGroupName, [Object] [Parameter(Mandatory = $true)] $endpoint, [string] [Parameter(Mandatory = $false)] $connectedServiceNameARM, [string][Parameter(Mandatory=$true)] $msalLibraryPath, [string][Parameter(Mandatory=$true)] $openSSLExePath) $accessToken = Get-AzureRMAccessToken -endpoint $endpoint -connectedServiceNameARM $connectedServiceNameARM -msalLibraryPath $msalLibraryPath -openSSLExePath $openSSLExePath $subscriptionId = $endpoint.Data.SubscriptionId.ToLower() Write-Verbose "[Azure Rest Call] Get Load Balancers details" $method = "GET" $uri = "$($endpoint.Url)/subscriptions/$subscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.Network/loadBalancers?api-version=$azureStackapiVersion" $headers = @{Authorization = ("{0} {1}" -f $accessToken.token_type, $accessToken.access_token) } $loadBalancersDetails = Invoke-RestMethod -Uri $uri -Method $method -Headers $headers -ContentType $script:jsonContentType if (-not $loadBalancersDetails) { throw "Unable to fetch load balancer details" } if ($loadBalancersDetails.value) { return $loadBalancersDetails.value | ForEach-Object { Add-PropertiesToRoot -rootObject $_ } } return $loadBalancersDetails.value } function Get-AzureLoadBalancerDetails { [CmdletBinding()] param([String] [Parameter(Mandatory = $true)] $resourceGroupName, [String] [Parameter(Mandatory = $true)] $name, [Object] [Parameter(Mandatory = $true)] $endpoint, [string] [Parameter(Mandatory = $false)] $connectedServiceNameARM, [string][Parameter(Mandatory=$true)] $msalLibraryPath, [string][Parameter(Mandatory=$true)] $openSSLExePath) $accessToken = Get-AzureRMAccessToken -endpoint $endpoint -connectedServiceNameARM $connectedServiceNameARM -msalLibraryPath $msalLibraryPath -openSSLExePath $openSSLExePath $subscriptionId = $endpoint.Data.SubscriptionId.ToLower() Write-Verbose "[Azure Rest Call] Get Load balancer details with name : $name" $method = "GET" $uri = "$($endpoint.Url)/subscriptions/$subscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.Network/loadBalancers/" + $name + "?api-version=$azureStackapiVersion" $headers = @{Authorization = ("{0} {1}" -f $accessToken.token_type, $accessToken.access_token) } $loadBalancerDetails = Invoke-RestMethod -Uri $uri -Method $method -Headers $headers -ContentType $script:jsonContentType if ($loadBalancerDetails) { return $loadBalancersDetails | ForEach-Object { Add-PropertiesToRoot -rootObject $_ } } return $loadBalancerDetails } function Get-AzureRMLoadBalancerFrontendIpConfigDetails { [CmdletBinding()] param([Object] [Parameter(Mandatory = $true)] $loadBalancer) $frontendIPConfigurations = $loadBalancer.frontendIPConfigurations if ($frontendIPConfigurations) { return Add-PropertiesToRoot -rootObject $frontendIPConfigurations } return $frontendIPConfigurations } function Get-AzureRMLoadBalancerInboundNatRuleConfigDetails { [CmdletBinding()] param([Object] [Parameter(Mandatory = $true)] $loadBalancer) $inboundNatRules = $loadBalancer.inboundNatRules if ($inboundNatRules) { return Add-PropertiesToRoot -rootObject $inboundNatRules } return $inboundNatRules } function Add-PropertiesToRoot { [CmdletBinding()] param([Object] [Parameter(Mandatory = $true)] $rootObject) if ($rootObject -and $rootObject.properties) { $rootObject.properties.psObject.Properties | ForEach-Object { $rootObject | Add-Member -MemberType $_.MemberType -Name $_.Name -Value $_.Value -Force } $rootObject.psObject.properties.remove("properties"); } return $rootObject } function ConvertTo-Pfx { param( [String][Parameter(Mandatory = $true)] $pemFileContent, [string][Parameter(Mandatory = $true)] $openSSLExePath ) if ($ENV:Agent_TempDirectory) { $pemFilePath = "$ENV:Agent_TempDirectory\clientcertificate.pem" $pfxFilePath = "$ENV:Agent_TempDirectory\clientcertificate.pfx" $pfxPasswordFilePath = "$ENV:Agent_TempDirectory\clientcertificatepassword.txt" } else { $pemFilePath = "$ENV:System_DefaultWorkingDirectory\clientcertificate.pem" $pfxFilePath = "$ENV:System_DefaultWorkingDirectory\clientcertificate.pfx" $pfxPasswordFilePath = "$ENV:System_DefaultWorkingDirectory\clientcertificatepassword.txt" } # save the PEM certificate to a PEM file Set-Content -Path $pemFilePath -Value $pemFileContent # use openssl to convert the PEM file to a PFX file $pfxFilePassword = [System.Guid]::NewGuid().ToString() Set-Content -Path $pfxPasswordFilePath -Value $pfxFilePassword -NoNewline $openSSLArgs = "pkcs12 -export -in $pemFilePath -out $pfxFilePath -password file:`"$pfxPasswordFilePath`"" Invoke-VstsTool -FileName $openSSLExePath -Arguments $openSSLArgs -RequireExitCodeZero return $pfxFilePath, $pfxFilePassword } function Get-VstsFederatedToken { param( [Parameter(Mandatory=$true)] [string]$serviceConnectionId, [Parameter(Mandatory=$true)] [string]$vstsAccessToken, [Parameter(Mandatory=$true)] [string]$OMDirectory ) $newtonsoftDll = [System.IO.Path]::Combine($OMDirectory, "Newtonsoft.Json.dll") if (!(Test-Path -LiteralPath $newtonsoftDll -PathType Leaf)) { Write-Verbose "$newtonsoftDll not found." throw } $jsAssembly = [System.Reflection.Assembly]::LoadFrom($newtonsoftDll) $vsServicesDll = [System.IO.Path]::Combine($OMDirectory, "Microsoft.VisualStudio.Services.WebApi.dll") if (!(Test-Path -LiteralPath $vsServicesDll -PathType Leaf)) { Write-Verbose "$vsServicesDll not found." throw } try { Add-Type -LiteralPath $vsServicesDll } catch { # The requested type may successfully load now even though the assembly itself is not fully loaded. Write-Verbose "$($_.Exception.GetType().FullName): $($_.Exception.Message)" } $onAssemblyResolve = [System.ResolveEventHandler] { param($sender, $e) if ($e.Name -like 'Newtonsoft.Json, *') { return $jsAssembly } Write-Verbose "Unable to resolve assembly name '$($e.Name)'" return $null } [System.AppDomain]::CurrentDomain.add_AssemblyResolve($onAssemblyResolve) $taskHttpClient = $null; try { Write-Verbose "Trying again to construct the HTTP client." $federatedCredential = New-Object Microsoft.VisualStudio.Services.OAuth.VssOAuthAccessTokenCredential($vstsAccessToken) $uri = Get-VstsTaskVariable -Name 'System.CollectionUri' -Require $vssCredentials = New-Object Microsoft.VisualStudio.Services.Common.VssCredentials( (New-Object Microsoft.VisualStudio.Services.Common.WindowsCredential($false)), # Do not use default credentials. $federatedCredential, [Microsoft.VisualStudio.Services.Common.CredentialPromptType]::DoNotPrompt) $taskHttpClient = Get-VstsVssHttpClient -OMDirectory $OMDirectory ` -TypeName Microsoft.TeamFoundation.DistributedTask.WebApi.TaskHttpClient ` -VssCredentials $vssCredentials -Uri $uri } finally { Write-Verbose "Removing assemlby resolver." [System.AppDomain]::CurrentDomain.remove_AssemblyResolve($onAssemblyResolve) } $planId = Get-VstsTaskVariable -Name 'System.PlanId' -Require $jobId = Get-VstsTaskVariable -Name 'System.JobId' -Require $hub = Get-VstsTaskVariable -Name 'System.HostType' -Require $projectId = Get-VstsTaskVariable -Name 'System.TeamProjectId' -Require # CreateOidcTokenAsync method requires a parameter called claims even though it could be set as an empty dictionary $claims = New-Object 'System.Collections.Generic.Dictionary[String,String]' $tokenResponse = $taskHttpClient.CreateOidcTokenAsync( $global:ProjectId, $global:Hub, $global:PlanId, $global:JobId, $claims, $ServiceConnectionId, $null ).Result $federatedToken = $tokenResponse.OidcToken if ($null -eq $federatedToken -or $federatedToken -eq [string]::Empty) { Write-Verbose "Failed to create OIDC token." throw "Could not generate a client assertion for federated login." } Write-Verbose "Generated OIDC token." return $federatedToken } # Export only the public function. Export-ModuleMember -Function Get-AzStorageKeys Export-ModuleMember -Function Get-AzRMStorageKeys Export-ModuleMember -Function Get-AzRmVmCustomScriptExtension Export-ModuleMember -Function Remove-AzRmVmCustomScriptExtension Export-ModuleMember -Function Get-AzStorageAccount Export-ModuleMember -Function Get-AzRmStorageAccount Export-ModuleMember -Function Get-AzRmResourceGroup Export-ModuleMember -Function Get-AzureNetworkInterfaceDetails Export-ModuleMember -Function Get-AzurePublicIpAddressDetails Export-ModuleMember -Function Get-AzureLoadBalancersDetails Export-ModuleMember -Function Get-AzureLoadBalancerDetails Export-ModuleMember -Function Get-AzureRMLoadBalancerFrontendIpConfigDetails Export-ModuleMember -Function Get-AzureRMLoadBalancerInboundNatRuleConfigDetails Export-ModuleMember -Function Get-AzureRMAccessToken |