Skip to content

Commit c239906

Browse files
authored
Merge branch 'main' into feat/DTOSS-7388-Integrate-Post-Deployment-Test-Results-With-Slack
2 parents e391f47 + 1ea284c commit c239906

File tree

22 files changed

+270
-68
lines changed

22 files changed

+270
-68
lines changed

infrastructure/modules/app-service-plan/tfdocs.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -246,17 +246,17 @@ Type: `string`
246246

247247
Default: `null`
248248

249-
### <a name="input_wildcard_ssl_cert_key_vault_secret_id"></a> [wildcard\_ssl\_cert\_key\_vault\_secret\_id](#input\_wildcard\_ssl\_cert\_key\_vault\_secret\_id)
249+
### <a name="input_wildcard_ssl_cert_name"></a> [wildcard\_ssl\_cert\_name](#input\_wildcard\_ssl\_cert\_name)
250250

251-
Description: Wildcard SSL certificate Key Vault secret id, for App Service Custom Domain binding.
251+
Description: Wildcard SSL certificate name as it will appear in the App Service binding, for Custom Domain binding.
252252

253253
Type: `string`
254254

255255
Default: `null`
256256

257-
### <a name="input_wildcard_ssl_cert_name"></a> [wildcard\_ssl\_cert\_name](#input\_wildcard\_ssl\_cert\_name)
257+
### <a name="input_wildcard_ssl_cert_pfx_blob_key_vault_secret_name"></a> [wildcard\_ssl\_cert\_pfx\_blob\_key\_vault\_secret\_name](#input\_wildcard\_ssl\_cert\_pfx\_blob\_key\_vault\_secret\_name)
258258

259-
Description: Wildcard SSL certificate name, for Custom Domain binding.
259+
Description: Wildcard SSL certificate pfx blob Key Vault secret name, for Custom Domain binding.
260260

261261
Type: `string`
262262

@@ -293,3 +293,4 @@ The following resources are used by this module:
293293
- [azurerm_app_service_virtual_network_swift_connection.appservice_vnet_swift_connection](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/app_service_virtual_network_swift_connection) (resource)
294294
- [azurerm_monitor_autoscale_setting.asp_autoscale](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/monitor_autoscale_setting) (resource)
295295
- [azurerm_service_plan.appserviceplan](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/service_plan) (resource)
296+
- [azurerm_key_vault_secret.pfx_blob](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/key_vault_secret) (data source)

infrastructure/modules/container-app-environment/README.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,37 @@ For the list of inputs, outputs, resources... check the [terraform module docume
1212
```hcl
1313
module "container-app-environment" {
1414
source = "../../../dtos-devops-templates/infrastructure/modules/container-app-environment"
15+
providers = {
16+
azurerm = azurerm
17+
azurerm.dns = azurerm
18+
}
1519
1620
name = "manage-breast-screening-${var.environment}"
1721
resource_group_name = azurerm_resource_group.this.name
18-
log_analytics_workspace_id = data.terraform_remote_state.audit.outputs.log_analytics_workspace_id
22+
log_analytics_workspace_id = module.log_analytics_workspace_audit.id
1923
vnet_integration_subnet_id = module.container_app_subnet.id
24+
private_dns_zone_rg_name = var.private_dns_zone_rg_name
25+
}
26+
```
27+
28+
If the private DNS zone is located in a different subscription, for instance in a hub and spoke architecture, the provider of that subscription must be provided:
29+
30+
```hcl
31+
provider "azurerm" {
32+
alias = "hub"
33+
subscription_id = var.hub_subscription_id
34+
35+
features {}
36+
}
37+
38+
module "container-app-environment" {
39+
source = "../../../dtos-devops-templates/infrastructure/modules/container-app-environment"
40+
providers = {
41+
azurerm = azurerm
42+
azurerm.dns = azurerm.hub
43+
}
44+
45+
name = "manage-breast-screening-${var.environment}"
46+
...
2047
}
2148
```

infrastructure/modules/container-app-environment/main.tf

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,31 @@ resource "azurerm_container_app_environment" "main" {
55
log_analytics_workspace_id = var.log_analytics_workspace_id
66
infrastructure_subnet_id = var.vnet_integration_subnet_id
77
internal_load_balancer_enabled = true
8+
zone_redundancy_enabled = var.zone_redundancy_enabled
9+
}
10+
11+
module "apex-record" {
12+
source = "../private-dns-a-record"
13+
providers = {
14+
azurerm = azurerm.dns
15+
}
16+
17+
name = local.dns_record
18+
resource_group_name = var.private_dns_zone_rg_name
19+
zone_name = "azurecontainerapps.io"
20+
ttl = 60
21+
records = [azurerm_container_app_environment.main.static_ip_address]
22+
}
23+
24+
module "wildcard-record" {
25+
source = "../private-dns-a-record"
26+
providers = {
27+
azurerm = azurerm.dns
28+
}
29+
30+
name = "*.${local.dns_record}"
31+
resource_group_name = var.private_dns_zone_rg_name
32+
zone_name = "azurecontainerapps.io"
33+
ttl = 60
34+
records = [azurerm_container_app_environment.main.static_ip_address]
835
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
output "id" {
2+
description = "Container app environment ID"
23
value = azurerm_container_app_environment.main.id
34
}
45

56
output "default_domain" {
7+
description = "Default internal DNS domain. Should be registered in the private DNS zone."
68
value = azurerm_container_app_environment.main.default_domain
79
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
terraform {
2+
required_providers {
3+
azurerm = {
4+
source = "hashicorp/azurerm"
5+
configuration_aliases = [ azurerm.dns ]
6+
}
7+
}
8+
}

infrastructure/modules/container-app-environment/tfdocs.md

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ Description: Name of the container app environment.
1616

1717
Type: `string`
1818

19+
### <a name="input_private_dns_zone_rg_name"></a> [private\_dns\_zone\_rg\_name](#input\_private\_dns\_zone\_rg\_name)
20+
21+
Description: Name of the hub resource group where the private DNS zone is located.
22+
23+
Type: `string`
24+
1925
### <a name="input_resource_group_name"></a> [resource\_group\_name](#input\_resource\_group\_name)
2026

2127
Description: Name of the resource group to create the container app environment in.
@@ -28,17 +34,43 @@ Description: ID of the subnet for the container app environment. Must be at leas
2834

2935
Type: `string`
3036

37+
## Optional Inputs
38+
39+
The following input variables are optional (have default values):
40+
41+
### <a name="input_zone_redundancy_enabled"></a> [zone\_redundancy\_enabled](#input\_zone\_redundancy\_enabled)
42+
43+
Description: Enable availability zone redundancy for the container app environment. Should be set to true in production.
44+
45+
Type: `bool`
46+
47+
Default: `false`
48+
## Modules
49+
50+
The following Modules are called:
51+
52+
### <a name="module_apex-record"></a> [apex-record](#module\_apex-record)
53+
54+
Source: ../private-dns-a-record
55+
56+
Version:
57+
58+
### <a name="module_wildcard-record"></a> [wildcard-record](#module\_wildcard-record)
59+
60+
Source: ../private-dns-a-record
61+
62+
Version:
3163
## Outputs
3264

3365
The following outputs are exported:
3466

3567
### <a name="output_default_domain"></a> [default\_domain](#output\_default\_domain)
3668

37-
Description: n/a
69+
Description: Default internal DNS domain. Should be registered in the private DNS zone.
3870

3971
### <a name="output_id"></a> [id](#output\_id)
4072

41-
Description: n/a
73+
Description: Container app environment ID
4274
## Resources
4375

4476
The following resources are used by this module:

infrastructure/modules/container-app-environment/variables.tf

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,22 @@ variable "vnet_integration_subnet_id" {
1717
type = string
1818
description = "ID of the subnet for the container app environment. Must be at least /23"
1919
}
20+
21+
variable "private_dns_zone_rg_name" {
22+
type = string
23+
description = "Name of the hub resource group where the private DNS zone is located."
24+
}
25+
26+
variable "zone_redundancy_enabled" {
27+
type = bool
28+
description = "Enable availability zone redundancy for the container app environment. Should be set to true in production."
29+
default = false
30+
}
31+
32+
locals {
33+
dns_record = replace(
34+
azurerm_container_app_environment.main.default_domain,
35+
".azurecontainerapps.io",
36+
""
37+
)
38+
}

infrastructure/modules/container-app/README.md

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ Deploy an [Azure container app](https://learn.microsoft.com/en-us/azure/containe
66
For the list of inputs, outputs, resources... check the [terraform module documentation](tfdocs.md).
77

88
## Usage
9+
Create the common container app environment:
910
```hcl
10-
# Create the common container app environment
1111
module "container-app-environment" {
1212
source = "../../../dtos-devops-templates/infrastructure/modules/container-app-environment"
1313
@@ -16,31 +16,55 @@ module "container-app-environment" {
1616
log_analytics_workspace_id = data.terraform_remote_state.audit.outputs.log_analytics_workspace_id
1717
vnet_integration_subnet_id = module.container_app_subnet.id
1818
}
19+
```
1920

20-
# Create a webapp for a container listening on port 8000
21-
# The webapp will be available on internal URL https://<name>.<container app environment default domain>
21+
Create a webapp for a container listening on port 8000. The webapp will be available on internal URL `https://<name>.<container app environment default domain>`:
22+
```hcl
2223
module "webapp" {
23-
source = "../../../modules/dtos-devops-templates/infrastructure/modules/container-app"
24-
name = "manage-breast-screening-web-${var.environment}"
25-
container_app_environment_id = module.container-app-environment.id
26-
resource_group_name = azurerm_resource_group.this.name
27-
app_key_vault_name = module.app-key-vault.name # All secrets in the app key vault are mapped securely as environment variables
28-
docker_image = var.docker_image # Docker image and unique tag
29-
# Map of non secret environment variables
24+
source = "../../../modules/dtos-devops-templates/infrastructure/modules/container-app"
25+
name = "manage-breast-screening-web-${var.environment}"
26+
container_app_environment_id = module.container-app-environment.id
27+
resource_group_name = azurerm_resource_group.this.name
28+
docker_image = var.docker_image
3029
environment_variables = {
3130
"ALLOWED_HOSTS" = "manage-breast-screening-web-${var.environment}.${module.container-app-environment.default_domain}"
3231
}
3332
is_web_app = true
3433
http_port = 8000
3534
}
35+
```
3636

37-
# Create a background worker (no ingress)
37+
Create a background worker (no ingress):
38+
```hcl
3839
module "worker" {
39-
source = "../../../modules/dtos-devops-templates/infrastructure/modules/container-app"
40-
name = "manage-breast-screening-worker-${var.environment}"
41-
container_app_environment_id = module.container-app-environment.id
42-
resource_group_name = azurerm_resource_group.this.name
43-
app_key_vault_name = module.app-key-vault.name
44-
docker_image = var.docker_image_worker
40+
source = "../../../modules/dtos-devops-templates/infrastructure/modules/container-app"
41+
name = "manage-breast-screening-worker-${var.environment}"
42+
container_app_environment_id = module.container-app-environment.id
43+
resource_group_name = azurerm_resource_group.this.name
44+
docker_image = var.docker_image_worker
4545
}
4646
```
47+
48+
## Key vault secrets
49+
The container app can be mapped to an Azure key vault. All secrets are fetched and provided as secret environment variables to the app. The values are updated when terraform runs, or automatically within 30 min.
50+
51+
A secret name in key vault must use hyphens: `SECRET-KEY`. It is automatically mapped as an environment variable with underscore: `SECRET_KEY`.
52+
53+
**Warning:** The module cannot read from key vault if doesn't exist yet:
54+
1. For each environment, create first via terraform:
55+
- key vault with the [key-vault module](../key-vault/)
56+
- container-app with `app_key_vault_id` from above and `fetch_secrets_from_app_key_vault` set to `false` (default)
57+
1. Add the secrets to the key vault manually
58+
1. Set `fetch_secrets_from_app_key_vault` set to `true` and run terraform to populate the app secret environment variables
59+
60+
Example:
61+
```hcl
62+
module "worker" {
63+
source = "../../../modules/dtos-devops-templates/infrastructure/modules/container-app"
64+
...
65+
app_key_vault_id = module.app-key-vault.key_vault_id
66+
fetch_secrets_from_app_key_vault = var.fetch_secrets_from_app_key_vault
67+
}
68+
69+
# Set fetch_secrets_from_app_key_vault to true in tfvars once the key vault is populated with secrets
70+
```
Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
1-
data "azurerm_key_vault" "app" {
2-
count = var.app_key_vault_name != null ? 1 : 0
1+
data "azurerm_key_vault_secrets" "app" {
2+
count = var.fetch_secrets_from_app_key_vault ? 1 : 0
3+
depends_on = [module.key_vault_reader_role]
34

4-
name = var.app_key_vault_name
5-
resource_group_name = var.resource_group_name
5+
key_vault_id = var.app_key_vault_id
66
}
77

8-
data "azurerm_key_vault_secrets" "app" {
9-
count = var.app_key_vault_name != null ? 1 : 0
10-
11-
key_vault_id = data.azurerm_key_vault.app[0].id
8+
data "azurerm_resource_group" "main" {
9+
name = var.resource_group_name
1210
}

infrastructure/modules/container-app/main.tf

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,40 @@
1+
module "container_app_identity" {
2+
source = "../managed-identity"
3+
resource_group_name = var.resource_group_name
4+
location = data.azurerm_resource_group.main.location
5+
uai_name = "${var.name}-identity"
6+
}
7+
8+
# Allow the container app to read secrets from keyvaults in the resource groups
9+
module "key_vault_reader_role" {
10+
count = var.fetch_secrets_from_app_key_vault ? 1 : 0
11+
12+
source = "../rbac-assignment"
13+
14+
scope = data.azurerm_resource_group.main.id
15+
role_definition_name = "Key Vault Secrets User"
16+
principal_id = module.container_app_identity.principal_id
17+
}
18+
119
resource "azurerm_container_app" "main" {
220
name = var.name
321
container_app_environment_id = var.container_app_environment_id
422
resource_group_name = var.resource_group_name
523
revision_mode = "Single"
624

725
identity {
8-
type = "SystemAssigned"
26+
type = "UserAssigned"
27+
identity_ids = [module.container_app_identity.id]
928
}
1029

1130
dynamic "secret" {
12-
for_each = var.app_key_vault_name != null ? data.azurerm_key_vault_secrets.app[0].secrets : []
31+
for_each = var.fetch_secrets_from_app_key_vault ? data.azurerm_key_vault_secrets.app[0].secrets : []
1332

1433
content {
1534
# KV secrets are uppercase and hyphen separated
1635
# app container secrets are lowercase and hyphen separated
17-
name = lower(secret.value.name)
18-
identity = "System"
36+
name = lower(secret.value.name)
37+
identity = module.container_app_identity.id
1938
key_vault_secret_id = secret.value.id
2039
}
2140
}
@@ -36,7 +55,7 @@ resource "azurerm_container_app" "main" {
3655
}
3756

3857
dynamic "env" {
39-
for_each = var.app_key_vault_name != null ? data.azurerm_key_vault_secrets.app[0].secrets : []
58+
for_each = var.fetch_secrets_from_app_key_vault ? data.azurerm_key_vault_secrets.app[0].secrets : []
4059
content {
4160
# Env vars are uppercase and underscore separated
4261
name = upper(replace(env.value.name, "-", "_"))
@@ -63,19 +82,3 @@ resource "azurerm_container_app" "main" {
6382
}
6483
}
6584
}
66-
67-
# resource "azurerm_role_assignment" "key_vault_reader" {
68-
# count = var.app_key_vault_name != null ? 1 : 0
69-
70-
# scope = data.azurerm_key_vault.app[0].id
71-
# role_definition_name = "Key Vault Secrets User"
72-
# principal_id = azurerm_container_app.main.identity[0].principal_id
73-
# }
74-
75-
module "key_vault_reader_role" {
76-
source = "../rbac-assignment"
77-
78-
scope = data.azurerm_key_vault.app[0].id
79-
role_definition_name = "Key Vault Secrets User"
80-
principal_id = azurerm_container_app.main.identity[0].principal_id
81-
}

0 commit comments

Comments
 (0)