AzureHelper.ps1
function Import-AppConfiguration { param ( [string]$ConfigSettingPath, [string]$SvcName, [ValidateSet('service', 'shared')] [string]$ConfigScope, [string]$Env, [string]$Stamp, [ValidateSet('public', 'fairfax')] [string]$Cloud, [string]$AacPrimaryName, [string]$AacFailoverName, [bool]$IsPrimary ) function Get-PrimaryStampName { param ( [string]$FailoverOrPrimaryStampName ) $stampNames = @{ 'self1s' = 'self01' 'sh01s' = 'sh01' 'prap1s' = 'prap01' 'prau1s' = 'prau01' 'preu1s' = 'preu01' 'preu2s' = 'preu02' 'prna1s' = 'prna01' 'prna2s' = 'prna02' 'ghb1s' = 'ghb01' 'gcp1s' = 'gcp01' 'ghp1s' = 'ghp01' } if ($stampNames.ContainsKey($FailoverOrPrimaryStampName)) { return $stampNames[$FailoverOrPrimaryStampName] } else { return $FailoverOrPrimaryStampName } } function Import-ConfigFile { param ( [string]$FileName, [string]$AacName, [string]$FilePath, [string]$ConfigPrefix, [string]$ConfigScope, [bool]$DryRun = $false ) $importedFileName = "$ConfigPrefix-$FileName-$AacName" if ($importedFiles.ContainsKey($importedFileName)) { $importedFilePath = $importedFiles[$importedFileName] Write-Host "Have picked $importedFilePath, skip $FilePath" -ForegroundColor Yellow return } if ($DryRun -eq $false) { Write-Host "Will import $FileName to $AacName" az appconfig kv import -n $AacName -s file --path $FilePath --prefix $ConfigPrefix --format json --separator : --content-type application/json --yes # azure cli doesn't create exceptions for powershell to catch, use $? to check the most recent command execution status # https://learn.microsoft.com/en-us/cli/azure/use-cli-effectively?tabs=bash%2Cbash2#error-handling-for-azure-cli-in-powershell if ($? -eq $false) { Write-Host "Failed to import $FileName to $AacName as application/json content type, try with the default content type" -ForegroundColor Red az appconfig kv import -n $AacName -s file --path $FilePath --prefix $ConfigPrefix --format json --separator : --yes if ($? -eq $false) { Write-Host "Failed to import $FileName to $AacName after retry" -ForegroundColor Red exit 1 } else { Write-Host "Imported $FileName to $AacName successfully after retry" -ForegroundColor Green } } else { Write-Host "Imported $FileName to $AacName as application/json successfully" -ForegroundColor Green } } $importedFiles.Add($importedFileName, $FilePath) } function Import-Config { param ( [string][Parameter(Mandatory)] $ConfigPath, [string][Parameter(Mandatory)][ValidateSet("service", "shared")] $ConfigScope, [bool]$DryRun = $false ) Write-Host "Check files under $ConfigPath" foreach ($file in Get-ChildItem $ConfigPath) { $target = Get-Item $file.FullName if ($target.PSIsContainer) { # skip folder continue } $fileName = Split-Path $file -Leaf if ([bool]::Parse($IsPrimary)) { Import-ConfigFile -FileName $fileName -AacName $AacPrimaryName -FilePath $file.FullName -ConfigPrefix $configPrefix -ConfigScope $ConfigScope -DryRun $DryRun } else { Import-ConfigFile -FileName $fileName -AacName $AacFailoverName -FilePath $file.FullName -ConfigPrefix $configPrefix -ConfigScope $ConfigScope -DryRun $DryRun } } } function GetKeyValueFromAac { param ( [string]$ConfigPrefix, [string]$AacStoreName ) # Run the az command to list all key-value pairs in the App Configuration store $appConfigKeys = az appconfig kv list --all --name "$AacStoreName" --key "$ConfigPrefix*" --query "[].{Key:key, Value:value}" --output json | ConvertFrom-Json # Loop through the key-value pairs and add them to the dictionary foreach ($key in $appConfigKeys) { $AacConfigDic.Add($key.Key, $key.Value) } return $AacConfigDic } # Convert the object to a nested dictionary function ConvertTo-Dictionary { param ( [object]$Obj, [string]$KeyPrefix ) $separator = ":" foreach ($property in $obj.PSObject.Properties) { $propertyKey = $keyPrefix + $property.Name if ($property.Value -is [System.Management.Automation.PSCustomObject]) { $childDict = ConvertTo-Dictionary -Obj $property.Value -KeyPrefix "$propertyKey$separator" } elseif ($property.Value -is [System.Array]) { $i = 0 foreach ($item in $property.Value) { $JsonConfigDic[$propertyKey + $separator + $i] = $item $i++ } $JsonConfigDic[$propertyKey] = $item } else { $JsonConfigDic[$propertyKey] = $property.Value } } } function GetKeyValueFromConfigFile { foreach ($file in $importedFiles.Keys) { $keyPrefix = $file.Split('-')[0] $json = Get-Content -Path $importedFiles[$file] -Raw $obj = ConvertFrom-Json -InputObject $json ConvertTo-Dictionary -Obj $obj -KeyPrefix $keyPrefix } } function FindDiffKeys { param ( [string]$AacStoreName, [string]$ConfigPrefix ) GetKeyValueFromConfigFile $KeysToDelete = @() $AacConfigDic = GetKeyValueFromAac -ConfigPrefix $ConfigPrefix -AacStoreName $AacStoreName Write-Host "AAC keys count" $AacConfigDic.Keys.Count Write-Host "JsonConfigDic keys count" $JsonConfigDic.Keys.Count foreach ($key in $AacConfigDic.Keys) { if (!$JsonConfigDic.ContainsKey($key)) { $KeysToDelete += $key } } return $KeysToDelete } $configPrefix = "{0}:{1}:" -f $ConfigScope, $SvcName $JsonConfigDic = @{} $AacConfigDic = @{} $importedFiles = @{} $Stamp = Get-PrimaryStampName -FailoverOrPrimaryStampName $Stamp $byEnvStampSetting = "$ConfigSettingPath/Configurations/$ConfigScope/$Cloud/$Env/$Stamp" $byEnvSetting = "$ConfigSettingPath/Configurations/$ConfigScope/$Cloud/$Env" $byCloud = "$ConfigSettingPath/Configurations/$ConfigScope/$Cloud" $defaultSetting = "$ConfigSettingPath/Configurations/$ConfigScope" if (Test-Path -Path $byEnvStampSetting) { Import-Config -ConfigPath $byEnvStampSetting -ConfigScope $ConfigScope -DryRun $true } if (Test-Path -Path $byEnvSetting) { Import-Config -ConfigPath $byEnvSetting -ConfigScope $ConfigScope -DryRun $true } if (Test-Path -Path $byCloud) { Import-Config -ConfigPath $byCloud -ConfigScope $ConfigScope -DryRun $true } if (Test-Path -Path $defaultSetting) { Import-Config -ConfigPath $defaultSetting -ConfigScope $ConfigScope -DryRun $true } # Delete keys that are not present in JSON configuration files if ($isPrimaryScaleUnit) { $toDeleteKeys = FindDiffKeys -AacStoreName $AacPrimaryName -ConfigPrefix $configPrefix } else { $toDeleteKeys = FindDiffKeys -AacStoreName $AacFailoverName -ConfigPrefix $configPrefix } foreach ($key in $toDeleteKeys) { if ($key -eq "shared:" + $SvcAlias + ":sentinel" -or $key -eq "service:" + $SvcAlias + ":sentinel") { continue } Write-Host "${key} exists in AAC but not in Json configuration files, will delete it from AAC" if ($isPrimaryScaleUnit) { az appconfig kv delete -n $AacPrimaryName --key $key --yes } else { az appconfig kv delete -n $AacFailoverName --key $key --yes } } $importedFiles = @{} if (Test-Path -Path $byEnvStampSetting) { Write-Host "----------------------Will import $ConfigScope Remote Configurations under path $byEnvStampSetting - By Environments, Scale Units----------------------" Import-Config -ConfigPath $byEnvStampSetting -ConfigScope $ConfigScope } else { Write-Host "----------------------Path byEnvStampSetting not found: $byEnvStampSetting----------------------" } if (Test-Path -Path $byEnvSetting) { Write-Host "----------------------Will import $ConfigScope Remote Configurations under path $byEnvSetting - By Environments----------------------" Import-Config -ConfigPath $byEnvSetting -ConfigScope $ConfigScope } else { Write-Host "----------------------Path byEnvSetting not found: $byEnvSetting" } if (Test-Path -Path $byCloud) { Write-Host "----------------------Will import $ConfigScope Remote Configurations under path $byCloud - By Cloud----------------------" Import-Config -ConfigPath $byCloud -ConfigScope $ConfigScope } else { Write-Host "----------------------Path byCloud not found: $byCloud" } if (Test-Path -Path $defaultSetting) { Write-Host "----------------------Will import $ConfigScope Remote Configurations under path $defaultSetting - Common Configurations----------------------" Import-Config -ConfigPath $defaultSetting -ConfigScope $ConfigScope } else { Write-Host "----------------------Path defaultSetting not found: $defaultSetting" } } function Login-By-UserAccount { param ( [string]$SubscriptionId, [string]$AadTenantId, [string]$Ev2Env ) $acct = $null $userObjectId = $null try { $acct = Set-AzContext -Subscription $SubscriptionId -Tenant $AadTenantId -ErrorAction Stop $userObjectId = (Get-AzADUser -upn "$($acct.Account.Id)").Id } catch { if ($Ev2Env -eq "Fairfax") { Connect-AzAccount -Tenant $AadTenantId -ErrorAction Stop -EnvironmentName AzureUSGovernment } else { Connect-AzAccount -Tenant $AadTenantId -ErrorAction Stop } $acct = Set-AzContext -Subscription $SubscriptionId -Tenant $AadTenantId -ErrorAction Stop $userObjectId = (Get-AzADUser -upn "$($acct.Account.Id)").Id } if (($acct -eq $null) -or ($userObjectId -eq $null)) { Enter-ExceptionHandler "Login-By-UserAccount" (New-Object System.Exception "Null 'acct' or '$userObjectId'") } Write-Host "Login succeeded with object ID: $userObjectId" return $userObjectId } function Login-By-UserManagedIdentity { param ( [string]$SubscriptionId, [string]$AadTenantId, [string]$Ev2Env ) $isLogin = $false for ($i = 0; $i -lt 10; $i++) { try { Write-Host "$($i+1) try: login Azure via user managed identity" if ($Ev2Env -eq "Fairfax") { Connect-AzAccount -Identity -Tenant $AadTenantId -ErrorAction Stop -EnvironmentName AzureUSGovernment } else { Connect-AzAccount -Identity -Tenant $AadTenantId -ErrorAction Stop } Set-AzContext -Subscription $SubscriptionId -Tenant $AadTenantId -ErrorAction Stop Write-Host "Login succeeded" $isLogin = $true break } catch { Start-Sleep -s 10 } } if (!($isLogin)) { Enter-ExceptionHandler "Login-By-UserManagedIdentity" (New-Object System.Exception "Login failed") } } function Login-Azure { param ( [string]$SubscriptionId, [bool]$Local, [string]$Env ) # Use common subscription ID if not specified if ([string]::IsNullOrEmpty($SubscriptionId)) { $SubscriptionId = Get-SubscriptionId -Env $Env } $aadTenantId = Get-AadTenantId -Env $Env $ev2Env = Get-Ev2Env -Env $Env if ($Local) { return Login-By-UserAccount -SubscriptionId $SubscriptionId -AadTenantId $aadTenantId -Ev2Env $ev2Env } else { Login-By-UserManagedIdentity -SubscriptionId $SubscriptionId -AadTenantId $aadTenantId -Ev2Env $ev2Env return "" } } function Azure-Cli-Login-By-UserAccount { param ( [string]$SubscriptionId, [string]$AadTenantId, [string]$Ev2Env ) $acct = $null $userObjectId = $null if ($Ev2Env -eq "Fairfax") { az cloud set --name AzureUSGovernment } az login --tenant $AadTenantId --only-show-errors az account set --subscription $SubscriptionId $acct = az account show | ConvertFrom-Json $userObjectId = (az ad user show --id ($acct.user.Name) | ConvertFrom-Json).id if (($acct -eq $null) -or ($userObjectId -eq $null)) { Enter-ExceptionHandler "Azure-Cli-Login-By-UserAccount" (New-Object System.Exception "Null 'acct' or '$userObjectId'") } Write-Host "Login succeeded with object ID: $userObjectId" return $userObjectId } function Azure-Cli-Login-By-UserManagedIdentity { param ( [string]$SubscriptionId, [string]$Ev2Env ) $isLogin = $false for ($i = 0; $i -lt 10; $i++) { try { Write-Host "$($i+1) try: login Azure CLI via user managed identity" if ($Ev2Env -eq "Fairfax") { az cloud set --name AzureUSGovernment } az login --identity --only-show-errors if (!$?) { throw "ERROR: in az login" } az account set --subscription $SubscriptionId Write-Host "Login succeeded" $isLogin = $true break } catch { Start-Sleep -s 10 } } if (!($isLogin)) { Enter-ExceptionHandler "Azure-Cli-Login-By-UserManagedIdentity" (New-Object System.Exception "Login failed") } } function Login-Azure-Cli { param ( [string]$SubscriptionId, [bool]$Local, [string]$Env ) # Use common subscription ID if not specified if ([string]::IsNullOrEmpty($SubscriptionId)) { $SubscriptionId = Get-SubscriptionId -Env $Env } $aadTenantId = Get-AadTenantId -Env $Env $ev2Env = Get-Ev2Env -Env $Env if ($Local) { return Azure-Cli-Login-By-UserAccount -SubscriptionId $subscriptionId -AadTenantId $aadTenantId -Ev2Env $ev2Env } else { Azure-Cli-Login-By-UserManagedIdentity -SubscriptionId $subscriptionId -Ev2Env $ev2Env return "" } } function Create-KeyVault-And-Grant-AccessPolicy { param ( [string]$Env, [string]$UserObjectId, [string]$ResourceGroup, [string]$Location, [string]$KeyVaultName, [ValidateSet("Standard", "Premium")] [string]$Sku = "Standard", [string]$Stamp, [string]$Version, [string]$ServiceAlias ) if (-not (Get-AzResourceGroup -Name $ResourceGroup -Location $Location -ErrorAction SilentlyContinue)) { Write-Host "Creating resource group: $ResourceGroup" New-AzResourceGroup -Name $ResourceGroup -Location $Location -ErrorAction Stop } else { Write-Host "Resource group: $ResourceGroup already exists" } $tags = @{ 'Stamp' = $Stamp; 'Environment' = $Env; 'Build' = $Version; 'MicroService' = $ServiceAlias; 'TimeStamp' = $(Get-Date -Format "yyyyMMddTHHmmssZ").ToString() } if (Get-AzKeyVault -Location $Location -VaultName $KeyVaultName -InRemovedState) { Undo-AzKeyVaultRemoval -Location $Location -VaultName $KeyVaultName -ResourceGroupName $ResourceGroup -ErrorAction Stop Write-Host "Key vault: $KeyVaultName restored" } if (-not (Get-AzKeyVault -VaultName $KeyVaultName -ResourceGroupName $ResourceGroup)) { New-AzKeyVault -VaultName $KeyVaultName -ResourceGroupName $ResourceGroup -Location $Location -EnabledForDeployment -EnabledForTemplateDeployment -EnabledForDiskEncryption -Sku $Sku -ErrorAction Stop -Tag $tags Write-Host "Key vault: $KeyVaultName created" } else { Write-Host "Key vault: $KeyVaultName already exists" Update-AzKeyVault -ResourceGroupName $ResourceGroup -VaultName $KeyVaultName -Tag $tags } Set-AzKeyVaultAccessPolicy -VaultName $KeyVaultName -ResourceGroupName $ResourceGroup -ObjectId $UserObjectId -PermissionsToKeys All -PermissionsToCertificates All -PermissionsToSecrets All -ErrorAction Stop Set-AzKeyVaultAccessPolicy -VaultName $KeyVaultName -ResourceGroupName $ResourceGroup -BypassObjectIdValidation -ObjectId (Get-Ev2ObjectId $Env) -PermissionsToKeys Get, List -PermissionsToSecrets Get, List -PermissionsToCertificates Get, List -ErrorAction Stop Set-AzKeyVaultAccessPolicy -VaultName $KeyVaultName -ResourceGroupName $ResourceGroup -BypassObjectIdValidation -ObjectId (Get-AntaresObjectId $Env) -PermissionsToKeys Get, List -PermissionsToSecrets Get, List -PermissionsToCertificates Get, List -ErrorAction Stop } function Create-KeyVault-Key { param ( [string]$ResourceGroup, [string]$KeyVaultName, [string]$KeyName, [int]$ExpireInMonths, [int]$RotateInDays, [string]$Stamp, [string]$Version, [string]$ServiceAlias ) Write-Host "`n`n****************************** Key Name: $KeyName" $tags = @{ 'RgName' = $ResourceGroup; 'ResourceName' = 'None'; 'CreatedBy' = 'CPC'; 'Stamp' = $Stamp; 'Environment' = $Env; 'Build' = $Version; 'MicroService' = $ServiceAlias; 'TimeStamp' = $(Get-Date -Format "yyyyMMddTHHmmssZ").ToString() } if (!$ExpireInMonths) { $ExpireInMonths = Get-DefaultKeyExpireInMonths Write-Host "Set default ExpireInMonths as: $ExpireInMonths" } if (!$RotateInDays) { $RotateInDays = Get-DefaultKeyRotateInDays Write-Host "Set default RotateInDays as: $RotateInDays" } $currentKey = Get-AzKeyVaultKey -VaultName $KeyVaultName -Name $KeyName if ($currentKey) { Update-AzKeyVaultKey -VaultName $KeyVaultName -Name $KeyName -Tag $tags -ErrorAction Stop Write-Host "Update key tags - Done" } if (!$currentKey -or !$currentKey.Expires) { $expires = (Get-Date).AddMonths($ExpireInMonths).ToUniversalTime() Add-AzKeyVaultKey -VaultName $KeyVaultName -Name $KeyName -Expires $expires -Destination Software -Tag $tags -ErrorAction Stop Write-Host "Create key - Done" } else { Write-Host "Key already exists" } $currentPolicy = Get-AzKeyVaultKeyRotationPolicy -VaultName $KeyVaultName -Name $KeyName # if policy not exist or only contains default Notify action if (!$currentPolicy -or ($currentPolicy.LifetimeActions.Count -eq 1)) { $action = [Microsoft.Azure.Commands.KeyVault.Models.PSKeyRotationLifetimeAction]::new() $action.Action = "Rotate" $action.TimeAfterCreate = New-TimeSpan -Days $RotateInDays Set-AzKeyVaultKeyRotationPolicy -VaultName $KeyVaultName -Name $KeyName -KeyRotationLifetimeAction $action -ErrorAction Stop Write-Host "Set key rotation policy - Done" } else { Write-Host "Rotation policy already exists" $currentPolicy } } function Create-KeyVault-Certificate { param ( [string]$Env, [string]$UserObjectId, [string]$ResourceGroup, [string]$Location, [string]$KeyVaultName, [string]$IssuerName, [string]$ProviderName, [string]$CertName, [string]$CertSubject, [string[]]$CertDnsName, [int]$CertValidityInMonths, [int]$CertRenewalPercentage, [bool]$KeyNotExportable = $false, [string[]]$AdvancedKeyUsages = @(), [bool]$CertOnly = $false, [string]$Stamp, [string]$Version, [string]$ServiceAlias ) Write-Host "`n`n****************************** Certificate Name: $CertName" $DefaultKeyUsages = @("DigitalSignature", "KeyEncipherment") $KeyUsage = $DefaultKeyUsages + $AdvancedKeyUsages $tags = @{ 'RgName' = $ResourceGroup; 'ResourceName' = 'None'; 'CreatedBy' = 'CPC'; 'Stamp' = $Stamp; 'Environment' = $Env; 'Build' = $Version; 'MicroService' = $ServiceAlias; 'TimeStamp' = $(Get-Date -Format "yyyyMMddTHHmmssZ").ToString() } if ($CertOnly -eq $false) { Create-KeyVault-And-Grant-AccessPolicy -Env $Env -UserObjectId $UserObjectId -ResourceGroup $ResourceGroup -Location $Location -KeyVaultName $KeyVaultName -Stamp $Stamp -Version $Version -ServiceAlias $ServiceAlias } if ($CertDnsName -eq $null) { $CertDnsName = $CertSubject } if ($CertValidityInMonths -eq 0) { $CertValidityInMonths = Get-DefaultCertValidityInMonths $Env } if ($CertRenewalPercentage -eq 0) { $CertRenewalPercentage = Get-DefaultCertRenewalPercentage $Env } Write-Host "`n---------------- Target policy: Begin ----------------------" Write-Host "Subject: $CertSubject" Write-Host "DNS Name: $CertDnsName" Write-Host "ValidityInMonths: $CertValidityInMonths" Write-Host "RenewalPercentage: $CertRenewalPercentage" Write-Host "KeyNotExportable: $KeyNotExportable" Write-Host "KeyUsage: $KeyUsage" Write-Host "----------------- Target policy: End ----------------------`n" $succeeded = $false for ($i = 0; $i -lt 3; $i++) { try { Write-Host "$($i+1) try: create certificate" $currentCert = Get-AzKeyVaultCertificate -VaultName $KeyVaultName -Name $CertName $certOperation = Get-AzKeyVaultCertificateOperation -VaultName $KeyVaultName -Name $CertName if ($currentCert -and $currentCert.Enabled -and $certOperation.Status -eq "completed") { Write-Host "Certificate already exists and enabled, check policy now" $currentPolicy = Get-AzKeyVaultCertificatePolicy -VaultName $KeyVaultName -Name $CertName Write-Host "`n---------------- Current policy: Begin ----------------------" $currentPolicy | Select-Object * Write-Host "----------------- Current policy: End ----------------------`n" # Check if policy is different or not if ($currentPolicy.SubjectName -eq "CN=$CertSubject" -and (ConvertTo-Json -InputObject $currentPolicy.DnsNames) -eq (ConvertTo-Json -InputObject $CertDnsName) -and ($currentPolicy.ValidityInMonths -eq $CertValidityInMonths) -and ($currentPolicy.RenewAtPercentageLifetime -eq $CertRenewalPercentage) -and (!($currentPolicy.Exportable) -eq $KeyNotExportable) -and ((Compare-Object ($currentPolicy.KeyUsage | Sort-Object) ($KeyUsage | Sort-Object)) -eq $null)) { Write-Host "Certificate policy is valid, skip" Update-AzKeyVaultCertificate -VaultName $KeyVaultName -Name $CertName -Tag $tags -ErrorAction Stop return } } if (!$currentCert) { Write-Host "Certificate does not exist, continue..." } else { Write-Host "Certificate already exists but its policy is not valid, continue..." } if (-not (Get-AzKeyVaultCertificateIssuer -VaultName $KeyVaultName -Name $IssuerName)) { Write-Host "Creating issuer: $IssuerName" Set-AzKeyVaultCertificateIssuer -VaultName $KeyVaultName -IssuerProvider $ProviderName -Name $IssuerName } else { Write-Host "Issuer: $IssuerName already exists" } if ($KeyNotExportable) { $certPolicy = New-AzKeyVaultCertificatePolicy -KeyNotExportable -SubjectName "CN=$CertSubject" -DnsName $CertDnsName -IssuerName $IssuerName -ValidityInMonths $CertValidityInMonths -RenewAtPercentageLifetime $CertRenewalPercentage -KeyUsage $KeyUsage } else { $certPolicy = New-AzKeyVaultCertificatePolicy -SubjectName "CN=$CertSubject" -DnsName $CertDnsName -IssuerName $IssuerName -ValidityInMonths $CertValidityInMonths -RenewAtPercentageLifetime $CertRenewalPercentage -KeyUsage $KeyUsage } Write-Host "Creating certificate" Add-AzKeyVaultCertificate -VaultName $KeyVaultName -Name $CertName -CertificatePolicy $certPolicy -Tag $tags -ErrorAction Stop | Out-Null Check-KeyVault-Certificate -KeyVaultName $KeyVaultName -CertName $CertName $succeeded = $true break } catch { Start-Sleep -s 120 } } if (!($succeeded)) { throw "Create certificate failed" } } function Check-KeyVault-Certificate { param ( [string]$KeyVaultName, [string]$CertName ) $i = 0 do { $i++ Write-Host "$i try, wait until creation operation completes" Start-Sleep -s 10 $certOperation = Get-AzKeyVaultCertificateOperation -VaultName $KeyVaultName -Name $CertName } while ($certOperation.Status -eq "inProgress") if ($certOperation.Status -ne "completed") { Write-Host "Certificate is disabled" Write-Host "Error code: $($certOperation.ErrorCode)" Write-Host "Error message: $($certOperation.ErrorMessage)" throw "Certificate: $CertName can not be enabled, please check!" } Write-Host "Certificate is enabled, done" } function Purge-KeyVault-Certificate { param ( [string]$KeyVaultName, [string]$CertName ) # Wait until deletion operation completes Remove-AzKeyVaultCertificate -VaultName $KeyVaultName -Name $CertName -Force -ErrorAction SilentlyContinue do { Start-Sleep -s 5 $a = Get-AzKeyVaultCertificate -VaultName $KeyVaultName -Name $CertName } while ($a) Remove-AzKeyVaultCertificate -VaultName $KeyVaultName -Name $CertName -Force -InRemovedState do { Start-Sleep -s 5 $a = Get-AzKeyVaultCertificate -VaultName $KeyVaultName -Name $CertName -InRemovedState -ErrorAction SilentlyContinue } while ($a) Write-Host "Purge delete certificate: [$CertName] in key vault: [$KeyVaultName] succeeded" } function New-EventHubSecrets { param ( [string]$VaultName, [string]$ResourceGroup, [string]$EventHubName, [string]$NamespaceName, [string]$ListenPrimarySecretName, [string]$ListenSecondarySecretName, [string]$SendPrimarySecretName, [string]$SendSecondarySecretName ) function New-EventHubSecretHelper { param ( [string]$Descrip, [string]$KeyType, [string]$RuleName, [string]$KeyName, [int]$ExpiryYear, [string]$SecretName ) $eventHubTagName = 'Eventhub:' + $EventHubName $tags = @{ 'Description' = $Descrip; 'ResourceID' = $eventHubTagName; 'UsageType' = 'EventHubConnectionString'; 'SecretType' = 'ConnectionString'; 'KeyType' = $KeyType; 'AuthRule' = $RuleName; 'EventhubNamespace' = $NamespaceName; 'RgName' = $ResourceGroup; 'ResourceName' = $NamespaceName; 'EventHubName' = $EventHubName } $keysFromEH = Get-AzEventHubKey -ResourceGroupName $ResourceGroup -NamespaceName $NamespaceName -EventHubName $EventHubName -Name $RuleName $secretNewValue = $keysFromEH.$KeyName New-AzResourceSecret -VaultName $VaultName -SecretName $SecretName -SecretNewValue $secretNewValue -ExpiryYear $ExpiryYear -Tags $tags } New-EventHubSecretHelper -Descrip 'Stores primary Listen key for EventHub' -KeyType 'Primary' -RuleName 'Listen' -KeyName 'primaryConnectionString' -ExpiryYear 1 -SecretName $ListenPrimarySecretName New-EventHubSecretHelper -Descrip 'Stores secondary Listen key for EventHub' -KeyType 'Secondary' -RuleName 'Listen' -KeyName 'secondaryConnectionString' -ExpiryYear 2 -SecretName $ListenSecondarySecretName New-EventHubSecretHelper -Descrip 'Stores primary Send key for EventHub' -KeyType 'Primary' -RuleName 'Send' -KeyName 'primaryConnectionString' -ExpiryYear 1 -SecretName $SendPrimarySecretName New-EventHubSecretHelper -Descrip 'Stores secondary Send key for EventHub' -KeyType 'Secondary' -RuleName 'Send' -KeyName 'secondaryConnectionString' -ExpiryYear 2 -SecretName $SendSecondarySecretName } function New-CosomosDbSecrets { param ( [string]$CosmosDbResourceGroup, [string]$CosmosDbAccountName, [string]$VaultName, [string]$ResourceGroup, [string]$StampName, [string]$EnvName, [string]$Version, [string]$SvcName, [string]$PrimarySecretName, [string]$PrimaryReadOnlySecretName, [string]$SecondarySecretName, [string]$SecondaryReadOnlySecretName, [string]$primaryConnectionStringSecretName, [string]$secondaryConnectionStringSecretName ) function New-CosmosDbSecretHelper { param ( [string]$KeyName, [int]$ExpiryYear, [bool]$SetConnectionString, [string]$SecretName, [string]$KeyType ) $tags = @{ 'Stamp' = $StampName; 'Environment' = $EnvName; 'Build' = $Version; 'Microservice' = $SvcName; 'SecretType' = 'CosmosDbKey'; 'KeyType' = $KeyType; 'RgName' = $ResourceGroup; 'ResourceName' = $CosmosDbAccountName } # Use common subscription ID if not specified $subscriptionId = Get-SubscriptionId -Env $EnvName $resourceId = "/subscriptions/$subscriptionId/resourceGroups/$CosmosDbResourceGroup/providers/Microsoft.DocumentDB/databaseAccounts/$CosmosDbAccountName" if ($SetConnectionString) { $connectionStringObject = Invoke-AzResourceAction -Action listConnectionStrings -ResourceId $resourceId -Force -ErrorAction Stop for ($i = 0; $i -lt $connectionStringObject.connectionStrings.Length; $i++) { if ($connectionStringObject.connectionStrings[$i].description -eq $KeyName) { $secretNewValue = $connectionStringObject.connectionStrings[$i].connectionString break } } } else { $keysFromDb = Invoke-AzResourceAction -Action listKeys -ResourceId $resourceId -Force -ErrorAction Stop $SecretNewValue = $keysFromDb.$KeyName } New-AzResourceSecret -VaultName $VaultName -SecretName $SecretName -SecretNewValue $secretNewValue -ExpiryYear $ExpiryYear -Tags $tags } New-CosmosDbSecretHelper -KeyName 'primaryMasterKey' -ExpiryYear 1 -SetConnectionString $false -SecretName $PrimarySecretName -KeyType 'Primary' New-CosmosDbSecretHelper -KeyName 'primaryReadonlyMasterKey' -ExpiryYear 1 -SetConnectionString $false -SecretName $PrimaryReadOnlySecretName -KeyType 'PrimaryReadOnly' New-CosmosDbSecretHelper -KeyName 'secondaryMasterKey' -ExpiryYear 2 -SetConnectionString $false -SecretName $SecondarySecretName -KeyType 'Secondary' New-CosmosDbSecretHelper -KeyName 'secondaryReadonlyMasterKey' -ExpiryYear 2 -SetConnectionString $false -SecretName $SecondaryReadOnlySecretName -KeyType 'SecondaryReadOnly' New-CosmosDbSecretHelper -KeyName 'Primary SQL Connection String' -ExpiryYear 1 -SetConnectionString $true -SecretName $primaryConnectionStringSecretName -KeyType 'Primary' New-CosmosDbSecretHelper -KeyName 'Secondary SQL Connection String' -ExpiryYear 2 -SetConnectionString $true -SecretName $secondaryConnectionStringSecretName -KeyType 'Secondary' } function New-EventGridSecrets { param ( [string]$VaultName, [string]$ResourceGroup, [string]$StampName, [string]$EnvName, [string]$Version, [string]$PrimarySecretName, [string]$SecondarySecretName, [string]$EndpointSecretName, [string]$EventGridName, [string]$EventGridResourceGroup ) function New-EventGridSecretHelper { param ( [string]$KeyName, [string]$ExpiryYear, [bool]$SetEndpointSecret, [string]$SecretName, [string]$KeyType, [string]$Descrip ) $tags = @{ 'Stamp' = $StampName; 'Environment' = $EnvName; 'Build' = $Version; 'SecretType' = 'Key'; 'KeyType' = $KeyType; 'Description' = $Descrip; 'UsageType' = 'EventGridTopicKey'; 'RgName' = $EventGridResourceGroup; 'ResourceName' = $EventGridName } if ($SetEndpointSecret) { $keysFromEG = Get-AzEventGridTopic -ResourceGroupName $EventGridResourceGroup -Name $EventGridName $secretNewValue = $keysFromEG.Endpoint } else { $keysFromEG = Get-AzEventGridTopicKey -ResourceGroup $EventGridResourceGroup -Name $EventGridName $secretNewValue = $keysFromEG.$KeyName } New-AzResourceSecret -VaultName $VaultName -SecretName $SecretName -SecretNewValue $secretNewValue -ExpiryYear $ExpiryYear -Tags $tags } New-EventGridSecretHelper -KeyName 'Key1' -ExpiryYear 1 -SetEndpointSecret $false -SecretName $PrimarySecretName -KeyType 'Primary' -Descrip 'Stores primary read key for EventGrid topic' New-EventGridSecretHelper -KeyName 'Key2' -ExpiryYear 2 -SetEndpointSecret $false -SecretName $SecondarySecretName -KeyType 'Secondary' -Descrip 'Stores secondary read key for EventGrid topic' New-EventGridSecretHelper -KeyName '' -ExpiryYear 1 -SetEndpointSecret $true -SecretName $EndpointSecretName -KeyType 'Primary' -Descrip 'Stores topic endpoint for EventGrid topic' } function New-StorageSecrets { param ( [string]$StorageAccountName, [string]$VaultName, [string]$ResourceGroup, [string]$StampName, [string]$EnvName, [string]$Version, [string]$SvcName, [string]$StorageEndpointSuffix, [string]$PrimarySecretName, [string]$SecondarySecretName, [string]$PrimaryConnectionStringSecretName, [string]$SecondaryConnectionStringSecretName ) function New-StorageSecretHelper { param ( [string]$Index, [int]$ExpiryYear, [bool]$SetConnectionString, [string]$SecretName, [string]$KeyType ) $tags = @{ 'Stamp' = $StampName; 'Environment' = $EnvName; 'Build' = $Version; 'Microservice' = $SvcName; 'SecretType' = 'StorageKey'; 'KeyType' = $KeyType; 'RgName' = $ResourceGroup; 'ResourceName' = $StorageAccountName } $keysFromSA = Invoke-AzResourceAction -Action listKeys -ResourceType 'Microsoft.Storage/storageAccounts' -ApiVersion '2019-06-01' -ResourceGroupName $ResourceGroup -Name $StorageAccountName -Force -ErrorAction Stop $keyValue = $keysFromSA.keys[$Index].value if ($SetConnectionString) { $secretNewValue = 'DefaultEndpointsProtocol=https;AccountName=' + $StorageAccountName + ';AccountKey=' + $keyValue + ';EndpointSuffix=' + $StorageEndpointSuffix } else { $secretNewValue = $keyValue } New-AzResourceSecret -VaultName $VaultName -SecretName $SecretName -SecretNewValue $secretNewValue -ExpiryYear $ExpiryYear -Tags $tags } New-StorageSecretHelper -Index 0 -ExpiryYear 1 -SetConnectionString $false -SecretName $PrimarySecretName -KeyType 'Primary' New-StorageSecretHelper -Index 1 -ExpiryYear 2 -SetConnectionString $false -SecretName $SecondarySecretName -KeyType 'Secondary' New-StorageSecretHelper -Index 0 -ExpiryYear 1 -SetConnectionString $true -SecretName $PrimaryConnectionStringSecretName -KeyType 'Primary' New-StorageSecretHelper -Index 1 -ExpiryYear 2 -SetConnectionString $true -SecretName $SecondaryConnectionStringSecretName -KeyType 'Secondary' } function New-ServiceBusSecrets { param ( [string]$NamespaceName, [string]$VaultName, [string]$ResourceGroup, [string]$StampName, [string]$EnvName, [string]$Version, [string]$SvcName, [string]$SecretName ) function New-ServiceBusSecretHelper { param ( [int]$ExpiryYear, [string]$SecretName, [string]$KeyType, [string]$KeyName ) $tags = @{ 'Stamp' = $StampName; 'Environment' = $EnvName; 'Build' = $Version; 'Microservice' = $SvcName; 'SecretType' = 'ServiceBusKey'; 'KeyType' = $KeyType; 'RgName' = $ResourceGroup; 'ResourceName' = $NamespaceName } $keysFromSB = Get-AzServiceBusKey -ResourceGroupName $ResourceGroup -Namespace $NamespaceName -Name 'RootManageSharedAccessKey' $secretNewValue = $keysFromSB.$KeyName New-AzResourceSecret -VaultName $VaultName -SecretName $SecretName -SecretNewValue $secretNewValue -ExpiryYear $ExpiryYear -Tags $tags } New-ServiceBusSecretHelper -ExpiryYear 1 -SecretName $SecretName -KeyType 'Primary' -KeyName 'primaryConnectionString' } function New-RedisSecrets { param ( [string]$VaultName, [string]$ResourceGroup, [string]$PrimarySecretName, [string]$SecondarySecretName, [string]$RedisName, [string]$RedisResourceGroup, [string]$RedisEndpoint ) function New-RedisSecretHelper { param ( [int]$ExpiryYear, [string]$SecretName, [string]$KeyName ) $tags = @{ 'RgName' = $RedisResourceGroup; 'ResourceName' = $RedisName } $keysFromRC = Get-AzRedisCacheKey -ResourceGroupName $RedisResourceGroup -Name $RedisName $placeholderPwd = $keysFromRC.$KeyName $secretNewValue = $RedisName + '.' + $RedisEndpoint + ':6380,password=' + $placeholderPwd + ',ssl=True,abortConnect=False' New-AzResourceSecret -VaultName $VaultName -SecretName $SecretName -SecretNewValue $secretNewValue -ExpiryYear $ExpiryYear -Tags $tags } New-RedisSecretHelper -ExpiryYear 1 -SecretName $PrimarySecretName -KeyName 'primaryKey' New-RedisSecretHelper -ExpiryYear 2 -SecretName $SecondarySecretName -KeyName 'secondaryKey' } function New-AzResourceSecret { param ( [string]$VaultName, [string]$SecretName, [string]$SecretNewValue, [int]$ExpiryYear = 2, [object]$Tags ) $cpcTag = @{ 'CreatedBy' = 'CPC' } $Tags = $Tags + $cpcTag | Select-Object -uniq $secret = Get-AzKeyVaultSecret -VaultName $VaultName -Name $SecretName $secretOriginalValue = Get-AzKeyVaultSecret -VaultName $VaultName -Name $SecretName -AsPlainText if ($secret -ne $null) { Update-AzKeyVaultSecret -VaultName $VaultName -Name $SecretName -Tag $Tags -ErrorAction Stop } if (($secret -eq $null) -or ($secret.Expires -eq $null) -or ($secretOriginalValue -ne $SecretNewValue)) { Write-Output 'Ready to Set Secret. Secret Name is ' + $SecretName $secretValue = ConvertTo-SecureString -String $SecretNewValue -AsPlainText -Force $expireAt = (Get-Date).AddYears($ExpiryYear).ToUniversalTime() Set-AzKeyVaultSecret -VaultName $VaultName -Name $SecretName -SecretValue $secretValue -Expires $expireAt -Tags $Tags -ErrorAction Stop } } function TrafficManagerLocator { param ( [string]$ResourceGroupName, [string]$TrafficManagerName, [string]$TargetLocation ) $trafficManager = Get-AzTrafficManagerProfile -Name $TrafficManagerName -ResourceGroupName $ResourceGroupName $trafficManagerEndpoints = $trafficManager.Endpoints $locationArray = @() foreach ($endpoint in $trafficManagerEndpoints) { if ($endpoint.EndpointStatus -eq 'Enabled') { $locationArray += $endpoint.Location.Replace(' ', '').ToLower() } } $isEnabled = $locationArray.Contains($TargetLocation.Replace(' ', '').ToLower()) return $isEnabled } function Get-ASEIP { param ( [string]$SubscriptionsId, [string]$ResourceGroup, [string]$AseName, [string]$Cloud ) $vip = "" $azContext = Get-AzContext $azProfile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile $profileClient = New-Object -TypeName Microsoft.Azure.Commands.ResourceManager.Common.RMProfileClient -ArgumentList ($azProfile) $token = $profileClient.AcquireAccessToken($azContext.Subscription.TenantId) $authHeader = @{ "Content-Type" = "application/json" "Authorization" = "Bearer " + $token.AccessToken } $armEndpoint = "" switch ($Cloud) { "public" { $armEndpoint = "https://management.azure.com/" } "fairfax" { $armEndpoint = "https://management.usgovcloudapi.net/" } default { Write-Host "Could not find ARM endpoint for cloud: $Cloud"; exit 1 } } $restUri = $armEndpoint + "subscriptions/$($subscriptionsId)/resourceGroups/$($resourceGroup)/providers/Microsoft.Web/hostingEnvironments/$($aseName)?api-version=2019-08-01" $ase = Invoke-RestMethod -Uri $restUri -Method Get -Headers $authHeader if (($ase.properties.internalLoadBalancingMode -eq "None") -and ($ase.properties.networkingConfiguration -eq $null)) { # it is ASEv2 type $restUri = $armEndpoint + "subscriptions/$($subscriptionsId)/resourceGroups/$($resourceGroup)/providers/Microsoft.Web/hostingEnvironments/$($aseName)/capacities/virtualip?api-version=2019-08-01" $ase = Invoke-RestMethod -Uri $restUri -Method Get -Headers $authHeader $vip = $ase.properties.serviceIpAddress } else { # ASEv3 if ($ase.properties.internalLoadBalancingMode -eq "None") { # external type $vip = $ase.properties.networkingConfiguration.externalInboundIpAddresses | Out-String } else { # internal type $vip = $ase.properties.networkingConfiguration.internalInboundIpAddresses | Out-String } } $vip = $vip.Trim() return $vip } |