public/Add-AzBootstrapEnvironment.ps1
function Add-AzBootstrapEnvironment { [CmdletBinding(ConfirmImpact = 'Medium')] param( [Parameter(Mandatory = $true)] [string]$EnvironmentName, [Parameter(Mandatory = $true)] [string]$ResourceGroupName, [Parameter(Mandatory = $true)] [string]$Location, [Parameter(Mandatory = $true)] [string]$PlanManagedIdentityName, # Name for the primary/plan MI [string]$ApplyManagedIdentityName, [string]$GitHubOwner, [string]$GitHubRepo, [string]$PlanEnvNameOverride, [string]$ApplyEnvNameOverride, [string[]]$ApplyEnvironmentUserReviewers, [string[]]$ApplyEnvironmentTeamReviewers, [bool]$AddOwnerAsReviewer = $true, [string]$ArmTenantId, [string]$ArmSubscriptionId, [string]$TerraformStateStorageAccountName ) # Validate required parameters # Validate required parameters if (-not $EnvironmentName -or -not $ResourceGroupName -or -not $Location -or -not $PlanManagedIdentityName) { throw "Parameters 'EnvironmentName', 'ResourceGroupName', 'Location', and 'PlanManagedIdentityName' are required" } # Retrieve Azure context (Subscription ID and Tenant ID) if not provided if (-not $ArmTenantId -or -not $ArmSubscriptionId) { $azContext = Get-AzCliContext # This function handles checks and throws on failure $ArmSubscriptionId = $azContext.SubscriptionId $ArmTenantId = $azContext.TenantId } # Check storage account name if provided if (-not [string]::IsNullOrWhiteSpace($TerraformStateStorageAccountName)) { $storageAccountValidation = Test-AzStorageAccountName -StorageAccountName $TerraformStateStorageAccountName if (-not $storageAccountValidation) { throw "A valid storage account is required." } } $RepoInfo = Get-GitHubRepositoryInfo -OverrideOwner $GitHubOwner -OverrideRepo $GitHubRepo if (-not $RepoInfo) { throw "Could not determine GitHub repository information. Ensure you are in a git repository or provide -Owner and -Repo parameters." } # Determine GitHub environment names $actualPlanEnvName = if (-not [string]::IsNullOrWhiteSpace($PlanEnvNameOverride)) { $PlanEnvNameOverride } else { "$EnvironmentName-iac-plan" } $actualApplyEnvName = if (-not [string]::IsNullOrWhiteSpace($ApplyEnvNameOverride)) { $ApplyEnvNameOverride } else { "$EnvironmentName-iac-apply" } $ApplyManagedIdentityName = if (-not [string]::IsNullOrWhiteSpace($ApplyManagedIdentityName)) { $ApplyManagedIdentityName } else { $PlanManagedIdentityName.Replace("-plan", "-apply") } # Check if the resource group already exists Write-BootstrapLog "Checking if Azure resource group '$ResourceGroupName' already exists..." if (Test-AzResourceGroupExists -ResourceGroupName $ResourceGroupName) { throw "Azure resource group '$ResourceGroupName' already exists. Please choose a different name." } # do the deployment $infraDetails = New-AzBicepDeployment -EnvironmentName $EnvironmentName ` -ResourceGroupName $ResourceGroupName ` -Location $Location ` -PlanManagedIdentityName $PlanManagedIdentityName ` -ApplyManagedIdentityName $ApplyManagedIdentityName ` -GitHubOwner $RepoInfo.Owner ` -GitHubRepo $RepoInfo.Repo ` -PlanEnvName $actualPlanEnvName ` -ApplyEnvName $actualApplyEnvName ` -ArmSubscriptionId $ArmSubscriptionId ` -TerraformStateStorageAccountName $TerraformStateStorageAccountName # Save configuration to .azbootstrap.jsonc at the top level of the repository $environmentConfig = [PSCustomObject]@{ EnvironmentName = $EnvironmentName ResourceGroupName = $ResourceGroupName DeploymentStackName = $infraDetails.DeploymentStackName PlanGitHubEnvironmentName = $actualPlanEnvName ApplyGitHubEnvironmentName = $actualApplyEnvName TerraformStateStorageAccountName = $TerraformStateStorageAccountName } $repoPath = git rev-parse --show-toplevel 2>$null if ($LASTEXITCODE -eq 0 -and $repoPath) { $configPath = Join-Path $repoPath ".azbootstrap.jsonc" Add-AzBootstrapConfig -ConfigPath $configPath -EnvironmentConfig $environmentConfig } else { Write-Warning "Could not determine repository root path. Skipping writing configuration file." } if (-not $infraDetails) { throw "Failed to set up Azure infrastructure for environment '$EnvironmentName'." } # Create/update environment and set secrets $secrets = @{ "ARM_TENANT_ID" = $ArmTenantId "ARM_SUBSCRIPTION_ID" = $ArmSubscriptionId } if (-not [string]::IsNullOrWhiteSpace($TerraformStateStorageAccountName)) { $secrets += @{ "TF_STATE_RESOURCE_GROUP_NAME" = $ResourceGroupName "TF_STATE_STORAGE_ACCOUNT_NAME" = $TerraformStateStorageAccountName } } Write-Bootstraplog "Configuring GitHub environment '$actualPlanEnvName'..." New-GitHubEnvironment -Owner $RepoInfo.Owner -Repo $RepoInfo.Repo -EnvironmentName $actualPlanEnvName $secrets["ARM_CLIENT_ID"] = $infraDetails.PlanManagedIdentityClientId foreach ($key in $secrets.Keys) { Set-GitHubEnvironmentSecrets -Owner $RepoInfo.Owner -Repo $RepoInfo.Repo -EnvironmentName $actualPlanEnvName -Secrets @{$key=$secrets[$key]} } Write-Bootstraplog "Configuring GitHub environment '$actualApplyEnvName'..." New-GitHubEnvironment -Owner $RepoInfo.Owner -Repo $RepoInfo.Repo -EnvironmentName $actualApplyEnvName $secrets["ARM_CLIENT_ID"] = $infraDetails.ApplyManagedIdentityClientId foreach ($key in $secrets.Keys) { Set-GitHubEnvironmentSecrets -Owner $RepoInfo.Owner -Repo $RepoInfo.Repo -EnvironmentName $actualApplyEnvName -Secrets @{$key=$secrets[$key]} } # add reviewers to the apply environment Set-GitHubEnvironmentPolicy -Owner $RepoInfo.Owner ` -Repo $RepoInfo.Repo ` -EnvironmentName $actualApplyEnvName ` -UserReviewers $ApplyEnvironmentUserReviewers ` -TeamReviewers $ApplyEnvironmentTeamReviewers ` -AddOwnerAsReviewer $AddOwnerAsReviewer Write-BootstrapLog "GitHub environments '$actualPlanEnvName' and '$actualApplyEnvName' configured successfully." -Level Success return $environmentConfig } Export-ModuleMember -Function Add-AzBootstrapEnvironment |