functions/Posh-ACME/Invoke-SetupPoshACME.ps1
# based on https://github.com/rmbolger/Posh-ACME/blob/main/docs/Plugins/Azure.md as of 2025-06-10 function Invoke-SetupPoshACME { [CmdletBinding()] param ( [string]$TenantId, [string]$SubscriptionId, [string]$ServicePrincipalName, [Parameter(Mandatory = $true)] [string]$ResourceGroupName, [string]$DnsTxtContributorRoleName = 'DNS TXT Contributor', [string]$PoshACMEServicePrincipalDisplayName = 'PoshACME', [int]$ValidYears = 5 ) begin { $doConnectWithStoredServicePrincipalCredentials = (-not ([string]::IsNullOrWhiteSpace($TenantId) -or [string]::IsNullOrWhiteSpace($SubscriptionId) -or [string]::IsNullOrWhiteSpace($ServicePrincipalName))) } process { # Connect to Azure if ($doConnectWithStoredServicePrincipalCredentials) { $connected = Test-AzContextAndConnect -TenantId $TenantId -SubscriptionId $SubscriptionId -ServicePrincipalName $ServicePrincipalName -Endpoint https://graph.microsoft.com } if (-not $connected) { $connectParams = @{} if (-not [string]::IsNullOrWhiteSpace($TenantId)) { $connectParams += @{ Tenant = $TenantId } } if (-not [string]::IsNullOrWhiteSpace($SubscriptionId)) { $connectParams += @{ Subscription = $SubscriptionId } } $az = Connect-AzAccount @connectParams $tenantID = $az.Context.Subscription.TenantId $subscriptionID = $az.Context.Subscription.Id } else { $tenantId = $TenantId $subscriptionId = $SubscriptionId } # Create a Custom Role if (-not ($roleDef = Get-AzRoleDefinition -Name $DnsTxtContributorRoleName)) { $roleDef = Get-AzRoleDefinition -Name "DNS Zone Contributor" $roleDef.Id = $null $roleDef.Name = $DnsTxtContributorRoleName $roleDef.Description = "Manage DNS TXT records only." $roleDef.Actions.RemoveRange(0,$roleDef.Actions.Count) $roleDef.Actions.Add("Microsoft.Network/dnsZones/TXT/*") $roleDef.Actions.Add("Microsoft.Network/dnsZones/read") $roleDef.Actions.Add("Microsoft.Authorization/*/read") $roleDef.Actions.Add("Microsoft.Insights/alertRules/*") $roleDef.Actions.Add("Microsoft.ResourceHealth/availabilityStatuses/read") $roleDef.Actions.Add("Microsoft.Resources/deployments/read") $roleDef.Actions.Add("Microsoft.Resources/subscriptions/resourceGroups/read") $roleDef.AssignableScopes.Clear() $roleDef.AssignableScopes.Add("/subscriptions/$($subscriptionId)") $null = New-AzRoleDefinition $roleDef } else { Write-Warning "Role definition $DnsTxtContributorRoleName already exists!" } #Create a Service Principal / App Registration $existingSp = Get-AzADServicePrincipal -DisplayName $PoshACMEServicePrincipalDisplayName if ($existingSp) { Write-Warning "Service principal $PoshACMEServicePrincipalDisplayName already exists!" $sp = $existingSp } else { $notBefore = Get-Date $notAfter = $notBefore.AddYears($ValidYears) $spParams = @{ DisplayName = $PoshACMEServicePrincipalDisplayName StartDate = $notBefore EndDate = $notAfter } try { $sp = New-AzADServicePrincipal @spParams } catch { # TODO test error for 'Insufficient privileges to complete the operation' throw $_ } } # TODO add support for certificate based service principal $spPass = $sp.PasswordCredentials.SecretText | ConvertTo-SecureString -AsPlainText -Force $appCred = [pscredential]::new($sp.AppId,$spPass) # Assign Permissions to the Service Principal $raParams = @{ ApplicationId = $sp.AppId ResourceGroupName = $ResourceGroupName RoleDefinitionName = $DnsTxtContributorRoleName } $null = New-AzRoleAssignment @raParams $pArgs = @{ AZSubscriptionId = $subscriptionID AZTenantId = $tenantID AZAppCred = $appCred } $null = Set-AzStoredServicePrincipalCredential -TenantId $TenantId -SubscriptionId $SubscriptionId -ServicePrincipalName $PoshACMEServicePrincipalDisplayName -ApplicationId $sp.AppId -ClientSecret $spPass } end { return $pArgs } } |