Skip to content

Commit c15f627

Browse files
committed
Merge branch 'main' into feature/aks
2 parents 8470f91 + 3f965c3 commit c15f627

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

85 files changed

+6796
-368
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,6 @@ yarn-error.log*
1717

1818
*.terraform*
1919
*.tfstate*
20+
*tfvars*
2021
.terraform.lock.hcl
22+
.env

modules/AGENTS.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,14 @@ When choosing a provider version for a module, consider:
106106
- Creates custom role definitions with specific permissions
107107
- Uses role assignments for principal access
108108
- Scoped to subscription or management group level
109+
- **Service Principal Creation (Optional):**
110+
- Can create Azure AD application and service principal
111+
- Supports **Workload Identity Federation (WIF)** for passwordless authentication
112+
- Falls back to application password if WIF not configured
113+
- Automatically assigns created principals to role definitions
114+
- **Two-tier permissions for networking:**
115+
- `buildingblock_deploy` role: Main deployment permissions
116+
- `buildingblock_deploy_hub` role: Hub-specific permissions for VNet peering (e.g., ACR, Key Vault)
109117

110118
**GCP Backplane Pattern:** *TBD - To be documented*
111119

@@ -139,6 +147,43 @@ When choosing a provider version for a module, consider:
139147
- **Negative scenarios:** Invalid inputs that should fail gracefully
140148
- **Naming collision tests:** Prevent resource conflicts
141149
- **Cross-provider consistency:** Similar test patterns across clouds
150+
- **Test Users:** Use the following test users:
151+
- **User Tom:**
152+
```json
153+
{
154+
meshIdentifier = "likvid-tom-user"
155+
username = "[email protected]"
156+
firstName = "Tom"
157+
lastName = "Livkid"
158+
159+
160+
roles = ["admin", "Workspace Owner"]
161+
}
162+
```
163+
- **User Daniela:**
164+
```json
165+
{
166+
meshIdentifier = "likvid-daniela-user"
167+
username = "[email protected]"
168+
firstName = "Daniela"
169+
lastName = "Livkid"
170+
171+
172+
roles = ["user", "Workspace Manager"]
173+
}
174+
```
175+
- **User Anna:**
176+
```json
177+
{
178+
meshIdentifier = "likvid-anna-user"
179+
username = "[email protected]"
180+
firstName = "Anna"
181+
lastName = "Livkid"
182+
183+
184+
roles = ["reader", "Workspace Member"]
185+
}
186+
```
142187

143188
**Example Test Structure:**
144189
```hcl
@@ -202,10 +247,12 @@ category: storage
202247

203248
- **Cost Management:** Budget alerts (AWS, Azure, GCP)
204249
- **Storage:** S3 buckets, Azure storage accounts, GCS buckets
250+
- **Container Registries:** Azure Container Registry (ACR) with private networking
205251
- **Networking:** VPCs, spoke networks, subnets
206252
- **Databases:** PostgreSQL, managed database instances
207253
- **Security:** Key Vault, IAM roles, secret management
208254
- **CI/CD:** GitHub Actions integration, service connections
255+
- **Compute:** Virtual machines (Azure), AKS integration
209256

210257
## Best Practices for New Modules
211258

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
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 -->
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
output "documentation_md" {
2+
value = <<EOF
3+
# Container Registry Building Block
4+
5+
The Container Registry Building Block configures an Azure Container Registry (ACR) with optional private networking for your subscriptions.
6+
7+
## Automation
8+
9+
We automate the deployment of a Container Registry Building Block using the common [Azure Building Blocks Automation Infrastructure](../automation.md).
10+
In order to deploy this building block, this infrastructure receives the following roles.
11+
12+
| Role Name | Description | Permissions |
13+
|-----------|-------------|-------------|
14+
| `${azurerm_role_definition.buildingblock_deploy.name}` | ${azurerm_role_definition.buildingblock_deploy.description} | ${join("<br>", formatlist("- `%s`", azurerm_role_definition.buildingblock_deploy.permissions[0].actions))} |
15+
| `${azurerm_role_definition.buildingblock_deploy_hub.name}` | ${azurerm_role_definition.buildingblock_deploy_hub.description} | ${join("<br>", formatlist("- `%s`", azurerm_role_definition.buildingblock_deploy_hub.permissions[0].actions))} |
16+
17+
EOF
18+
description = "Markdown documentation with information about the Container Registry Building Block backplane"
19+
}

0 commit comments

Comments
 (0)