Skip to content

Commit 35402ab

Browse files
committed
terraform deployment - base in place
1 parent 46f336a commit 35402ab

5 files changed

Lines changed: 104 additions & 12 deletions

File tree

README.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,14 +101,18 @@ $env:AZDO_ORG_SERVICE_URL = "https://dev.azure.com/<your-org>"
101101
$env:AZDO_PERSONAL_ACCESS_TOKEN = "<your-pat>"
102102
```
103103

104-
3) In `terraform-infrastructure/terraform.tfvars`, set:
104+
3) Enable the Azure DevOps resources in Terraform.
105105

106-
```hcl
107-
ado_enabled = true
108-
ado_org_service_url = "https://dev.azure.com/<your-org>"
106+
Preferred: use the provided var-file:
107+
108+
```pwsh
109+
cd terraform-infrastructure
110+
terraform apply -auto-approve -var-file=terraform.tfvars -var-file=terraform.ado.tfvars
109111
```
110112

111-
4) Apply. Terraform will:
113+
Alternative: set `ado_enabled = true` and `ado_org_service_url` in `terraform.tfvars`.
114+
115+
4) Terraform will:
112116
- create the Azure DevOps project + repo + YAML pipeline
113117
- create the AzureRM service connection using Workload Identity Federation (WIF)
114118
- read the generated WIF Issuer/Subject and create the Entra federated credential

azure-pipelines.yml

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,63 @@
1+
# This pipeline:
2+
# 1) Builds/publishes an unsigned .NET executable
3+
# 2) Optionally loads signing inputs from Azure Key Vault
4+
# 3) Ensures the Trusted Signing certificate profile exists (creates it if needed)
5+
# 4) Signs the binary with SignTool + Artifact Signing dlib
6+
# 5) Verifies and publishes the signed artifact
7+
18
trigger:
29
- main
310

411
pool:
512
vmImage: 'windows-latest'
613

714
variables:
15+
# Build settings for the demo app.
816
buildConfiguration: 'Release'
917
runtimeIdentifier: 'win-x64'
1018

11-
# Update if your service connection name differs.
19+
# AzureRM service connection name in Azure DevOps.
20+
# Terraform defaults this to 'sc-artifact-signing'.
1221
azureServiceConnection: 'sc-artifact-signing'
1322

14-
# Set these as pipeline variables or in a variable group.
15-
# If you set keyVaultName (or Terraform provides it via variable group),
16-
# the AzureKeyVault@2 task below will populate the artifactSigning* variables.
23+
# Key Vault integration:
24+
# - If keyVaultName is non-empty, AzureKeyVault@2 will pull the secrets below
25+
# and populate the corresponding pipeline variables at runtime.
26+
# - If keyVaultName is empty, you must set the artifactSigning* variables
27+
# via pipeline variables or a variable group.
1728
keyVaultName: ''
29+
30+
# Trusted Signing inputs (usually sourced from Key Vault):
1831
artifactSigningEndpoint: ''
1932
artifactSigningAccountName: ''
2033
artifactSigningCertificateProfileName: ''
34+
35+
# Identity validation is portal-only; when the profile does not exist yet,
36+
# set this (preferably as a Key Vault secret) so the pipeline can create it.
2137
artifactSigningIdentityValidationId: ''
38+
39+
# Resource group that contains the Code Signing account.
2240
artifactSigningResourceGroupName: ''
41+
42+
# Certificate profile type to create if missing.
43+
# This must match the Microsoft.CodeSigning API allowed values.
2344
artifactSigningCertificateProfileType: 'PublicTrust'
45+
46+
# Optional: object id of the Azure DevOps service principal.
47+
# If provided, the pipeline will attempt to ensure the signer RBAC role assignment.
48+
# (Terraform can also handle RBAC; this is a best-effort helper.)
2449
adoServicePrincipalObjectId: ''
2550

2651
steps:
52+
# Build the demo app, producing an unsigned SigningDemo.exe.
2753
- task: DotNetCoreCLI@2
2854
displayName: Publish (unsigned)
2955
inputs:
3056
command: publish
3157
projects: '**/SigningDemo.csproj'
3258
arguments: '-c $(buildConfiguration) -r $(runtimeIdentifier) --self-contained false'
3359

60+
# Load signing inputs from Key Vault (only if keyVaultName is set).
3461
- task: AzureKeyVault@2
3562
displayName: Load signing variables from Key Vault
3663
condition: and(succeeded(), ne(variables['keyVaultName'], ''))
@@ -40,6 +67,11 @@ steps:
4067
SecretsFilter: 'artifactSigningEndpoint,artifactSigningAccountName,artifactSigningCertificateProfileName,artifactSigningIdentityValidationId'
4168
RunAsPreJob: false
4269

70+
# Sign the binary.
71+
# Notes:
72+
# - If the certificate profile doesn't exist yet, this step creates it via ARM (az rest).
73+
# - To create the profile, the service connection identity typically needs permission at RG/account scope.
74+
# - To sign, the identity needs the appropriate Artifact Signing signer role.
4375
- task: AzureCLI@2
4476
displayName: Sign with Azure Artifact Signing (SignTool + dlib)
4577
inputs:
@@ -74,6 +106,7 @@ steps:
74106
$profileResourceId = "/subscriptions/$subId/resourceGroups/$rg/providers/Microsoft.CodeSigning/codeSigningAccounts/$accountName/certificateProfiles/$profileName"
75107
$profileUrl = "https://management.azure.com$profileResourceId?api-version=2025-10-13"
76108
109+
# If the profile doesn't exist yet (first run), create it using the identity validation ID.
77110
Write-Host "Ensuring certificate profile exists: $profileResourceId"
78111
79112
$profileExists = $false
@@ -102,6 +135,8 @@ steps:
102135
Write-Host "Created certificate profile: $profileName"
103136
}
104137
138+
# Optional best-effort RBAC helper: assign signer role at the profile scope.
139+
# If Terraform already assigned roles, this is usually unnecessary.
105140
$spObjectId = Get-Trimmed "$(adoServicePrincipalObjectId)"
106141
if (-not [string]::IsNullOrWhiteSpace($spObjectId)) {
107142
$roleName = "Artifact Signing Certificate Profile Signer"
@@ -115,6 +150,10 @@ steps:
115150
116151
az account show --output table
117152
153+
# Download tooling via NuGet extraction:
154+
# - nuget.exe (for pulling packages)
155+
# - Microsoft.Windows.SDK.BuildTools (signtool.exe)
156+
# - Microsoft.ArtifactSigning.Client (Azure.CodeSigning.Dlib.dll)
118157
$tempRoot = Join-Path "$(Agent.TempDirectory)" "artifact-signing"
119158
New-Item -ItemType Directory -Force -Path $tempRoot | Out-Null
120159
@@ -140,6 +179,7 @@ steps:
140179
if (-not $dlib) { throw "Azure.CodeSigning.Dlib.dll not found after extracting Microsoft.ArtifactSigning.Client" }
141180
142181
$metadataPath = Join-Path $tempRoot "metadata.json"
182+
# metadata.json is consumed by the dlib and tells it which account/profile to use.
143183
$metadata = @{
144184
Endpoint = "$(artifactSigningEndpoint)"
145185
CodeSigningAccountName = "$(artifactSigningAccountName)"
@@ -160,14 +200,17 @@ steps:
160200
Write-Host "Using signtool: $($signtool.FullName)"
161201
Write-Host "Using dlib: $($dlib.FullName)"
162202
203+
# Sign in-place using the official SignTool + /dlib flow.
163204
& $signtool.FullName sign /v /debug /fd SHA256 /tr "http://timestamp.acs.microsoft.com" /td SHA256 /dlib "$($dlib.FullName)" /dmdf "$metadataPath" "$signed"
164205
206+
# Verify that Windows considers the resulting file authenticode-signed.
165207
- powershell: |
166208
$ErrorActionPreference = 'Stop'
167209
$signed = "$(Build.ArtifactStagingDirectory)\SigningDemo.signed.exe"
168210
Get-AuthenticodeSignature $signed | Format-List
169211
displayName: Verify signature
170212

213+
# Publish the signed binary as a build artifact.
171214
- task: PublishBuildArtifacts@1
172215
displayName: Publish signed artifact
173216
inputs:
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Azure DevOps provisioning (optional)
2+
# Use with:
3+
# terraform apply -var-file=terraform.tfvars -var-file=terraform.ado.tfvars
4+
#
5+
# Required environment variables for the azuredevops provider:
6+
# $env:AZDO_ORG_SERVICE_URL = "https://dev.azure.com/<your-org>"
7+
# $env:AZDO_PERSONAL_ACCESS_TOKEN = "<your-pat>"
8+
#
9+
# NOTE: Terraform can create the ADO project/repo/pipeline/service-connection,
10+
# but it does not push this repo's contents into the new ADO repo.
11+
12+
ado_enabled = true
13+
14+
# If you prefer setting the org URL as a variable instead of env var, uncomment:
15+
# ado_org_service_url = "https://dev.azure.com/<your-org>"
16+
17+
# Names (safe defaults)
18+
ado_project_name = "ArtifactSigningDemo"
19+
ado_repo_name = "Azure-ArtifactSigning-DevOps"
20+
ado_pipeline_name = "artifact-signing-demo"
21+
22+
# Service connection name MUST match azureServiceConnection in azure-pipelines.yml
23+
ado_service_connection_name = "sc-artifact-signing"
24+
25+
# Default: secretless auth (requires org feature enabled)
26+
ado_service_endpoint_authentication_scheme = "WorkloadIdentityFederation"
27+
28+
# If your org can't use WIF yet, switch to ServicePrincipal and provide the secret via:
29+
# $env:TF_VAR_ado_service_principal_client_secret = "..."
30+
# ado_service_endpoint_authentication_scheme = "ServicePrincipal"

terraform-infrastructure/terraform.tfvars

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
location = "eastus"
55
resource_group_name = "RG-artifact-signing-demo"
6-
code_signing_account_name = "aasdemoREPLACE_ME"
6+
code_signing_account_name = "aasdemo-replace-me"
77

88
# After you complete Identity validation in the Azure portal, paste the Identity validation Id here.
99
identity_validation_id = null

terraform-infrastructure/variables.tf

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@ variable "resource_group_name" {
1313
variable "code_signing_account_name" {
1414
type = string
1515
description = "Artifact Signing (Trusted Signing) account name. Must be globally unique, 3-24 characters."
16+
17+
validation {
18+
condition = (
19+
length(var.code_signing_account_name) >= 3 &&
20+
length(var.code_signing_account_name) <= 24 &&
21+
can(regex("^[A-Za-z][A-Za-z0-9]*(?:-[A-Za-z0-9]+)*$", var.code_signing_account_name))
22+
)
23+
error_message = "code_signing_account_name must be 3-24 chars, start with a letter, contain only letters/numbers/hyphens, and not contain underscores. Example: aasdemo-abc123."
24+
}
1625
}
1726

1827
variable "code_signing_sku" {
@@ -85,8 +94,14 @@ variable "ado_org_service_url" {
8594
nullable = true
8695

8796
validation {
88-
condition = var.ado_enabled == false || (var.ado_org_service_url != null && length(trimspace(var.ado_org_service_url)) > 0)
89-
error_message = "When ado_enabled=true you must set ado_org_service_url (or AZDO_ORG_SERVICE_URL)."
97+
condition = (
98+
var.ado_enabled == false || (
99+
length(trimspace(var.ado_org_service_url == null ? "" : var.ado_org_service_url)) > 0 &&
100+
can(regex("^https://dev\\.azure\\.com/[^\\s/]+$", trimspace(var.ado_org_service_url == null ? "" : var.ado_org_service_url))) &&
101+
!strcontains(upper(trimspace(var.ado_org_service_url == null ? "" : var.ado_org_service_url)), "REPLACE_ME")
102+
)
103+
)
104+
error_message = "When ado_enabled=true you must set ado_org_service_url to a real org URL like https://dev.azure.com/your-org (not REPLACE_ME)."
90105
}
91106
}
92107

0 commit comments

Comments
 (0)