|
| 1 | +# Azure Container Registry Building Block - Backplane |
| 2 | + |
| 3 | +This directory contains the "backplane" configuration for the Azure Container Registry Building Block. The backplane sets up the necessary permissions and service principals required to deploy Azure Container Registries in Azure subscriptions. |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +The backplane creates: |
| 8 | +- Custom Azure RBAC role definitions with ACR deployment permissions |
| 9 | +- Optional service principal for automated deployments (main scope) |
| 10 | +- **Optional separate service principal for hub VNet peering (least privilege)** |
| 11 | +- Role assignments for the service principals or existing principals |
| 12 | +- Support for workload identity federation or application passwords |
| 13 | +- Separate scopes: main scope (subscription or management group) and hub scope (subscription or management group) |
| 14 | + |
| 15 | +## Required Permissions |
| 16 | + |
| 17 | +The backplane creates two role definitions: |
| 18 | + |
| 19 | +### Main Deployment Role (`buildingblock_deploy`) |
| 20 | + |
| 21 | +Grants the following permissions: |
| 22 | + |
| 23 | +#### Container Registry |
| 24 | +- Full CRUD operations on registries |
| 25 | +- Manage credentials, webhooks, replications, scope maps, and tokens |
| 26 | +- Import images |
| 27 | + |
| 28 | +#### Networking |
| 29 | +- Create and manage private endpoints |
| 30 | +- Create and manage private DNS zones and records |
| 31 | +- Create and manage virtual networks, subnets, and VNet peerings |
| 32 | +- Join subnets and peer VNets |
| 33 | + |
| 34 | +#### Resource Management |
| 35 | +- Create, read, and delete resource groups |
| 36 | +- Manage deployments |
| 37 | +- Assign roles (for AKS integration) |
| 38 | + |
| 39 | +### Hub Deployment Role (`buildingblock_deploy_hub`) |
| 40 | + |
| 41 | +Limited permissions for hub VNet peering: |
| 42 | +- Read resource groups and virtual networks |
| 43 | +- Manage virtual network peerings |
| 44 | + |
| 45 | +## Usage |
| 46 | + |
| 47 | +Choose ONE of the following authentication methods: |
| 48 | + |
| 49 | +### Option 1: Workload Identity Federation (Recommended) |
| 50 | + |
| 51 | +Best for GitHub Actions, Azure DevOps, and other OIDC-capable CI/CD systems. No secrets to manage. |
| 52 | + |
| 53 | +```hcl |
| 54 | +module "acr_backplane" { |
| 55 | + source = "./backplane" |
| 56 | +
|
| 57 | + name = "container-registry" |
| 58 | + scope = "/providers/Microsoft.Management/managementGroups/landing-zones" |
| 59 | + hub_scope = "/subscriptions/00000000-0000-0000-0000-000000000000" |
| 60 | +
|
| 61 | + # Create main service principal with WIF |
| 62 | + create_service_principal_name = "acr-deployer" |
| 63 | + workload_identity_federation = { |
| 64 | + issuer = "https://token.actions.githubusercontent.com" |
| 65 | + subject = "repo:your-org/your-repo:ref:refs/heads/main" |
| 66 | + } |
| 67 | +
|
| 68 | + # Create separate hub service principal with WIF (least privilege) |
| 69 | + create_hub_service_principal_name = "acr-hub-peering" |
| 70 | + hub_workload_identity_federation = { |
| 71 | + issuer = "https://token.actions.githubusercontent.com" |
| 72 | + subject = "repo:your-org/your-repo:ref:refs/heads/main" |
| 73 | + } |
| 74 | +} |
| 75 | +``` |
| 76 | + |
| 77 | +### Option 2: Using Existing Service Principals |
| 78 | + |
| 79 | +Use this when you already have service principals created and managed externally. |
| 80 | + |
| 81 | +```hcl |
| 82 | +module "acr_backplane" { |
| 83 | + source = "./backplane" |
| 84 | +
|
| 85 | + name = "container-registry" |
| 86 | + scope = "/providers/Microsoft.Management/managementGroups/landing-zones" |
| 87 | + hub_scope = "/subscriptions/00000000-0000-0000-0000-000000000000" |
| 88 | +
|
| 89 | + # Provide object IDs of existing service principals |
| 90 | + existing_principal_ids = ["00000000-0000-0000-0000-000000000001"] |
| 91 | + existing_hub_principal_ids = ["00000000-0000-0000-0000-000000000002"] |
| 92 | +} |
| 93 | +``` |
| 94 | + |
| 95 | +### Option 3: Password Authentication (Not Recommended) |
| 96 | + |
| 97 | +Only use when WIF is not available. Requires secure password management. |
| 98 | + |
| 99 | +```hcl |
| 100 | +module "acr_backplane" { |
| 101 | + source = "./backplane" |
| 102 | +
|
| 103 | + name = "container-registry" |
| 104 | + scope = "/providers/Microsoft.Management/managementGroups/landing-zones" |
| 105 | + hub_scope = "/subscriptions/00000000-0000-0000-0000-000000000000" |
| 106 | +
|
| 107 | + # Create service principals with password authentication |
| 108 | + create_service_principal_name = "acr-deployer" |
| 109 | + create_hub_service_principal_name = "acr-hub-peering" |
| 110 | +
|
| 111 | + # Do NOT set workload_identity_federation - passwords will be auto-generated |
| 112 | + # Retrieve passwords from Terraform state or Azure portal |
| 113 | +} |
| 114 | +``` |
| 115 | + |
| 116 | +## Security Considerations |
| 117 | + |
| 118 | +- The role definitions follow the principle of least privilege |
| 119 | +- Service principals should use workload identity federation when possible |
| 120 | +- Passwords are marked as sensitive and not exposed in outputs |
| 121 | +- Review the permissions before deploying to production environments |
| 122 | +- The hub role has minimal permissions for VNet peering only |
| 123 | + |
| 124 | +<!-- BEGIN_TF_DOCS --> |
| 125 | +## Requirements |
| 126 | + |
| 127 | +| Name | Version | |
| 128 | +|------|---------| |
| 129 | +| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.3.0 | |
| 130 | +| <a name="requirement_azuread"></a> [azuread](#requirement\_azuread) | ~> 3.6.0 | |
| 131 | +| <a name="requirement_azurerm"></a> [azurerm](#requirement\_azurerm) | ~> 4.36.0 | |
| 132 | + |
| 133 | +## Modules |
| 134 | + |
| 135 | +No modules. |
| 136 | + |
| 137 | +## Resources |
| 138 | + |
| 139 | +| Name | Type | |
| 140 | +|------|------| |
| 141 | +| [azuread_application.buildingblock_deploy](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/application) | resource | |
| 142 | +| [azuread_application.buildingblock_deploy_hub](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/application) | resource | |
| 143 | +| [azuread_application_federated_identity_credential.buildingblock_deploy](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/application_federated_identity_credential) | resource | |
| 144 | +| [azuread_application_federated_identity_credential.buildingblock_deploy_hub](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/application_federated_identity_credential) | resource | |
| 145 | +| [azuread_application_password.buildingblock_deploy](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/application_password) | resource | |
| 146 | +| [azuread_application_password.buildingblock_deploy_hub](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/application_password) | resource | |
| 147 | +| [azuread_service_principal.buildingblock_deploy](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/service_principal) | resource | |
| 148 | +| [azuread_service_principal.buildingblock_deploy_hub](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/service_principal) | resource | |
| 149 | +| [azurerm_role_assignment.created_principal](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/role_assignment) | resource | |
| 150 | +| [azurerm_role_assignment.created_principal_hub](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/role_assignment) | resource | |
| 151 | +| [azurerm_role_assignment.created_principal_hub_to_landingzone](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/role_assignment) | resource | |
| 152 | +| [azurerm_role_assignment.created_principal_landingzone_to_hub](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/role_assignment) | resource | |
| 153 | +| [azurerm_role_assignment.existing_principals](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/role_assignment) | resource | |
| 154 | +| [azurerm_role_assignment.existing_principals_hub](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/role_assignment) | resource | |
| 155 | +| [azurerm_role_assignment.existing_principals_hub_to_landingzone](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/role_assignment) | resource | |
| 156 | +| [azurerm_role_assignment.existing_principals_landingzone_to_hub](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/role_assignment) | resource | |
| 157 | +| [azurerm_role_definition.buildingblock_deploy](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/role_definition) | resource | |
| 158 | +| [azurerm_role_definition.buildingblock_deploy_hub](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/role_definition) | resource | |
| 159 | +| [azurerm_role_definition.buildingblock_hub_to_landingzone](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/role_definition) | resource | |
| 160 | +| [azurerm_role_definition.buildingblock_landingzone_to_hub](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/role_definition) | resource | |
| 161 | +| [azurerm_subscription.current](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/subscription) | data source | |
| 162 | + |
| 163 | +## Inputs |
| 164 | + |
| 165 | +| Name | Description | Type | Default | Required | |
| 166 | +|------|-------------|------|---------|:--------:| |
| 167 | +| <a name="input_create_hub_service_principal_name"></a> [create\_hub\_service\_principal\_name](#input\_create\_hub\_service\_principal\_name) | name of a separate service principal to create for hub VNet peering (least privilege) | `string` | `null` | no | |
| 168 | +| <a name="input_create_service_principal_name"></a> [create\_service\_principal\_name](#input\_create\_service\_principal\_name) | name of a service principal to create and grant permissions to deploy the building block | `string` | `null` | no | |
| 169 | +| <a name="input_existing_hub_principal_ids"></a> [existing\_hub\_principal\_ids](#input\_existing\_hub\_principal\_ids) | set of existing principal ids that will be granted permissions to peer with the hub VNet | `set(string)` | `[]` | no | |
| 170 | +| <a name="input_existing_principal_ids"></a> [existing\_principal\_ids](#input\_existing\_principal\_ids) | set of existing principal ids that will be granted permissions to deploy the building block | `set(string)` | `[]` | no | |
| 171 | +| <a name="input_hub_scope"></a> [hub\_scope](#input\_hub\_scope) | Scope for hub VNet peering permissions (management group or subscription). Typically a hub subscription, but can be a management group containing hub resources. | `string` | n/a | yes | |
| 172 | +| <a name="input_hub_workload_identity_federation"></a> [hub\_workload\_identity\_federation](#input\_hub\_workload\_identity\_federation) | Configuration for workload identity federation for hub service principal. If not provided, an application password will be created instead. | <pre>object({<br> issuer = string<br> subject = string<br> })</pre> | `null` | no | |
| 173 | +| <a name="input_name"></a> [name](#input\_name) | name of the building block, used for naming resources | `string` | `"container-registry"` | no | |
| 174 | +| <a name="input_scope"></a> [scope](#input\_scope) | Scope where the building block should be deployable (management group or subscription), typically the parent of all Landing Zones. | `string` | n/a | yes | |
| 175 | +| <a name="input_workload_identity_federation"></a> [workload\_identity\_federation](#input\_workload\_identity\_federation) | Configuration for workload identity federation. If not provided, an application password will be created instead. | <pre>object({<br> issuer = string<br> subject = string<br> })</pre> | `null` | no | |
| 176 | + |
| 177 | +## Outputs |
| 178 | + |
| 179 | +| Name | Description | |
| 180 | +|------|-------------| |
| 181 | +| <a name="output_application_password"></a> [application\_password](#output\_application\_password) | Information about the created application password (excludes the actual password value for security). | |
| 182 | +| <a name="output_created_application"></a> [created\_application](#output\_created\_application) | Information about the created Azure AD application. | |
| 183 | +| <a name="output_created_hub_application"></a> [created\_hub\_application](#output\_created\_hub\_application) | Information about the created hub Azure AD application. | |
| 184 | +| <a name="output_created_hub_service_principal"></a> [created\_hub\_service\_principal](#output\_created\_hub\_service\_principal) | Information about the created hub service principal. | |
| 185 | +| <a name="output_created_service_principal"></a> [created\_service\_principal](#output\_created\_service\_principal) | Information about the created service principal. | |
| 186 | +| <a name="output_documentation_md"></a> [documentation\_md](#output\_documentation\_md) | Markdown documentation with information about the Container Registry Building Block backplane | |
| 187 | +| <a name="output_hub_application_password"></a> [hub\_application\_password](#output\_hub\_application\_password) | Information about the created hub application password (excludes the actual password value for security). | |
| 188 | +| <a name="output_hub_role_assignment_ids"></a> [hub\_role\_assignment\_ids](#output\_hub\_role\_assignment\_ids) | The IDs of the hub role assignments for all service principals. | |
| 189 | +| <a name="output_hub_role_assignment_principal_ids"></a> [hub\_role\_assignment\_principal\_ids](#output\_hub\_role\_assignment\_principal\_ids) | The principal IDs of all service principals that have been assigned the hub role. | |
| 190 | +| <a name="output_hub_role_definition_id"></a> [hub\_role\_definition\_id](#output\_hub\_role\_definition\_id) | The ID of the role definition that enables deployment of the building block to the hub. | |
| 191 | +| <a name="output_hub_role_definition_name"></a> [hub\_role\_definition\_name](#output\_hub\_role\_definition\_name) | The name of the role definition that enables deployment of the building block to the hub. | |
| 192 | +| <a name="output_hub_scope"></a> [hub\_scope](#output\_hub\_scope) | The scope (management group or subscription) where VNet peering role is applied. | |
| 193 | +| <a name="output_hub_workload_identity_federation"></a> [hub\_workload\_identity\_federation](#output\_hub\_workload\_identity\_federation) | Information about the created hub workload identity federation credential. | |
| 194 | +| <a name="output_provider_tf"></a> [provider\_tf](#output\_provider\_tf) | Ready-to-use provider.tf configuration for buildingblock deployment | |
| 195 | +| <a name="output_role_assignment_ids"></a> [role\_assignment\_ids](#output\_role\_assignment\_ids) | The IDs of the role assignments for all service principals. | |
| 196 | +| <a name="output_role_assignment_principal_ids"></a> [role\_assignment\_principal\_ids](#output\_role\_assignment\_principal\_ids) | The principal IDs of all service principals that have been assigned the role. | |
| 197 | +| <a name="output_role_definition_id"></a> [role\_definition\_id](#output\_role\_definition\_id) | The ID of the role definition that enables deployment of the building block. | |
| 198 | +| <a name="output_role_definition_name"></a> [role\_definition\_name](#output\_role\_definition\_name) | The name of the role definition that enables deployment of the building block. | |
| 199 | +| <a name="output_scope"></a> [scope](#output\_scope) | The scope where the role definition and role assignments are applied. | |
| 200 | +| <a name="output_workload_identity_federation"></a> [workload\_identity\_federation](#output\_workload\_identity\_federation) | Information about the created workload identity federation credential. | |
| 201 | +<!-- END_TF_DOCS --> |
0 commit comments