|
| 1 | +# Azure Artifact Signing (Azure DevOps + Terraform) |
| 2 | + |
| 3 | +This repo is a minimal, demo-friendly setup for: |
| 4 | +- Provisioning Azure Artifact Signing (Trusted Signing) resources with Terraform. |
| 5 | +- Building a small Windows .NET executable. |
| 6 | +- Signing it in Azure DevOps using **SignTool + Artifact Signing dlib** (private key stays in Microsoft-managed HSMs). |
| 7 | + |
| 8 | +## What Terraform creates |
| 9 | + |
| 10 | +- Resource group |
| 11 | +- Artifact Signing account (`Microsoft.CodeSigning/codeSigningAccounts`) |
| 12 | +- (Optional, Terraform-deployed) Certificate profile (`.../certificateProfiles`) once you provide an **Identity validation Id** |
| 13 | +- (Optional, Terraform-deployed) Microsoft Entra app registration + service principal for an Azure DevOps **Workload Identity Federation** service connection |
| 14 | +- (Optional, Terraform-deployed) Azure DevOps resources (when `ado_enabled = true`): |
| 15 | + - Project |
| 16 | + - Git repo |
| 17 | + - YAML pipeline |
| 18 | + - AzureRM service connection (Workload Identity Federation) |
| 19 | + - Pipeline authorizations for the service connection + variable group |
| 20 | +- (Optional, Terraform-deployed) RBAC assignment: `Artifact Signing Certificate Profile Signer` at the certificate profile scope |
| 21 | + |
| 22 | +Notes: |
| 23 | +- **Identity validation** itself is **portal-only** (service requirement). Terraform can’t complete that workflow; you paste the resulting `identity_validation_id` into `terraform.tfvars`. |
| 24 | +- 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). |
| 25 | + |
| 26 | +## Prereqs |
| 27 | + |
| 28 | +- Azure CLI installed and logged in (`az login`) |
| 29 | +- Terraform installed |
| 30 | +- Permission to register resource providers + create resources in your subscription |
| 31 | + |
| 32 | +One-time provider registration (per subscription): |
| 33 | + |
| 34 | +```pwsh |
| 35 | +az provider register --namespace Microsoft.CodeSigning |
| 36 | +``` |
| 37 | + |
| 38 | +## Deploy with Terraform |
| 39 | + |
| 40 | +Terraform files live in `terraform-infrastructure/`. |
| 41 | + |
| 42 | +1) Edit `terraform-infrastructure/terraform.tfvars` and set a globally-unique account name. |
| 43 | + |
| 44 | +2) Run Terraform: |
| 45 | + |
| 46 | +```pwsh |
| 47 | +cd terraform-infrastructure |
| 48 | +terraform init |
| 49 | +terraform validate |
| 50 | +terraform apply -auto-approve |
| 51 | +``` |
| 52 | + |
| 53 | +3) In Azure portal, open the Artifact Signing account and complete **Identity validation** (portal-only). |
| 54 | + |
| 55 | +4) Copy the **Identity validation Id** from the portal and add it to `terraform-infrastructure/terraform.tfvars`: |
| 56 | + |
| 57 | +```hcl |
| 58 | +identity_validation_id = "00000000-0000-0000-0000-000000000000" |
| 59 | +certificate_profile_name = "demo-code-signing" |
| 60 | +certificate_profile_type = "PublicTrustTest" |
| 61 | +``` |
| 62 | + |
| 63 | +5) Apply again to create the certificate profile: |
| 64 | + |
| 65 | +```pwsh |
| 66 | +cd terraform-infrastructure |
| 67 | +terraform validate |
| 68 | +terraform apply -auto-approve |
| 69 | +``` |
| 70 | + |
| 71 | +## Azure DevOps (Terraform-managed) |
| 72 | + |
| 73 | +This repo can also create the Azure DevOps project/repo/pipeline/service-connection using the Terraform `azuredevops` provider. |
| 74 | + |
| 75 | +1) Create a PAT in Azure DevOps with permissions to manage projects/repos/pipelines/service connections. |
| 76 | + |
| 77 | +2) Set env vars (PowerShell): |
| 78 | + |
| 79 | +```pwsh |
| 80 | +$env:AZDO_ORG_SERVICE_URL = "https://dev.azure.com/<your-org>" |
| 81 | +$env:AZDO_PERSONAL_ACCESS_TOKEN = "<your-pat>" |
| 82 | +``` |
| 83 | + |
| 84 | +3) In `terraform-infrastructure/terraform.tfvars`, set: |
| 85 | + |
| 86 | +```hcl |
| 87 | +ado_enabled = true |
| 88 | +ado_org_service_url = "https://dev.azure.com/<your-org>" |
| 89 | +``` |
| 90 | + |
| 91 | +4) Apply. Terraform will: |
| 92 | +- create the Azure DevOps project + repo + YAML pipeline |
| 93 | +- create the AzureRM service connection using Workload Identity Federation (WIF) |
| 94 | +- read the generated WIF Issuer/Subject and create the Entra federated credential |
| 95 | + |
| 96 | +Notes: |
| 97 | +- The `WorkloadIdentityFederation` auth scheme requires your org feature to be enabled. If your org can’t use it yet, set `ado_service_endpoint_authentication_scheme = "ServicePrincipal"` and also set `TF_VAR_ado_service_principal_client_secret` for the service principal secret. |
| 98 | +- Terraform creates an empty repo. You still need to push this repo’s code into the Azure DevOps repo (Terraform will output the clone URL). |
| 99 | + |
| 100 | +## Pipeline |
| 101 | + |
| 102 | +- [azure-pipelines.yml](azure-pipelines.yml) builds `SigningDemo.exe`, installs the required signing components via NuGet extraction, then signs using the official SignTool + `/dlib` flow. |
| 103 | +- Configure pipeline variables: |
| 104 | + - `artifactSigningEndpoint` (Terraform output `artifact_signing_endpoint`) |
| 105 | + - `artifactSigningAccountName` (Terraform output `artifact_signing_account_name`) |
| 106 | + - `artifactSigningCertificateProfileName` (your profile name) |
| 107 | + - Service connection name in YAML: update `azureSubscription` if you didn't name it `sc-artifact-signing`. |
| 108 | + |
| 109 | +### Optional: use Azure Key Vault for pipeline variables |
| 110 | + |
| 111 | +If you enable Key Vault (`keyvault_enabled=true`) Terraform will: |
| 112 | +- create an RBAC-enabled Key Vault |
| 113 | +- grant your current identity **Key Vault Secrets Officer** (so you can set secrets) |
| 114 | +- grant the Azure DevOps service principal **Key Vault Secrets User** (so the pipeline can read secrets) |
| 115 | + |
| 116 | +Then create secrets in Key Vault with these names: |
| 117 | +- `artifactSigningEndpoint` |
| 118 | +- `artifactSigningAccountName` |
| 119 | +- `artifactSigningCertificateProfileName` |
| 120 | + |
| 121 | +The pipeline will automatically load them (it runs `AzureKeyVault@2` when `keyVaultName` is non-empty). |
| 122 | + |
| 123 | +If signing fails with 403, validate: |
| 124 | +- Endpoint matches region |
| 125 | +- Service connection identity has `Artifact Signing Certificate Profile Signer` at the certificate profile scope |
| 126 | + |
| 127 | +## Azure Portal link |
| 128 | + |
| 129 | +After `terraform apply`, open the resource group: |
| 130 | +https://portal.azure.com/#view/HubsExtension/BrowseResourceGroups |
0 commit comments