I'd like to use Azure Container Apps (ACA) and avoid AKS management
my understanding is that I can create an Azure Container App Environment (ACE) in TF without creating any ACA instances. Then I could use the AzureContainerApps@1 task to deploy my containers to the ACE.
assuming this is all correct, I'm still struggling with how I get secrets from my Key Vault into my ACA instance.
I'm reading store-secret-value-in-container-apps and I noticed they don't even show an option for using the ADO task.
Is what I'm trying to do possible? Where are some decent docs?
TL/DR - I'm really trying to avoid defining my ACA Instance(s) in Terraform. TF is a tool for creating infrastructure.... NOT managing deployments
I got this all working using TF
in my "shared-infra" module, I create the Container App Environment:
resource "azurerm_container_app_environment" "cae" {
name = "apps-${var.sub}"
location = local.location
resource_group_name = ....
log_analytics_workspace_id = azurerm_log_analytics_workspace.law.id
infrastructure_subnet_id = azurerm_subnet.apps_subnet.id
infrastructure_resource_group_name = "${azurerm_resource_group.apps_cae.name}-infra"
zone_redundancy_enabled = true
dynamic "workload_profile" {
for_each = var.app_workload_profiles
content {
name = workload_profile.value.name
workload_profile_type = workload_profile.value.type
minimum_count = workload_profile.value.min_count
maximum_count = workload_profile.value.max_count
}
}
}
in my "aca-app" module, I create a placeholder app using a generic public image
resource "azurerm_container_app" "app" {
name = "${var.app_name}-${var.sub}"
container_app_environment_id = data.azurerm_container_app_environment.cae.id
resource_group_name = azurerm_resource_group.app.name
revision_mode = "Single"
workload_profile_name = "Consumption"
identity {
type = "SystemAssigned"
}
template {
min_replicas = 0
max_replicas = 1
container {
name = "main"
image = "nginx:latest" # create the app using this placeholder image
cpu = "0.25"
memory = "0.5Gi"
}
}
ingress {
allow_insecure_connections = false
external_enabled = true
target_port = 80
transport = "http"
traffic_weight {
latest_revision = true
percentage = 100
}
}
lifecycle {
ignore_changes = all
# https://developer.hashicorp.com/terraform/language/meta-arguments/lifecycle#ignore_changes
}
}
/*
grant the managed identity assigned to the ACA app permission to pull images from ACR
*/
resource "azurerm_role_assignment" "acr_pull_assignment" {
principal_id = azurerm_container_app.app.identity[0].principal_id
role_definition_name = "AcrPull"
scope = data.azurerm_container_registry.acr.id
provider = azurerm.global
}
each "app" is a root module that call the "aca-app" and "key-vault" child modules:
module "app" {
source = "......aca app module ref......"
providers = {
azurerm = azurerm
azurerm.global = azurerm.global
}
sub = "dev"
app_name = "my-api"
}
module "vault" {
source = "...key vault module ref..."
sub = "dev"
name = "my-api"
resource_group_name = module.app.resource_group_name
supplemental_role_assignments = [
{
name = "my-api"
principal_id = module.app.managed_identity_id
role = "Key Vault Secrets User"
},
]
}
once all this TF runs, I can go to my app url and see the NGINX splash page. From this point forward, my ADO pipelines / Github Actions use the az cli to update the app going forward using a command like so:
az containerapp update -n my-api -g rg-my-api --yaml dev-config.yaml
and dev-config.yaml has
name: my-api // must match the -n flag of the cli command
properties:
managedEnvironmentId: .... your CAE ID ....
configuration:
...other bits omitted...
registries:
- server: <your acr>.azurecr.io
identity: system
secrets:
- name: some-api-key
keyVaultUrl: https://<your-vault>.vault.azure.net/secrets/ld-api-key
identity: system
template:
containers:
- image: <your-acr>.azurecr.io/<registry>:{{tag}}
name: my-api
resources:
cpu: 1
memory: 2Gi
env:
- name: IMAGE
value: <your-acr>.azurecr.io/<registry>:{{tag}}
- name: SOME_API_KEY
secretRef: some-api-key
the {{tag}}
is replaced with sed in a pipeline step to determine which image to deploy, based on how the pipeline is triggered