|
| 1 | +# Create an environment |
| 2 | + |
| 3 | +This is the initial manual process to create a new Azure environment (e.g. `dev`, `preprod`, `prod`). |
| 4 | + |
| 5 | +All commands assume you are authenticated to Azure via `az login` and are running from the root of this repository unless stated otherwise. |
| 6 | + |
| 7 | +## Prerequisites |
| 8 | + |
| 9 | +- Owner role on both the hub and spoke Azure subscriptions |
| 10 | +- Access to Azure DevOps project `manage-breast-screening-gateway` at `https://dev.azure.com/nhse-dtos` |
| 11 | +- Access to the GitHub repository `NHSDigital/manage-breast-screening-gateway` |
| 12 | +- Access to an Azure Virtual Desktop (AVD) session connected to the internal network |
| 13 | + |
| 14 | +## Azure resource providers |
| 15 | + |
| 16 | +The following resource providers must be registered on the **spoke subscription** before running Terraform. Missing registrations will cause obscure errors during `terraform apply`. |
| 17 | + |
| 18 | +Run these once per subscription: |
| 19 | + |
| 20 | +```bash |
| 21 | +az provider register --namespace Microsoft.HybridCompute |
| 22 | +az provider register --namespace Microsoft.GuestConfiguration |
| 23 | +az provider register --namespace Microsoft.HybridConnectivity |
| 24 | +az provider register --namespace Microsoft.Relay |
| 25 | +az provider register --namespace Microsoft.PolicyInsights |
| 26 | +az provider register --namespace Microsoft.Insights |
| 27 | +az provider register --namespace Microsoft.OperationalInsights |
| 28 | +az provider register --namespace Microsoft.ManagedIdentity |
| 29 | +``` |
| 30 | + |
| 31 | +Verify registration status (wait until all show `Registered`): |
| 32 | + |
| 33 | +```bash |
| 34 | +az provider show --namespace Microsoft.HybridCompute --query registrationState -o tsv |
| 35 | +az provider show --namespace Microsoft.HybridConnectivity --query registrationState -o tsv |
| 36 | +az provider show --namespace Microsoft.GuestConfiguration --query registrationState -o tsv |
| 37 | +``` |
| 38 | + |
| 39 | +## Entra ID |
| 40 | + |
| 41 | +- Create Entra ID group in the `Digital screening` Administrative Unit: |
| 42 | + - `screening_mbsgw_[environment]` |
| 43 | + |
| 44 | +- Ask CCOE to assign the following roles to `mi-mbsgw-[environment]-adotoaz-uks`: |
| 45 | + - [Form for PIM](https://nhsdigitallive.service-now.com/nhs_digital?id=sc_cat_item&sys_id=28f3ab4f1bf3ca1078ac4337b04bcb78&sysparm_category=114fced51bdae1502eee65b9bd4bcbdc) |
| 46 | + - Role Names: `Group.Read.All` and `Directory Readers` |
| 47 | + - Application Name: `mi-mbsgw-[environment]-adotoaz-uks` |
| 48 | + - Managed Identity: `mi-mbsgw-[environment]-adotoaz-uks` |
| 49 | + - Description: Required to resolve the Arc onboarding service principal by display name during Terraform runs |
| 50 | + |
| 51 | +- Confirm the Arc onboarding service principal exists: |
| 52 | + - Name: `spn-azure-arc-onboarding-screening-[environment]` |
| 53 | + - If it does not exist, raise a request with the platform team to create it |
| 54 | + |
| 55 | +## Code |
| 56 | + |
| 57 | +- Create the configuration files in `infrastructure/environments/[environment]/`: |
| 58 | + - `variables.sh` — Azure subscription names, hub subscription, ADO pool, soft delete flag |
| 59 | + - `variables.tfvars` — Terraform variable overrides (can be empty if all defaults apply) |
| 60 | +- Add an `[environment]:` target in `scripts/terraform/terraform.mk` following the existing pattern |
| 61 | +- Add `[environment]` to the `environments` list in the `deploy-stage` step of `.github/workflows/cicd-2-main-branch.yaml` |
| 62 | + |
| 63 | +## Bicep (resource group initialisation) |
| 64 | + |
| 65 | +> [!IMPORTANT] |
| 66 | +> This step creates the Azure infrastructure that Terraform depends on — managed identities, Terraform state storage, Key Vault, and resource groups. It must be completed before running Terraform. |
| 67 | +> **Required**: Owner role on both the hub and spoke subscriptions. |
| 68 | +
|
| 69 | +From an AVD session: |
| 70 | + |
| 71 | +1. Login with Microsoft Graph scope: |
| 72 | + |
| 73 | + ```bash |
| 74 | + az login --scope https://graph.microsoft.com//.default -t HSCIC365.onmicrosoft.com |
| 75 | + ``` |
| 76 | + |
| 77 | +2. Run the Bicep deployment: |
| 78 | + |
| 79 | + ```bash |
| 80 | + make [environment] resource-group-init |
| 81 | + ``` |
| 82 | + |
| 83 | +This deploys two Bicep templates: |
| 84 | + |
| 85 | +**Hub subscription** (`main.bicep`) creates: |
| 86 | + |
| 87 | +- Managed identity `mi-mbsgw-[environment]-adotoaz-uks` — used by ADO to deploy Azure resources via Terraform |
| 88 | +- Managed identity `mi-mbsgw-[environment]-ghtoado-uks` — used by GitHub Actions to trigger ADO pipelines (OIDC federated) |
| 89 | +- Terraform state storage account `sambsgw[environment]tfstate` with private endpoint |
| 90 | +- Infra Key Vault `kv-mbsgw-[environment]-inf` with private endpoint |
| 91 | +- Reader role assigned to `mi-mbsgw-[environment]-ghtoado-uks` at hub subscription scope |
| 92 | + |
| 93 | +**Spoke subscription** (`core.bicep`) creates: |
| 94 | + |
| 95 | +- Resource group `rg-mbsgw-[environment]-uks-arc-enabled-servers` — where Arc machines register |
| 96 | +- Contributor, RBAC Administrator (delegated), and Resource Policy Contributor roles for `mi-mbsgw-[environment]-adotoaz-uks` |
| 97 | +- Contributor and Key Vault Secrets Officer roles for the `screening_mbsgw_[environment]` Entra ID group |
| 98 | + |
| 99 | +Note the outputs printed at the end of the deployment — you will need the managed identity client IDs in subsequent steps. |
| 100 | + |
| 101 | +## Azure DevOps |
| 102 | + |
| 103 | +### ADO group |
| 104 | + |
| 105 | +- Navigate to **Project Settings → Permissions → New group** |
| 106 | +- Name: `Run pipeline - [environment]` |
| 107 | +- Members: `mi-mbsgw-[environment]-ghtoado-uks` |
| 108 | + - There may be more than one identity with a similar name — check the client ID printed below the name matches the one from the Bicep output |
| 109 | +- Permissions (at project level): |
| 110 | + - View project-level information |
| 111 | + |
| 112 | +### ADO pipelines |
| 113 | + |
| 114 | +Create two pipelines, applying the same security settings to each: |
| 115 | + |
| 116 | +**Infrastructure pipeline** — deploys Arc infrastructure via Terraform: |
| 117 | + |
| 118 | +- Navigate to **Pipelines → New pipeline** |
| 119 | +- Source: Azure Repos Git → select this repository |
| 120 | +- Existing Azure Pipelines YAML file: `.azuredevops/pipelines/deploy.yml` |
| 121 | +- Name: `Deploy to Azure - [environment]` |
| 122 | +- Do not run yet |
| 123 | + |
| 124 | +**App pipeline** — deploys the gateway app to Arc machines: |
| 125 | + |
| 126 | +- Navigate to **Pipelines → New pipeline** |
| 127 | +- Source: Azure Repos Git → select this repository |
| 128 | +- Existing Azure Pipelines YAML file: `.azuredevops/pipelines/deploy-app.yml` |
| 129 | +- Name: `Deploy Gateway App - [environment]` |
| 130 | +- Do not run yet |
| 131 | + |
| 132 | +For each pipeline, manage security: |
| 133 | + |
| 134 | +- Navigate to the pipeline → **⋮ → Manage security** |
| 135 | +- Add group: `Run pipeline - [environment]` |
| 136 | +- Grant permissions: |
| 137 | + - Edit queue build configuration |
| 138 | + - Queue builds |
| 139 | + - View build pipeline |
| 140 | + - View builds |
| 141 | + |
| 142 | +### ADO service connection |
| 143 | + |
| 144 | +> [!NOTE] |
| 145 | +> If the Managed Identity dropdown is empty (common with guest accounts), use the **create manually** link and enter the values directly. |
| 146 | +
|
| 147 | +- Navigate to **Project Settings → Service connections → New service connection** |
| 148 | +- Connection type: `Azure Resource Manager` |
| 149 | +- Authentication method: `Workload Identity Federation (manual)` |
| 150 | +- Issuer: `https://token.actions.githubusercontent.com` |
| 151 | +- Subject identifier: `repo:NHSDigital/manage-breast-screening-gateway:environment:[environment]` |
| 152 | +- Enter the following (from the Bicep output or by running the commands below): |
| 153 | + |
| 154 | +```bash |
| 155 | +# Client ID and tenant ID of mi-mbsgw-[environment]-adotoaz-uks |
| 156 | +az identity show --name mi-mbsgw-[environment]-adotoaz-uks \ |
| 157 | + --resource-group rg-mi-[environment]-uks \ |
| 158 | + --subscription "[hub-subscription-name]" \ |
| 159 | + --query "{clientId: clientId, tenantId: tenantId}" -o json |
| 160 | + |
| 161 | +# Spoke subscription ID |
| 162 | +az account show --subscription "[spoke-subscription-name]" --query id -o tsv |
| 163 | +``` |
| 164 | + |
| 165 | +- Scope level: `Subscription` |
| 166 | +- Subscription ID: `[spoke-subscription-id]` |
| 167 | +- Subscription name: `[spoke-subscription-name]` |
| 168 | +- Resource group for service connection: leave blank |
| 169 | +- Service connection name: `mbsgw-[environment]` |
| 170 | +- Do **NOT** tick: Grant access permission to all pipelines |
| 171 | + |
| 172 | +Manage service connection security: |
| 173 | + |
| 174 | +- Navigate to the service connection → **⋮ → Security** |
| 175 | +- Add both `Deploy to Azure - [environment]` and `Deploy Gateway App - [environment]` pipelines with permission to use the connection |
| 176 | + |
| 177 | +### ADO environment |
| 178 | + |
| 179 | +- Navigate to **Pipelines → Environments → New environment** |
| 180 | +- Name: `[environment]` |
| 181 | +- Resource: None |
| 182 | +- Set exclusive lock (all environments except `review`) |
| 183 | +- Add pipeline permission for both `Deploy to Azure - [environment]` and `Deploy Gateway App - [environment]` |
| 184 | + |
| 185 | +## GitHub |
| 186 | + |
| 187 | +- Create a GitHub environment named `[environment]`: |
| 188 | + - Navigate to **Settings → Environments → New environment** |
| 189 | + |
| 190 | +- Add protection rules (all environments except `review`): |
| 191 | + - Deselect `Allow administrators to bypass configured protection rules` |
| 192 | + - In `Deployment branches and tags` choose `Selected branches and tags` |
| 193 | + - Click `Add deployment branch or tag rule` and enter `main` |
| 194 | + |
| 195 | +- Add the following environment secrets (values from `mi-mbsgw-[environment]-ghtoado-uks`): |
| 196 | + |
| 197 | +```bash |
| 198 | +# AZURE_CLIENT_ID — client ID of mi-mbsgw-[environment]-ghtoado-uks |
| 199 | +az identity show --name mi-mbsgw-[environment]-ghtoado-uks \ |
| 200 | + --resource-group rg-mi-[environment]-uks \ |
| 201 | + --subscription "[hub-subscription-name]" \ |
| 202 | + --query clientId -o tsv |
| 203 | + |
| 204 | +# AZURE_TENANT_ID |
| 205 | +az account show --query tenantId -o tsv |
| 206 | + |
| 207 | +# AZURE_SUBSCRIPTION_ID — hub subscription ID (where the managed identity has Reader) |
| 208 | +az account show --subscription "[hub-subscription-name]" --query id -o tsv |
| 209 | +``` |
| 210 | + |
| 211 | +| Secret | Value | |
| 212 | +| ---------------------- | ------------------------------------------------- | |
| 213 | +| `AZURE_CLIENT_ID` | Client ID of `mi-mbsgw-[environment]-ghtoado-uks` | |
| 214 | +| `AZURE_TENANT_ID` | Azure tenant ID | |
| 215 | +| `AZURE_SUBSCRIPTION_ID` | Hub subscription ID | |
| 216 | + |
| 217 | +## First run |
| 218 | + |
| 219 | +1. Merge a pull request to `main` to trigger the CI/CD pipeline, or trigger the ADO pipeline manually from Azure DevOps |
| 220 | +2. On the first run you will be prompted to authorise in ADO: |
| 221 | + - Service connection access by the pipeline |
| 222 | + - Agent pool access by the environment |
| 223 | +3. Check the pipeline completes successfully — Terraform will create: |
| 224 | + - Arc-enabled server RBAC assignments |
| 225 | + - Azure Relay hybrid connections (empty on first deploy — populated as Arc machines onboard) |
| 226 | + - Log Analytics workspace and Data Collection Rule |
| 227 | + - Azure Monitor policy assignments and remediation tasks |
| 228 | + |
| 229 | +## Arc machine discovery |
| 230 | + |
| 231 | +Terraform automatically discovers Arc machines registered in the Arc-enabled servers resource group and creates a relay Hybrid Connection for each one. On first deploy the resource group is empty, so no connections are created. After each Arc machine is onboarded at a hospital site, run `terraform apply` to pick it up: |
| 232 | + |
| 233 | +```bash |
| 234 | +make [environment] terraform-apply |
| 235 | +``` |
0 commit comments