Skip to content

Commit a92dfd9

Browse files
authored
Merge pull request #1 from MicrosoftCloudEssentials-LearningHub/structure
Structure
2 parents 79fc4b6 + 35402ab commit a92dfd9

12 files changed

Lines changed: 1380 additions & 1 deletion

File tree

.gitignore

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# Local .terraform directories
22
.terraform/
3+
*src/.env
4+
app-logs.zip
5+
LogFiles
6+
deploy.log
7+
__pycache__
8+
*.log
9+
*agents_state.json
10+
.env.example
11+
.env_automation
312

413
# .tfstate files
514
*.tfstate
@@ -13,7 +22,6 @@ crash.*.log
1322
# password, private keys, and other secrets. These should not be part of version
1423
# control as they are data points which are potentially sensitive and subject
1524
# to change depending on the environment.
16-
*.tfvars
1725
*.tfvars.json
1826

1927
# Ignore override files as they are usually used to override resources locally and so
@@ -35,3 +43,11 @@ override.tf.json
3543
# Ignore CLI configuration files
3644
.terraformrc
3745
terraform.rc
46+
47+
# .NET build output
48+
**/bin/
49+
**/obj/
50+
51+
# Visual Studio / VS Code
52+
.vs/
53+
.vscode/

README.md

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
# Azure Artifact Signing (Azure DevOps + Terraform)
2+
3+
Costa Rica
4+
5+
[![GitHub](https://img.shields.io/badge/--181717?logo=github&logoColor=ffffff)](https://github.com/)
6+
[brown9804](https://github.com/brown9804)
7+
8+
Last updated: 2026-02-19
9+
10+
----------
11+
12+
`In Azure DevOps, code signing is an automated pipeline step that runs after build, using a cloud‑hosted certificate where the private key never leaves Azure`. The pipeline then uses SignTool + the Artifact Signing dlib to call the service endpoint associated with those resources.
13+
14+
> - “Trusted Signing” = service branding/experience
15+
> - “Microsoft.CodeSigning/*” = the deployable Azure resources you manage with Terraform/ARM.
16+
17+
<details>
18+
<summary><b>List of References </b> (Click to expand)</summary>
19+
20+
- [What is Artifact Signing?](https://learn.microsoft.com/en-us/azure/artifact-signing/overview)
21+
- [Artifact Signing](https://azure.microsoft.com/en-us/products/artifact-signing?msockid=38ec3806873362243e122ce086486339)
22+
23+
</details>
24+
25+
> This repo is a minimal, demo-friendly setup for:
26+
> - Provisioning Azure Artifact Signing (Trusted Signing) resources with Terraform.
27+
> - Building a small Windows .NET executable.
28+
> - Signing it in Azure DevOps using **SignTool + Artifact Signing dlib** (private key stays in Microsoft-managed HSMs).
29+
30+
<img width="1523" height="743" alt="image" src="https://github.com/user-attachments/assets/5617dfde-d84b-4dd9-904f-7669b4de9374" />
31+
32+
## Prereqs
33+
34+
- Azure CLI installed and logged in (`az login`)
35+
- Terraform installed
36+
- Permission to register resource providers + create resources in your subscription. One-time provider registration (per subscription):
37+
38+
```pwsh
39+
az provider register --namespace Microsoft.CodeSigning
40+
```
41+
42+
## What Terraform creates
43+
44+
- Resource group
45+
- Artifact Signing account (`Microsoft.CodeSigning/codeSigningAccounts`)
46+
- Key Vault (RBAC-enabled) for pipeline variables/secrets (created by default)
47+
- Certificate profile (`.../certificateProfiles`)
48+
- Preferred: created by the Azure DevOps pipeline after you set the Key Vault secret `artifactSigningIdentityValidationId` (no second `terraform apply`)
49+
- Optional: created by Terraform if you set `identity_validation_id` and re-apply
50+
- Optional Azure DevOps resources (when `ado_enabled = true`)
51+
- Entra app registration + service principal
52+
- Azure DevOps project/repo/pipeline/service connection + authorizations
53+
54+
<img width="451" height="622" alt="image" src="https://github.com/user-attachments/assets/1306d110-be8f-49a8-96dc-c0354a2a6404" />
55+
56+
From [What is Artifact Signing?](https://learn.microsoft.com/en-us/azure/artifact-signing/overview)
57+
58+
> [!NOTE]
59+
> - **Identity validation** itself is **portal-only** (service requirement). Terraform can’t complete that workflow.
60+
> - After you complete it, set Key Vault secret `artifactSigningIdentityValidationId` and the pipeline will create the certificate profile automatically.
61+
> - If Terraform creates the Azure DevOps service connection (`ado_enabled = true`), it can read the generated WIF **Issuer/Subject** and create the Entra federated credential automatically.
62+
63+
## Deploy with Terraform
64+
65+
Terraform files live in `terraform-infrastructure/`.
66+
67+
1) Edit `terraform-infrastructure/terraform.tfvars` and set a globally-unique account name.
68+
69+
2) Run Terraform:
70+
71+
```pwsh
72+
cd terraform-infrastructure
73+
terraform init
74+
terraform validate
75+
terraform apply -auto-approve
76+
```
77+
78+
3) In Azure portal, open the Artifact Signing account and complete **Identity validation** (portal-only).
79+
80+
4) Copy the **Identity validation Id** from the portal and set it in Key Vault:
81+
82+
```pwsh
83+
$kvName = terraform -chdir=terraform-infrastructure output -raw keyvault_name
84+
az keyvault secret set --vault-name $kvName --name artifactSigningIdentityValidationId --value "00000000-0000-0000-0000-000000000000"
85+
```
86+
87+
5) Run the Azure DevOps pipeline. The `AzureCLI@2` step will create the certificate profile if it doesn’t exist yet, then sign the binaries.
88+
89+
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.
90+
91+
## Azure DevOps (Terraform-managed)
92+
93+
This repo can also create the Azure DevOps project/repo/pipeline/service-connection using the Terraform `azuredevops` provider.
94+
95+
1) Create a PAT in Azure DevOps with permissions to manage projects/repos/pipelines/service connections.
96+
97+
2) Set env vars (PowerShell):
98+
99+
```pwsh
100+
$env:AZDO_ORG_SERVICE_URL = "https://dev.azure.com/<your-org>"
101+
$env:AZDO_PERSONAL_ACCESS_TOKEN = "<your-pat>"
102+
```
103+
104+
3) Enable the Azure DevOps resources in Terraform.
105+
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
111+
```
112+
113+
Alternative: set `ado_enabled = true` and `ado_org_service_url` in `terraform.tfvars`.
114+
115+
4) Terraform will:
116+
- create the Azure DevOps project + repo + YAML pipeline
117+
- create the AzureRM service connection using Workload Identity Federation (WIF)
118+
- read the generated WIF Issuer/Subject and create the Entra federated credential
119+
120+
Notes:
121+
- 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.
122+
- 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).
123+
124+
## Pipeline
125+
126+
- [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.
127+
- If `keyVaultName` is set, the pipeline loads signing values from Key Vault via `AzureKeyVault@2`.
128+
129+
Minimum pipeline variables (when not using the Terraform-managed variable group):
130+
- `azureServiceConnection` (service connection name; default `sc-artifact-signing`)
131+
- `keyVaultName` (Terraform output `keyvault_name`)
132+
- `artifactSigningResourceGroupName` (Terraform `resource_group_name`)
133+
134+
Optional overrides (normally provided by Key Vault):
135+
- `artifactSigningEndpoint`
136+
- `artifactSigningAccountName`
137+
- `artifactSigningCertificateProfileName`
138+
- `artifactSigningIdentityValidationId` (only required until the profile exists)
139+
140+
Optional (only used when creating the certificate profile):
141+
- `artifactSigningCertificateProfileType` (defaults to `PublicTrust` if unset)
142+
- `adoServicePrincipalObjectId`
143+
144+
### Azure Key Vault for pipeline variables
145+
146+
Terraform creates a Key Vault by default and wires RBAC so:
147+
- your current identity can set secrets
148+
- the Azure DevOps service principal can read secrets
149+
150+
Terraform also populates these Key Vault secrets during `terraform apply`:
151+
- `artifactSigningEndpoint`
152+
- `artifactSigningAccountName`
153+
- `artifactSigningCertificateProfileName`
154+
- `artifactSigningIdentityValidationId` (placeholder until you set it after portal validation)
155+
156+
The pipeline automatically loads them (it runs `AzureKeyVault@2` when `keyVaultName` is non-empty).
157+
158+
If signing fails with 403, validate:
159+
- Endpoint matches region
160+
- Service connection identity has `Artifact Signing Certificate Profile Signer` at the certificate profile scope
161+
162+
## Azure Portal link
163+
164+
After `terraform apply`, open the resource group:
165+
https://portal.azure.com/#view/HubsExtension/BrowseResourceGroups
166+
167+
<!-- START BADGE -->
168+
<div align="center">
169+
<img src="https://img.shields.io/badge/Total%20views-1280-limegreen" alt="Total views">
170+
<p>Refresh Date: 2026-02-19</p>
171+
</div>
172+
<!-- END BADGE -->

SigningDemo/Program.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
using System;
2+
3+
Console.WriteLine("Hello from SigningDemo at " + DateTimeOffset.UtcNow.ToString("u"));

SigningDemo/SigningDemo.csproj

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
</Project>

0 commit comments

Comments
 (0)