diff --git a/bootstrap/modules/azure_devops/locals_files.tf b/bootstrap/modules/azure_devops/locals_files.tf index 6d08cf66..5a89ce75 100644 --- a/bootstrap/modules/azure_devops/locals_files.tf +++ b/bootstrap/modules/azure_devops/locals_files.tf @@ -33,7 +33,7 @@ locals { } module_files = { for key, value in var.repository_files : key => { - content = replace((file(value.path)), "# backend \"azurerm\" {}", "backend \"azurerm\" {}") + content = replace((file(value.path)), "# backend \"azurerm\" {}", "backend \"azurerm\" {\n ${local.is_authentication_scheme_workload_identity_federation ? "use_oidc = true" : "use_msi = true"}\n use_azuread_auth = true\n }") } if value.flag == "module" || value.flag == "additional" } repository_files = merge(local.cicd_file, local.module_files, var.use_template_repository ? {} : local.cicd_template_files) diff --git a/bootstrap/modules/azure_devops/repository_module.tf b/bootstrap/modules/azure_devops/repository_module.tf index 76cc780d..f07124de 100644 --- a/bootstrap/modules/azure_devops/repository_module.tf +++ b/bootstrap/modules/azure_devops/repository_module.tf @@ -14,7 +14,7 @@ resource "azuredevops_git_repository_file" "alz" { file = each.key content = each.value.content branch = local.default_branch - commit_message = "Add ${each.key} [skip ci]" + commit_message = "[skip ci]" overwrite_on_create = true } diff --git a/bootstrap/modules/azure_devops/repository_templates.tf b/bootstrap/modules/azure_devops/repository_templates.tf index 241131fc..59504f1e 100644 --- a/bootstrap/modules/azure_devops/repository_templates.tf +++ b/bootstrap/modules/azure_devops/repository_templates.tf @@ -14,7 +14,7 @@ resource "azuredevops_git_repository_file" "alz_templates" { file = each.key content = each.value.content branch = local.default_branch - commit_message = "Add ${each.key} [skip ci]" + commit_message = "[skip ci]" overwrite_on_create = true } diff --git a/templates/ci_cd/azuredevops/templates/helpers/terraform-apply.yaml b/templates/ci_cd/azuredevops/templates/helpers/terraform-apply.yaml index dcc95631..2d11112b 100644 --- a/templates/ci_cd/azuredevops/templates/helpers/terraform-apply.yaml +++ b/templates/ci_cd/azuredevops/templates/helpers/terraform-apply.yaml @@ -11,14 +11,27 @@ steps: azureSubscription: $${{ parameters.serviceConnection }} scriptType: pscore scriptLocation: inlineScript + addSpnToEnvironment: true inlineScript: | - # Workaround for MSI authentication + # Get settings from service connection az account show 2>$null | ConvertFrom-Json | Set-Variable account - if($account.user.name -eq 'systemAssignedIdentity') { - $env:ARM_USE_CLI = 'false' + $clientId = $account.user.name + $oidcToken ??= $env:idToken # requires addSpnToEnvironment: true + $subscriptionId = $account.id + $tenantId = $account.tenantId + $isOidc = $oidcToken -ne $null + + $env:ARM_TENANT_ID = $account.tenantId + $env:ARM_SUBSCRIPTION_ID = $account.id + + if($isOidc) { + # Note: We are using CLI auth for the provider as it caches the access token for us, which helps with edge cases like terraform test. + # The backend is hard coded to use OIDC auth as it does not support CLI auth yet. + $env:ARM_USE_CLI = 'true' + $env:ARM_OIDC_TOKEN = $oidcToken + $env:ARM_CLIENT_ID = $clientId + } else { $env:ARM_USE_MSI = 'true' - $env:ARM_TENANT_ID = $account.tenantId - $env:ARM_SUBSCRIPTION_ID = $account.id } # Run Terraform Apply @@ -29,7 +42,3 @@ steps: $arguments += "tfplan" Write-Host "Running: $command $arguments" & $command $arguments - - env: - ARM_USE_AZUREAD: true - ARM_USE_CLI: true diff --git a/templates/ci_cd/azuredevops/templates/helpers/terraform-init.yaml b/templates/ci_cd/azuredevops/templates/helpers/terraform-init.yaml index a67f9533..99c3561c 100644 --- a/templates/ci_cd/azuredevops/templates/helpers/terraform-init.yaml +++ b/templates/ci_cd/azuredevops/templates/helpers/terraform-init.yaml @@ -22,6 +22,7 @@ steps: $oidcToken ??= $env:idToken # requires addSpnToEnvironment: true $subscriptionId = $account.id $tenantId = $account.tenantId + $isOidc = $oidcToken -ne $null $arguments = @() $arguments += "init" @@ -29,15 +30,14 @@ steps: $arguments += "-backend-config=`"container_name=$($env:BACKEND_AZURE_STORAGE_ACCOUNT_CONTAINER_NAME)`"" $arguments += "-backend-config=`"key=$($env:BACKEND_AZURE_STORAGE_ACCOUNT_CONTAINER_KEY_NAME)`"" $arguments += "-backend-config=`"resource_group_name=$($env:BACKEND_AZURE_RESOURCE_GROUP_NAME)`"" - $arguments += "-backend-config=`"subscription_id=$subscriptionId`"" - $arguments += "-backend-config=`"tenant_id=$tenantId`"" - if($oidcToken -eq $null) { - $arguments += '-backend-config="use_msi=true"' - } else { - $arguments += "-backend-config=`"client_id=$clientId`"" - $arguments += "-backend-config=`"oidc_token=$oidcToken`"" - $arguments += '-backend-config="use_oidc=true"' + $env:ARM_SUBSCRIPTION_ID = $subscriptionId + $env:ARM_TENANT_ID = $tenantId + + # Note: The backend is hardcoded to use oidc or msi auth as we want to use a different auth type for the provider during plan and apply. + if($isOidc) { + $env:ARM_OIDC_TOKEN = $oidcToken + $env:ARM_CLIENT_ID = $clientId } # Run terraform init @@ -46,7 +46,6 @@ steps: & $command $arguments env: - ARM_USE_AZUREAD: true BACKEND_AZURE_RESOURCE_GROUP_NAME: $${{ parameters.backendAzureResourceGroupName }} BACKEND_AZURE_STORAGE_ACCOUNT_NAME: $${{ parameters.backendAzureStorageAccountName }} BACKEND_AZURE_STORAGE_ACCOUNT_CONTAINER_NAME: $${{ parameters.backendAzureStorageAccountContainerName }} diff --git a/templates/ci_cd/azuredevops/templates/helpers/terraform-installer.ps1 b/templates/ci_cd/azuredevops/templates/helpers/terraform-installer.ps1 deleted file mode 100644 index b46bba45..00000000 --- a/templates/ci_cd/azuredevops/templates/helpers/terraform-installer.ps1 +++ /dev/null @@ -1,108 +0,0 @@ -# Powershell verison of the installer script for testing purposes -# Scripts cannot be called from template repos in Azure DevOps, so we have to inline this script in the task -# See here for more details: https://learn.microsoft.com/en-us/azure/devops/pipelines/process/templates?view=azure-devops&pivots=templates-includes#use-other-repositories -param( - [string]$TF_VERSION = "latest", - [string]$TOOLS_PATH = ".\terraform" -) - -if($TF_VERSION -eq "latest") { - $TF_VERSION = (Invoke-WebRequest -Uri "https://checkpoint-api.hashicorp.com/v1/check/terraform").Content | ConvertFrom-Json | Select -ExpandProperty current_version -} - -$commandDetails = Get-Command -Name terraform -ErrorAction SilentlyContinue -if($commandDetails) { - Write-Host "Terraform already installed in $($commandDetails.Path), checking version" - $installedVersion = terraform version -json | ConvertFrom-Json - Write-Host "Installed version: $($installedVersion.terraform_version) on $($installedVersion.platform)" - if($installedVersion.terraform_version -eq $TF_VERSION) { - Write-Host "Installed version matches required version $TF_VERSION, skipping install" - return - } -} - -$unzipdir = Join-Path -Path $TOOLS_PATH -ChildPath "terraform_$TF_VERSION" -if (Test-Path $unzipdir) { - Write-Host "Terraform $TF_VERSION already installed." - if($os -eq "windows") { - $env:PATH = "$($unzipdir);$env:PATH" - } else { - $env:PATH = "$($unzipdir):$env:PATH" - } - Write-Host "##vso[task.setvariable variable=PATH]$env:PATH" - return -} - -$os = "" -if ($IsWindows) { - $os = "windows" -} -if($IsLinux) { - $os = "linux" -} -if($IsMacOS) { - $os = "darwin" -} - -# Enum values can be seen here: https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.architecture?view=net-7.0#fields -$architecture = ([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture).ToString().ToLower() - -if($architecture -eq "x64") { - $architecture = "amd64" -} -if($architecture -eq "x86") { - $architecture = "386" -} - -$osAndArchitecture = "$($os)_$($architecture)" - -$supportedOsAndArchitectures = @( - "darwin_amd64", - "darwin_arm64", - "linux_386", - "linux_amd64", - "linux_arm64", - "windows_386", - "windows_amd64" -) - -if($supportedOsAndArchitectures -notcontains $osAndArchitecture) { - Write-Error "Unsupported OS and architecture combination: $osAndArchitecture" - exit 1 -} - -$zipfilePath = "$unzipdir.zip" - -$url = "https://releases.hashicorp.com/terraform/$($TF_VERSION)/terraform_$($TF_VERSION)_$($osAndArchitecture).zip" - -if(!(Test-Path $TOOLS_PATH)) { - New-Item -ItemType Directory -Path $TOOLS_PATH| Out-String | Write-Verbose -} - -Invoke-WebRequest -Uri $url -OutFile "$zipfilePath" | Out-String | Write-Verbose - -Expand-Archive -Path $zipfilePath -DestinationPath $unzipdir - -$toolFileName = "terraform" - -if($os -eq "windows") { - $toolFileName = "$($toolFileName).exe" -} - -$toolFilePath = Join-Path -Path $unzipdir -ChildPath $toolFileName - -if($os -ne "windows") { - $isExecutable = $(test -x $toolFilePath; 0 -eq $LASTEXITCODE) - if(!($isExecutable)) { - chmod +x $toolFilePath - } -} - -if($os -eq "windows") { - $env:PATH = "$($unzipdir);$env:PATH" -} else { - $env:PATH = "$($unzipdir):$env:PATH" -} -Write-Host "##vso[task.setvariable variable=PATH]$env:PATH" -Remove-Item $zipfilePath -Write-Host "Installed Terraform version $TF_VERSION" diff --git a/templates/ci_cd/azuredevops/templates/helpers/terraform-plan.yaml b/templates/ci_cd/azuredevops/templates/helpers/terraform-plan.yaml index 35b0012b..3e4712c2 100644 --- a/templates/ci_cd/azuredevops/templates/helpers/terraform-plan.yaml +++ b/templates/ci_cd/azuredevops/templates/helpers/terraform-plan.yaml @@ -11,14 +11,27 @@ steps: azureSubscription: $${{ parameters.serviceConnection }} scriptType: pscore scriptLocation: inlineScript + addSpnToEnvironment: true inlineScript: | - # Workaround for MSI authentication + # Get settings from service connection az account show 2>$null | ConvertFrom-Json | Set-Variable account - if($account.user.name -eq 'systemAssignedIdentity') { - $env:ARM_USE_CLI = 'false' + $clientId = $account.user.name + $oidcToken ??= $env:idToken # requires addSpnToEnvironment: true + $subscriptionId = $account.id + $tenantId = $account.tenantId + $isOidc = $oidcToken -ne $null + + $env:ARM_TENANT_ID = $account.tenantId + $env:ARM_SUBSCRIPTION_ID = $account.id + + if($isOidc) { + # Note: We are using CLI auth for the provider as it caches the access token for us, which helps with edge cases like terraform test. + # The backend is hard coded to use OIDC auth as it does not support CLI auth yet. + $env:ARM_USE_CLI = 'true' + $env:ARM_OIDC_TOKEN = $oidcToken + $env:ARM_CLIENT_ID = $clientId + } else { $env:ARM_USE_MSI = 'true' - $env:ARM_TENANT_ID = $account.tenantId - $env:ARM_SUBSCRIPTION_ID = $account.id } # Run Terraform Plan @@ -36,6 +49,4 @@ steps: & $command $arguments env: - ARM_USE_AZUREAD: true - ARM_USE_CLI: true TERRAFORM_ACTION: $${{ coalesce(parameters.terraform_action, 'apply') }}