Skip to content

Commit 3fb70f7

Browse files
authored
Implement app secrets in container app jobs (#225)
1 parent 143bb43 commit 3fb70f7

6 files changed

Lines changed: 122 additions & 3 deletions

File tree

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,26 @@ module "container-app-job" {
4747
}
4848
```
4949

50+
## Key vault secrets
51+
The container app job can be mapped to Azure Key Vaults for secret management:
52+
53+
- **App Key Vault:**
54+
- All secrets from the app key vault are fetched and mapped to secret environment variables if `fetch_secrets_from_app_key_vault = true` and `app_key_vault_id` is provided.
55+
- Secret names in Key Vault must use hyphens (e.g., `SECRET-KEY`). These are mapped to environment variables with underscores (e.g., `SECRET_KEY`).
56+
- Secrets are updated when Terraform runs, or automatically within 30 minutes.
57+
58+
**Warning:** The module cannot read from a key vault if it doesn't exist yet. Recommended workflow:
59+
1. Create the key vault(s) using the [key-vault module](../key-vault/).
60+
2. Deploy the container app with `fetch_secrets_from_app_key_vault = false` (default) and/or `enable_auth = false`.
61+
3. Manually add the required secrets to the key vault(s).
62+
4. Set `fetch_secrets_from_app_key_vault = true` and/or `enable_auth = true`, then re-run Terraform to populate the app with secret environment variables and enable authentication.
63+
64+
Example (app secrets):
65+
```hcl
66+
module "job" {
67+
source = "../../../modules/dtos-devops-templates/infrastructure/modules/container-app-job"
68+
...
69+
app_key_vault_id = module.app-key-vault.key_vault_id
70+
fetch_secrets_from_app_key_vault = true
71+
}
72+
```
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
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_app]
4+
5+
key_vault_id = var.app_key_vault_id
6+
}

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

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,26 @@
22
locals {
33
all_identity_ids = compact(concat(
44
[var.acr_managed_identity_id],
5-
var.user_assigned_identity_ids)
6-
)
5+
var.user_assigned_identity_ids,
6+
[module.container_app_identity.id]
7+
))
8+
}
9+
10+
module "container_app_identity" {
11+
source = "../managed-identity"
12+
resource_group_name = var.resource_group_name
13+
location = var.location
14+
uai_name = "mi-${var.name}"
15+
}
16+
17+
module "key_vault_reader_role_app" {
18+
count = var.fetch_secrets_from_app_key_vault ? 1 : 0
19+
20+
source = "../rbac-assignment"
21+
22+
scope = var.app_key_vault_id
23+
role_definition_name = "Key Vault Secrets User"
24+
principal_id = module.container_app_identity.principal_id
725
}
826

927
resource "azurerm_container_app_job" "this" {
@@ -21,6 +39,19 @@ resource "azurerm_container_app_job" "this" {
2139
identity_ids = local.all_identity_ids
2240
}
2341

42+
dynamic "secret" {
43+
44+
for_each = var.fetch_secrets_from_app_key_vault ? data.azurerm_key_vault_secrets.app[0].secrets : []
45+
46+
content {
47+
# KV secrets are uppercase and hyphen separated
48+
# app container secrets are lowercase and hyphen separated
49+
name = lower(secret.value.name)
50+
identity = module.container_app_identity.id
51+
key_vault_secret_id = secret.value.id
52+
}
53+
}
54+
2455
# Configure manual trigger for on-demand execution via CLI
2556
dynamic "manual_trigger_config" {
2657
for_each = var.cron_expression == null ? [1] : []
@@ -61,6 +92,16 @@ resource "azurerm_container_app_job" "this" {
6192
value = env.value
6293
}
6394
}
95+
dynamic "env" {
96+
for_each = var.fetch_secrets_from_app_key_vault ? data.azurerm_key_vault_secrets.app[0].secrets : []
97+
content {
98+
# Env vars are uppercase and underscore separated
99+
name = upper(replace(env.value.name, "-", "_"))
100+
# KV secrets are uppercase and hyphen separated
101+
# app container secrets are lowercase and hyphen separated
102+
secret_name = lower(env.value.name)
103+
}
104+
}
64105
}
65106
}
66107

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,14 @@ Type: `string`
4848

4949
Default: `null`
5050

51+
### <a name="input_app_key_vault_id"></a> [app\_key\_vault\_id](#input\_app\_key\_vault\_id)
52+
53+
Description: ID of the key vault to store app secrets. Each secret is mapped to an environment variable. Required when fetch\_secrets\_from\_app\_key\_vault is true.
54+
55+
Type: `string`
56+
57+
Default: `null`
58+
5159
### <a name="input_container_args"></a> [container\_args](#input\_container\_args)
5260

5361
Description: The arguments to pass to the container command. Optional.
@@ -80,6 +88,16 @@ Type: `map(string)`
8088

8189
Default: `{}`
8290

91+
### <a name="input_fetch_secrets_from_app_key_vault"></a> [fetch\_secrets\_from\_app\_key\_vault](#input\_fetch\_secrets\_from\_app\_key\_vault)
92+
93+
Description: Fetch secrets from the app key vault and map them to secret environment variables. Requires app\_key\_vault\_id.
94+
95+
WARNING: The key vault must be created by terraform and populated manually before setting this to true.
96+
97+
Type: `bool`
98+
99+
Default: `false`
100+
83101
### <a name="input_job_parallelism"></a> [job\_parallelism](#input\_job\_parallelism)
84102

85103
Description: The number of replicas that can run in parallel.
@@ -143,10 +161,25 @@ Description: Workload profile in this container app environment
143161
Type: `string`
144162

145163
Default: `"Consumption"`
164+
## Modules
165+
166+
The following Modules are called:
167+
168+
### <a name="module_container_app_identity"></a> [container\_app\_identity](#module\_container\_app\_identity)
169+
170+
Source: ../managed-identity
171+
172+
Version:
173+
174+
### <a name="module_key_vault_reader_role_app"></a> [key\_vault\_reader\_role\_app](#module\_key\_vault\_reader\_role\_app)
175+
176+
Source: ../rbac-assignment
146177

178+
Version:
147179

148180
## Resources
149181

150182
The following resources are used by this module:
151183

152184
- [azurerm_container_app_job.this](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/container_app_job) (resource)
185+
- [azurerm_key_vault_secrets.app](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/key_vault_secrets) (data source)

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,22 @@ variable "resource_group_name" {
88
type = string
99
}
1010

11+
variable "app_key_vault_id" {
12+
description = "ID of the key vault to store app secrets. Each secret is mapped to an environment variable. Required when fetch_secrets_from_app_key_vault is true."
13+
type = string
14+
default = null
15+
}
16+
variable "fetch_secrets_from_app_key_vault" {
17+
description = <<EOT
18+
Fetch secrets from the app key vault and map them to secret environment variables. Requires app_key_vault_id.
19+
20+
WARNING: The key vault must be created by terraform and populated manually before setting this to true.
21+
EOT
22+
type = bool
23+
default = false
24+
nullable = false
25+
}
26+
1127
variable "location" {
1228
type = string
1329
description = "The location/region where the container app environment is created."

infrastructure/modules/container-app/main.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ module "key_vault_reader_role_infra" {
3030
locals {
3131
all_identity_ids = compact(concat(
3232
[var.acr_managed_identity_id],
33-
var.user_assigned_identity_ids,
33+
3434
[module.container_app_identity.id]
3535
))
3636
}

0 commit comments

Comments
 (0)