/source/Calamari.Azure/Scripts/AzureContext.ps1

https://github.com/OctopusDeploy/Calamari · Powershell · 212 lines · 142 code · 31 blank · 39 comment · 18 complexity · a8e0ca9867fab2df06ffd85cde53a231 MD5 · raw file

  1. ## Octopus Azure Context script, version 1.0
  2. ## --------------------------------------------------------------------------------------
  3. ##
  4. ## This script is used to load the Azure Powershell module and select the Azure subscription
  5. ##
  6. ## The script is passed the following parameters.
  7. ##
  8. ## $OctopusAzureTargetScript = "..."
  9. ## $OctopusAzureTargetScriptParameters = "..."
  10. ## $OctopusUseServicePrincipal = "false"
  11. ## $OctopusAzureSubscriptionId = "..."
  12. ## $OctopusAzureStorageAccountName = "..."
  13. ## $OctopusAzureCertificateFileName = "..."
  14. ## $OctopusAzureCertificatePassword = "..."
  15. ## $OctopusAzureADTenantId = "..."
  16. ## $OctopusAzureADClientId = "..."
  17. ## $OctopusAzureADPassword = "..."
  18. ## $OctopusAzureEnvironment = "..."
  19. ## $OctopusDisableAzureCLI = "..."
  20. ## $OctopusAzureExtensionsDirectory = "..."
  21. $ErrorActionPreference = "Stop"
  22. if ($PSVersionTable.PSVersion.Major -lt 5)
  23. {
  24. throw "These Azure commands are only supported in PowerShell versions 5 and above. This server is currently running PowerShell version $($PSVersionTable.PSVersion.ToString())."
  25. }
  26. function EnsureDirectoryExists([string] $path)
  27. {
  28. New-Item -ItemType Directory -Force -Path $path *>$null
  29. }
  30. function Execute-WithRetry([ScriptBlock] $command) {
  31. $attemptCount = 0
  32. $operationIncomplete = $true
  33. $sleepBetweenFailures = 5
  34. $maxFailures = 5
  35. while ($operationIncomplete -and $attemptCount -lt $maxFailures) {
  36. $attemptCount = ($attemptCount + 1)
  37. if ($attemptCount -ge 2) {
  38. Write-Host "Waiting for $sleepBetweenFailures seconds before retrying..."
  39. Start-Sleep -s $sleepBetweenFailures
  40. Write-Host "Retrying..."
  41. }
  42. try {
  43. & $command
  44. $operationIncomplete = $false
  45. } catch [System.Exception] {
  46. if ($attemptCount -lt ($maxFailures)) {
  47. Write-Host ("Attempt $attemptCount of $maxFailures failed: " + $_.Exception.Message)
  48. } else {
  49. throw
  50. }
  51. }
  52. }
  53. }
  54. Execute-WithRetry{
  55. pushd $env:OctopusCalamariWorkingDirectory
  56. try {
  57. If ([System.Convert]::ToBoolean($OctopusUseServicePrincipal)) {
  58. # Authenticate via Service Principal
  59. $securePassword = ConvertTo-SecureString $OctopusAzureADPassword -AsPlainText -Force
  60. $creds = New-Object System.Management.Automation.PSCredential ($OctopusAzureADClientId, $securePassword)
  61. if (Get-Command "Login-AzureRmAccount" -ErrorAction SilentlyContinue)
  62. {
  63. # Turn off context autosave, as this will make all authentication occur in memory, and isolate each session from the context changes in other sessions
  64. Disable-AzureRMContextAutosave -Scope Process
  65. $AzureEnvironment = Get-AzureRmEnvironment -Name $OctopusAzureEnvironment
  66. if (!$AzureEnvironment)
  67. {
  68. Write-Error "No Azure environment could be matched given the name $OctopusAzureEnvironment"
  69. exit -2
  70. }
  71. Write-Verbose "AzureRM Modules: Authenticating with Service Principal"
  72. # Force any output generated to be verbose in Octopus logs.
  73. Write-Host "##octopus[stdout-verbose]"
  74. Login-AzureRmAccount -Credential $creds -TenantId $OctopusAzureADTenantId -SubscriptionId $OctopusAzureSubscriptionId -Environment $AzureEnvironment -ServicePrincipal
  75. Write-Host "##octopus[stdout-default]"
  76. }
  77. elseif (Get-InstalledModule Az -ErrorAction SilentlyContinue)
  78. {
  79. if (-Not(Get-Command "Disable-AzureRMContextAutosave" -errorAction SilentlyContinue))
  80. {
  81. # Turn on AzureRm aliasing
  82. # See https://docs.microsoft.com/en-us/powershell/azure/migrate-from-azurerm-to-az?view=azps-3.0.0#enable-azurerm-compatibility-aliases
  83. Enable-AzureRmAlias -Scope Process
  84. }
  85. # Turn off context autosave, as this will make all authentication occur in memory, and isolate each session from the context changes in other sessions
  86. Disable-AzContextAutosave -Scope Process
  87. $AzureEnvironment = Get-AzEnvironment -Name $OctopusAzureEnvironment
  88. if (!$AzureEnvironment)
  89. {
  90. Write-Error "No Azure environment could be matched given the name $OctopusAzureEnvironment"
  91. exit -2
  92. }
  93. Write-Verbose "Az Modules: Authenticating with Service Principal"
  94. # Force any output generated to be verbose in Octopus logs.
  95. Write-Host "##octopus[stdout-verbose]"
  96. Connect-AzAccount -Credential $creds -TenantId $OctopusAzureADTenantId -SubscriptionId $OctopusAzureSubscriptionId -Environment $AzureEnvironment -ServicePrincipal
  97. Write-Host "##octopus[stdout-default]"
  98. }
  99. If (!$OctopusDisableAzureCLI -or $OctopusDisableAzureCLI -like [Boolean]::FalseString) {
  100. try {
  101. # authenticate with the Azure CLI
  102. Write-Host "##octopus[stdout-verbose]"
  103. # Config directory is set to make sure that our security is right for the step running it
  104. # and not using the one in the default config dir to avoid issues with user defined ones
  105. $env:AZURE_CONFIG_DIR = [System.IO.Path]::Combine($env:OctopusCalamariWorkingDirectory, "azure-cli")
  106. EnsureDirectoryExists($env:AZURE_CONFIG_DIR)
  107. # The azure extensions directory is getting overridden above when we set the azure config dir (undocumented behavior).
  108. # Set the azure extensions directory to the value of $OctopusAzureExtensionsDirectory if specified,
  109. # otherwise, back to the default value of $HOME\.azure\cliextension.
  110. if($OctopusAzureExtensionsDirectory)
  111. {
  112. Write-Host "Setting Azure CLI extensions directory to $OctopusAzureExtensionsDirectory"
  113. $env:AZURE_EXTENSION_DIR = $OctopusAzureExtensionsDirectory
  114. } else {
  115. $env:AZURE_EXTENSION_DIR = "$($HOME)\.azure\cliextensions"
  116. }
  117. $previousErrorAction = $ErrorActionPreference
  118. $ErrorActionPreference = "Continue"
  119. az cloud set --name $OctopusAzureEnvironment 2>$null 3>$null
  120. $ErrorActionPreference = $previousErrorAction
  121. Write-Host "Azure CLI: Authenticating with Service Principal"
  122. $loginArgs = @();
  123. $loginArgs += @("-u", (ConvertTo-QuotedString(ConvertTo-ConsoleEscapedArgument($OctopusAzureADClientId))));
  124. # Use the full argument because of https://github.com/Azure/azure-cli/issues/12105
  125. $loginArgs += @("--password", (ConvertTo-QuotedString(ConvertTo-ConsoleEscapedArgument($OctopusAzureADPassword))));
  126. $loginArgs += @("--tenant", (ConvertTo-QuotedString(ConvertTo-ConsoleEscapedArgument($OctopusAzureADTenantId))));
  127. az login --service-principal $loginArgs
  128. Write-Host "Azure CLI: Setting active subscription to $OctopusAzureSubscriptionId"
  129. az account set --subscription $OctopusAzureSubscriptionId
  130. Write-Host "##octopus[stdout-default]"
  131. Write-Verbose "Successfully authenticated with the Azure CLI"
  132. } catch {
  133. # failed to authenticate with Azure CLI
  134. Write-Verbose "Failed to authenticate with Azure CLI"
  135. Write-Verbose $_.Exception.Message
  136. }
  137. }
  138. } Else {
  139. # Authenticate via Management Certificate
  140. Write-Verbose "Loading the management certificate"
  141. Add-Type -AssemblyName "System"
  142. $certificate = new-object System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList @($OctopusAzureCertificateFileName, $OctopusAzureCertificatePassword, ([System.Security.Cryptography.X509Certificates.X509KeyStorageFlags] "PersistKeySet", "Exportable"))
  143. $AzureEnvironment = Get-AzureEnvironment | Where-Object {$_.Name -eq $OctopusAzureEnvironment}
  144. if (!$AzureEnvironment)
  145. {
  146. Write-Error "No Azure environment could be matched given name $OctopusAzureEnvironment"
  147. exit -2
  148. }
  149. $azureProfile = New-AzureProfile -SubscriptionId $OctopusAzureSubscriptionId -StorageAccount $OctopusAzureStorageAccountName -Certificate $certificate -Environment $AzureEnvironment
  150. $azureProfile.Save(".\AzureProfile.json")
  151. Select-AzureProfile -Profile $azureProfile | Out-Null
  152. }
  153. }
  154. finally {
  155. popd
  156. }
  157. }
  158. Write-Verbose "Invoking target script $OctopusAzureTargetScript with $OctopusAzureTargetScriptParameters parameters"
  159. try {
  160. Invoke-Expression ". `"$OctopusAzureTargetScript`" $OctopusAzureTargetScriptParameters"
  161. } catch {
  162. # Warn if FIPS 140 compliance required when using Service Management SDK
  163. if ([System.Security.Cryptography.CryptoConfig]::AllowOnlyFipsAlgorithms -and ![System.Convert]::ToBoolean($OctopusUseServicePrincipal)) {
  164. Write-Warning "The Azure Service Management SDK is not FIPS 140 compliant. http://g.octopushq.com/FIPS"
  165. }
  166. throw
  167. } finally {
  168. If (!$OctopusDisableAzureCLI -or $OctopusDisableAzureCLI -like [Boolean]::FalseString) {
  169. try {
  170. # Save the last exit code so az logout doesn't clobber it
  171. $previousLastExitCode = $LastExitCode
  172. $previousErrorAction = $ErrorActionPreference
  173. $ErrorActionPreference = "Continue"
  174. az logout 2>$null 3>$null
  175. } finally {
  176. # restore the previous last exit code
  177. $LastExitCode = $previousLastExitCode
  178. $ErrorActionPreference = $previousErrorAction
  179. }
  180. }
  181. }