-
-
Save omiossec/e64ec646668d8dda05502f07f9dbf11a to your computer and use it in GitHub Desktop.
<# | |
.SYNOPSIS | |
Create Azure Application, Certificate, SP and link them to Azure Automation Account as Run As Account | |
.DESCRIPTION | |
Create Azure Application, Certificate, SP and link them to Azure Automation Account as Run As Account | |
.PARAMETER ResourceGroupName | |
Name of the resource group where are located Automation Account and Keyvault | |
.PARAMETER AutomationAccount | |
Automation Account Name | |
.PARAMETER KeyVaultName | |
Keyvault name | |
.PARAMETER RunAsName | |
RunAs Account Name | |
#> | |
[CmdletBinding()] | |
param ( | |
[Parameter(Mandatory = $true)] | |
[string] | |
$ResourceGroupName, | |
[Parameter(Mandatory = $true)] | |
[string] | |
$AutomationAccount, | |
[Parameter(Mandatory = $true)] | |
[string] | |
$KeyVaultName | |
) | |
$RunAsAccountName = "$($AutomationAccount)-runas" | |
$CertificatSubjectName = "CN=$($RunAsAccountName)" | |
$AzAppUniqueId = (New-Guid).Guid | |
$AzAdAppURI = "http://$($AutomationAccount)$($AzAppUniqueId)" | |
$AzureKeyVaultCertificatePolicy = New-AzKeyVaultCertificatePolicy -SubjectName $CertificatSubjectName -IssuerName "Self" -KeyType "RSA" -KeyUsage "DigitalSignature" -ValidityInMonths 12 -RenewAtNumberOfDaysBeforeExpiry 20 -KeyNotExportable:$False -ReuseKeyOnRenewal:$False | |
Add-AzKeyVaultCertificate -VaultName $keyvaultName -Name $RunAsAccountName -CertificatePolicy $AzureKeyVaultCertificatePolicy | out-null | |
do { | |
start-sleep -Seconds 20 | |
} until ((Get-AzKeyVaultCertificateOperation -Name $RunAsAccountName -vaultName $keyvaultName).Status -eq "completed") | |
$PfxPassword = -join ((48..57) + (65..90) + (97..122) | Get-Random -Count 48| foreach-object {[char]$_}) | |
$PfxFilePath = join-path -Path (get-location).path -ChildPath "cert.pfx" | |
start-sleep 30 | |
$AzKeyVaultCertificatSecret = Get-AzKeyVaultSecret -VaultName $keyvaultName -Name $RunAsAccountName | |
$AzKeyVaultCertificatSecretBytes = [System.Convert]::FromBase64String($AzKeyVaultCertificatSecret.SecretValueText) | |
$certCollection = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection | |
$certCollection.Import($AzKeyVaultCertificatSecretBytes,$null,[System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable) | |
$protectedCertificateBytes = $certCollection.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Pkcs12, $PfxPassword) | |
[System.IO.File]::WriteAllBytes($PfxFilePath, $protectedCertificateBytes) | |
$AzADApplicationRegistration = New-AzADApplication -DisplayName $RunAsAccountName -HomePage "http://$($RunAsAccountName)" -IdentifierUris $AzAdAppURI | |
$AzKeyVaultCertificatStringValue = [System.Convert]::ToBase64String($certCollection.GetRawCertData()) | |
$AzADApplicationCredential = New-AzADAppCredential -ApplicationId $AzADApplicationRegistration.ApplicationId -CertValue $AzKeyVaultCertificatStringValue -StartDate $certCollection.NotBefore -EndDate $certCollection.NotAfter | |
$AzADServicePrincipal = New-AzADServicePrincipal -ApplicationId $AzADApplicationRegistration.ApplicationId -SkipAssignment | |
$PfxPassword = ConvertTo-SecureString $PfxPassword -AsPlainText -Force | |
New-AzAutomationCertificate -ResourceGroupName $ResourceGroupName -AutomationAccountName $AutomationAccount -Path $PfxFilePath -Name "AzureRunAsCertificate" -Password $PfxPassword -Exportable:$Exportable | |
$ConnectionFieldData = @{ | |
"ApplicationId" = $AzADApplicationRegistration.ApplicationId | |
"TenantId" = (Get-AzContext).Tenant.ID | |
"CertificateThumbprint" = $certCollection.Thumbprint | |
"SubscriptionId" = (Get-AzContext).Subscription.ID | |
} | |
New-AzAutomationConnection -ResourceGroupName $ResourceGroupName -AutomationAccountName $AutomationAccount -Name "AzureRunAsConnection" -ConnectionTypeName "AzureServicePrincipal" -ConnectionFieldValues $ConnectionFieldData | |
Great script. Cheers
I had the same issue where the RunAs account appeared as Incomplete. I'd been working with RunAs accounts a month ago and automated replacement of the assigned Role. Realised this code did not assign the Contributor role, or any other role to the RunAs Account.
Use New-AzRoleAssignment to assign a role of choice.
Hello @ColinEff, can you please elaborate more on the New-AzRoleAssignment thing you introduced to make it work?
Hi @t8mas062184
Snippet from code I used to get around the 'incomplete' issue. It uses a custom role I created for manipulating VMs. Replace the role name with an existing role or custom role. Importantly, a role has to be assigned to the RunAs account
#// -------------------------------------------------------------------
#// Define automation account variables
#// -------------------------------------------------------------------
$azEnvironment = [PSCustomObject]@{
#// Global Settings
Environment = "Production"
SubscriptionId = "11111111-2222-3333-4444-555555555555"
Location = "Australia East"
ResourceGroupName = "company-AUE-PRD-ITOPS"
AutomationAccountName = "company-AUE-PRD-ITOPS-AA"
CustomRoleName = "Virtual Machine Operations (Custom)" # custom role with perms to manage VM state
CustomRoleTempFile = "$scriptPath\az-role.json"
RunBookName = "ITOPS-category-scriptpurpose"
RunBookScript = "$scriptPath\az-script.ps1"
RunBookScriptDependencies = @("Az.Accounts","Az.Compute","Az.Resources")
AssignCustomRole = $False
KeyVaultName = "company-AUE-PRD-ITOPS-KV" # Key Vault Name, 24 chars max
#// Tags for resources (LocalTags) and the Resource Group (GlobalTags)
GlobalTags = @{Environment="Production";Owner="IT Operations Team";Purpose="IT Operations, Management and Automation";Production="True"}
}
#// -------------------------------------------------------------------
#// Create RunAs Account
#// -------------------------------------------------------------------
if ($azEnvironment.createRunAsAccount) {
Write-host "[INFO] Creating KeyVault and RunAs Account for Runbook $($azEnvironment.RunBookName)"
$result = Generate-RunAsAccount -ResourceGroupName $azEnvironment.ResourceGroupName `
-AutomationAccountName $azEnvironment.AutomationAccountName `
-Location $azEnvironment.Location `
-KeyVaultName $azEnvironment.KeyVaultName `
-KeyVaultTag $azEnvironment.GlobalTags
$result
} else {
Write-host "[INFO] Disabled. Skipped creating KeyVault and RunAs Account for Runbook $($azEnvironment.RunBookName)"
}
#// -------------------------------------------------------------------
#// Assign Custom Role to Automation Account RunAs Account (replace contributor role)
#// -------------------------------------------------------------------
if ($azEnvironment.assignCustomRole) {
$Confirm = $False
Write-host "[INFO] Assiging custom role $($azEnvironment.CustomRoleName) to RunAs account for AA $($azEnvironment.AutomationAccountName)"
# Force replace Contributor role if set to True
$ReplaceCustomRoleAssignment = $True
# Retrieve Automation Account
$AutomationAccount = Get-AzAutomationAccount -Name $azEnvironment.AutomationAccountName -ResourceGroupName $azEnvironment.ResourceGroupName
# Retrieve Automation Account App ID
$runasAccountAADAplicationId = Get-RunAsAccountAADApplicationId -ResourceGroupName $AutomationAccount.ResourceGroupName -AutomationAccountName $AutomationAccount.AutomationAccountName
# Replace role assignment
if ($runasAccountAADAplicationId) {
$subscriptionScope = "/subscriptions/" + $azEnvironment.SubscriptionId
#$subscriptionScope = $SubscriptionPath
# Retrieve existing role assignments
if ($ReplaceCustomRoleAssignment -eq $true) {
$currentRoleAssignments = Get-AzRoleAssignment `
-ServicePrincipalName $runasAccountAADAplicationId `
-Scope $subscriptionScope `
-ErrorAction Stop
} else {
# Retrieve 'assumed' contributor role assignment if ReplaceCustomRoleAssignment is false
$currentRoleAssignments = Get-AzRoleAssignment `
-ServicePrincipalName $runasAccountAADAplicationId `
-RoleDefinitionName "Contributor" `
-Scope $subscriptionScope `
-ErrorAction Stop
}
Write-Host "[INFO] Role assignments in this automation account:"
$currentRoleAssignments
# Check if contributor role is already assigned, if so nothing to do, exit
$hasContributorRoleAssignment = $false
foreach ($roleAssignment in $currentRoleAssignments) {
if ($roleAssignment.RoleDefinitionName -eq "Contributor") {
$hasContributorRoleAssignment = $true
Break
}
}
# Start process to replace existing role with contributor role
if (($hasContributorRoleAssignment -eq $true) -or ($ReplaceCustomRoleAssignment -eq $true)) {
Write-Host ("[INFO] Deleting existing role assignments from " + $automationAccount.AutomationAccountName)
foreach ($roleAssignment in $currentRoleAssignments)
{
if (($roleAssignment.RoleDefinitionName -eq "Contributor") -or ($ReplaceCustomRoleAssignment -eq $true))
{
Remove-AzRoleAssignment -InputObject $roleAssignment -ErrorAction Stop
Write-Host ("[INFO] Deleted role assignment to " + $roleAssignment.RoleDefinitionName)
}
}
Write-Host ("[INFO] Creating new role assignment for " + $automationAccount.AutomationAccountName)
$customRoleAssignment = New-AzRoleAssignment `
-RoleDefinitionName $azEnvironment.CustomRoleName `
-ServicePrincipalName $runasAccountAADAplicationId `
-ErrorAction Stop
Write-Host "[INFO] Created a new role assignment:" -ForegroundColor Green
$customRoleAssignment
} else {
Write-Host ("[ERROR] Automation account: " + $automationAccount.AutomationAccountName + " was not changed because Contributor role was not assigned to RunAs account.") -ForegroundColor Yellow
Write-Host
}
} else {
Write-Host ("[ERROR] Automation account: " + $AutomationAccount.AutomationAccountName + " was not changed because RunAs account does not exist.") -ForegroundColor Yellow
Write-Host
}
}
Thank you for your prompt response, @ColinEff! Creating RunAs Account is my first task in Azure and I find it challenging. This would definitely be a big help. I am creating my Automation Account via Ansible playbook using azure_rm_automationaccount (azcollections) but it lacks the functionality to create the RunAs Account. I'll see how this would be integrated with my Ansible playbook I've formulated for Automation Account creation.
Cheers!
Hi,
The script needs to be adjusted with the below to solve the deprecation of the .secretvaluetext
#THIS SECTION IS REPLACED ADDED AS THE .secretvaluetext OPERATOR IS DEPRECATED
#$AzKeyVaultCertificatSecretBytes = [System.Convert]::FromBase64String($AzKeyVaultCertificatSecret.SecretValueText)
#THE BELOW IS THE REPLACEMENT
$secretValueText = '';
$ssPtr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($AzKeyVaultCertificatSecret.SecretValue)
try {
$secretValueText = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($ssPtr)
} finally {
[System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($ssPtr)
}
$AzKeyVaultCertificatSecretBytes = [Convert]::FromBase64String($secretValueText)
Great script. Cheers
I had the same issue where the RunAs account appeared as Incomplete. I'd been working with RunAs accounts a month ago and automated replacement of the assigned Role. Realised this code did not assign the Contributor role, or any other role to the RunAs Account.
Use New-AzRoleAssignment to assign a role of choice.