Skip to content

Commit 2589d44

Browse files
committed
fixing deployment issues
1 parent c72bee9 commit 2589d44

8 files changed

Lines changed: 261 additions & 57 deletions

File tree

README.md

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -41,22 +41,28 @@ Last updated: 2026-02-19
4141

4242
- Resource group
4343
- Artifact Signing account (`Microsoft.CodeSigning/codeSigningAccounts`)
44-
- (Optional, Terraform-deployed) Certificate profile (`.../certificateProfiles`) once you provide an **Identity validation Id**
44+
- Key Vault (RBAC-enabled) for pipeline variables/secrets
45+
- (Optional) Certificate profile (`.../certificateProfiles`) can be created either:
46+
- by Terraform (if you set `identity_validation_id` and re-apply), or
47+
- by the Azure DevOps pipeline automatically (after you set a Key Vault secret; no Terraform re-run)
4548
- (Optional, Terraform-deployed) Microsoft Entra app registration + service principal for an Azure DevOps **Workload Identity Federation** service connection
4649
- (Optional, Terraform-deployed) Azure DevOps resources (when `ado_enabled = true`):
4750
- Project
4851
- Git repo
4952
- YAML pipeline
5053
- AzureRM service connection (Workload Identity Federation)
5154
- Pipeline authorizations for the service connection + variable group
52-
- (Optional, Terraform-deployed) RBAC assignment: `Artifact Signing Certificate Profile Signer` at the certificate profile scope
55+
- (Optional) RBAC assignment(s) for the Azure DevOps service principal
56+
- `Artifact Signing Certificate Profile Signer` at the certificate profile scope (required for signing)
57+
- `Contributor` at the resource group scope (only required if you want the pipeline to create the certificate profile)
5358

5459
<img width="451" height="622" alt="image" src="https://github.com/user-attachments/assets/1306d110-be8f-49a8-96dc-c0354a2a6404" />
5560

5661
From [What is Artifact Signing?](https://learn.microsoft.com/en-us/azure/artifact-signing/overview)
5762

5863
> [!NOTE]
59-
> - **Identity validation** itself is **portal-only** (service requirement). Terraform can’t complete that workflow; you paste the resulting `identity_validation_id` into `terraform.tfvars`.
64+
> - **Identity validation** itself is **portal-only** (service requirement). Terraform can’t complete that workflow.
65+
> - After you complete it, you can avoid a second `terraform apply` by setting the Key Vault secret `artifactSigningIdentityValidationId` and letting the pipeline create the certificate profile.
6066
> - If Terraform creates the Azure DevOps service connection (`ado_enabled = true`), it can also read the generated WIF **Issuer** and **Subject** and create the Entra **federated credential** automatically (no copy/paste).
6167
6268
## Deploy with Terraform
@@ -76,21 +82,20 @@ terraform apply -auto-approve
7682

7783
3) In Azure portal, open the Artifact Signing account and complete **Identity validation** (portal-only).
7884

79-
4) Copy the **Identity validation Id** from the portal and add it to `terraform-infrastructure/terraform.tfvars`:
85+
4) Copy the **Identity validation Id** from the portal and set it in Key Vault (no Terraform re-run required):
8086

81-
```hcl
82-
identity_validation_id = "00000000-0000-0000-0000-000000000000"
83-
certificate_profile_name = "demo-code-signing"
84-
certificate_profile_type = "PublicTrustTest"
87+
```pwsh
88+
az keyvault secret set \
89+
--vault-name <your-keyvault-name> \
90+
--name artifactSigningIdentityValidationId \
91+
--value "00000000-0000-0000-0000-000000000000"
8592
```
8693

87-
5) Apply again to create the certificate profile:
94+
5) Run the Azure DevOps pipeline. The `AzureCLI@2` step will:
95+
- create the certificate profile if it doesn't exist yet
96+
- ensure the `Artifact Signing Certificate Profile Signer` role assignment exists
8897

89-
```pwsh
90-
cd terraform-infrastructure
91-
terraform validate
92-
terraform apply -auto-approve
93-
```
98+
Optional: If you prefer Terraform to manage the certificate profile instead, set `identity_validation_id` in `terraform-infrastructure/terraform.tfvars` and run `terraform apply` again.
9499

95100
## Azure DevOps (Terraform-managed)
96101

@@ -128,21 +133,26 @@ Notes:
128133
- `artifactSigningEndpoint` (Terraform output `artifact_signing_endpoint`)
129134
- `artifactSigningAccountName` (Terraform output `artifact_signing_account_name`)
130135
- `artifactSigningCertificateProfileName` (your profile name)
136+
- `artifactSigningResourceGroupName` (Terraform `resource_group_name`)
137+
- `artifactSigningIdentityValidationId` (only required until the profile exists; best sourced from Key Vault)
138+
- `artifactSigningCertificateProfileType` (defaults to `PublicTrust` if unset)
139+
- `adoServicePrincipalObjectId` (optional; enables the pipeline to ensure RBAC assignment automatically)
131140
- Service connection name in YAML: update `azureSubscription` if you didn't name it `sc-artifact-signing`.
132141

133-
### Optional: use Azure Key Vault for pipeline variables
142+
### Azure Key Vault for pipeline variables
134143

135-
If you enable Key Vault (`keyvault_enabled=true`) Terraform will:
144+
Terraform creates a Key Vault by default. It will:
136145
- create an RBAC-enabled Key Vault
137146
- grant your current identity **Key Vault Secrets Officer** (so you can set secrets)
138147
- grant the Azure DevOps service principal **Key Vault Secrets User** (so the pipeline can read secrets)
139148

140-
Then create secrets in Key Vault with these names:
149+
Terraform also populates these Key Vault secrets during `terraform apply`:
141150
- `artifactSigningEndpoint`
142151
- `artifactSigningAccountName`
143152
- `artifactSigningCertificateProfileName`
153+
- `artifactSigningIdentityValidationId` (placeholder until you set it after portal validation)
144154

145-
The pipeline will automatically load them (it runs `AzureKeyVault@2` when `keyVaultName` is non-empty).
155+
The pipeline automatically loads them (it runs `AzureKeyVault@2` when `keyVaultName` is non-empty).
146156

147157
If signing fails with 403, validate:
148158
- Endpoint matches region

azure-pipelines.yml

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ variables:
1818
artifactSigningEndpoint: ''
1919
artifactSigningAccountName: ''
2020
artifactSigningCertificateProfileName: ''
21+
artifactSigningIdentityValidationId: ''
22+
artifactSigningResourceGroupName: ''
23+
artifactSigningCertificateProfileType: 'PublicTrust'
24+
adoServicePrincipalObjectId: ''
2125

2226
steps:
2327
- task: DotNetCoreCLI@2
@@ -33,7 +37,7 @@ steps:
3337
inputs:
3438
azureSubscription: '$(azureServiceConnection)'
3539
KeyVaultName: '$(keyVaultName)'
36-
SecretsFilter: 'artifactSigningEndpoint,artifactSigningAccountName,artifactSigningCertificateProfileName'
40+
SecretsFilter: 'artifactSigningEndpoint,artifactSigningAccountName,artifactSigningCertificateProfileName,artifactSigningIdentityValidationId'
3741
RunAsPreJob: false
3842

3943
- task: AzureCLI@2
@@ -45,9 +49,69 @@ steps:
4549
inlineScript: |
4650
$ErrorActionPreference = 'Stop'
4751
52+
function Get-Trimmed([string]$value) {
53+
if ($null -eq $value) { return '' }
54+
return $value.Trim()
55+
}
56+
57+
$rg = Get-Trimmed "$(artifactSigningResourceGroupName)"
58+
if ([string]::IsNullOrWhiteSpace($rg)) { throw "Missing variable: artifactSigningResourceGroupName" }
59+
60+
$accountName = Get-Trimmed "$(artifactSigningAccountName)"
61+
$profileName = Get-Trimmed "$(artifactSigningCertificateProfileName)"
62+
$profileType = Get-Trimmed "$(artifactSigningCertificateProfileType)"
63+
if ([string]::IsNullOrWhiteSpace($profileType)) { $profileType = 'PublicTrust' }
64+
4865
if ([string]::IsNullOrWhiteSpace("$(artifactSigningEndpoint)")) { throw "Missing variable: artifactSigningEndpoint" }
49-
if ([string]::IsNullOrWhiteSpace("$(artifactSigningAccountName)")) { throw "Missing variable: artifactSigningAccountName" }
50-
if ([string]::IsNullOrWhiteSpace("$(artifactSigningCertificateProfileName)")) { throw "Missing variable: artifactSigningCertificateProfileName" }
66+
if ([string]::IsNullOrWhiteSpace($accountName)) { throw "Missing variable: artifactSigningAccountName" }
67+
if ([string]::IsNullOrWhiteSpace($profileName)) { throw "Missing variable: artifactSigningCertificateProfileName" }
68+
69+
$identityValidationId = Get-Trimmed "$(artifactSigningIdentityValidationId)"
70+
71+
$subId = (az account show --query id -o tsv)
72+
if ([string]::IsNullOrWhiteSpace($subId)) { throw "Unable to determine subscription id from AzureCLI context." }
73+
74+
$profileResourceId = "/subscriptions/$subId/resourceGroups/$rg/providers/Microsoft.CodeSigning/codeSigningAccounts/$accountName/certificateProfiles/$profileName"
75+
$profileUrl = "https://management.azure.com$profileResourceId?api-version=2025-10-13"
76+
77+
Write-Host "Ensuring certificate profile exists: $profileResourceId"
78+
79+
$profileExists = $false
80+
try {
81+
az rest --method get --url $profileUrl --only-show-errors | Out-Null
82+
$profileExists = $true
83+
} catch {
84+
$profileExists = $false
85+
}
86+
87+
if (-not $profileExists) {
88+
if ([string]::IsNullOrWhiteSpace($identityValidationId)) {
89+
throw "Certificate profile is missing. Complete identity validation in the portal and set Key Vault secret 'artifactSigningIdentityValidationId' (or set pipeline variable artifactSigningIdentityValidationId)."
90+
}
91+
92+
$body = @{
93+
properties = @{
94+
identityValidationId = $identityValidationId
95+
profileType = $profileType
96+
includeStreetAddress = $false
97+
includePostalCode = $false
98+
}
99+
} | ConvertTo-Json -Depth 10
100+
101+
az rest --method put --url $profileUrl --body $body --only-show-errors | Out-Null
102+
Write-Host "Created certificate profile: $profileName"
103+
}
104+
105+
$spObjectId = Get-Trimmed "$(adoServicePrincipalObjectId)"
106+
if (-not [string]::IsNullOrWhiteSpace($spObjectId)) {
107+
$roleName = "Artifact Signing Certificate Profile Signer"
108+
Write-Host "Ensuring RBAC role '$roleName' on profile for SP object id $spObjectId"
109+
try {
110+
az role assignment create --assignee-object-id $spObjectId --assignee-principal-type ServicePrincipal --role $roleName --scope $profileResourceId --only-show-errors | Out-Null
111+
} catch {
112+
Write-Host "Role assignment may already exist; continuing."
113+
}
114+
}
51115
52116
az account show --output table
53117

terraform-infrastructure/.terraform.lock.hcl

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)