In this blog, we will be looking at deploying Azure Container Apps into your virtual network using Terraform and AzAPI. A follow on blog from my previous – Deploying multiple Container Apps in Azure using Terraform and AzAPI
Looking at the diagram below, I will be deploying the container app so it is only accessible within the virtual network and not accessible via the public internet.

The Terraform will create:
- Resource Group
- Log Analytics workspace that will be connected to the Container App environment
- Virtual network with aca subnet
- Container App environment deployed into above subnet
- Container App
- Private dns zone created with the associated
defaultDomain
property of the container app environment - A record within the private dns zone for the Container App with associated
staticIp
property of the container app environment
The Terraform
- Resource Group
resource "azurerm_resource_group" "rg" {
name = "${var.aca_name}-rg"
location = var.location
}
- Log Analytics workspace
resource "azurerm_log_analytics_workspace" "loganalytics" {
name = "${var.aca_name}-la"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
sku = "PerGB2018"
retention_in_days = 30
}
- Virtual Network and subnet
resource "azurerm_virtual_network" "virtual_network" {
name = "${var.aca_name}-vnet"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
address_space = [var.network_address_space]
}
resource "azurerm_subnet" "aca_subnet" {
name = var.aca_subnet_address_name
resource_group_name = azurerm_resource_group.rg.name
virtual_network_name = azurerm_virtual_network.virtual_network.name
address_prefixes = [var.aca_subnet_address_prefix]
}
- Container App environment
Notice the use of response_export_values
– this extracts the value data needed to create private dns zone & A record
resource "azapi_resource" "containerapp_environment" {
type = "Microsoft.App/managedEnvironments@2022-03-01"
name = "${var.aca_name}acae"
parent_id = azurerm_resource_group.rg.id
location = azurerm_resource_group.rg.location
body = jsonencode({
properties = {
appLogsConfiguration = {
destination = "log-analytics"
logAnalyticsConfiguration = {
customerId = azurerm_log_analytics_workspace.loganalytics.workspace_id
sharedKey = azurerm_log_analytics_workspace.loganalytics.primary_shared_key
}
}
vnetConfiguration = {
internal = true
infrastructureSubnetId = azurerm_subnet.aca_subnet.id
dockerBridgeCidr = "10.2.0.1/16"
platformReservedCidr = "10.1.0.0/16"
platformReservedDnsIP = "10.1.0.2"
}
}
})
depends_on = [
azurerm_virtual_network.virtual_network
]
response_export_values = ["properties.defaultDomain", "properties.staticIp"]
ignore_missing_property = true
}
- Container App
resource "azapi_resource" "containerapp" {
type = "Microsoft.App/containerapps@2022-03-01"
name = "app01acae"
parent_id = azurerm_resource_group.rg.id
location = azurerm_resource_group.rg.location
body = jsonencode({
properties = {
managedEnvironmentId = azapi_resource.containerapp_environment.id
configuration = {
ingress = {
external : true,
targetPort : 80
},
}
template = {
containers = [
{
image = "mcr.microsoft.com/azuredocs/containerapps-helloworld:latest"
name = "simple-hello-world-container"
resources = {
cpu = 0.25
memory = "0.5Gi"
}
}
]
scale = {
minReplicas = 0,
maxReplicas = 2
}
}
}
})
depends_on = [
azapi_resource.containerapp_environment
]
}
- Private dns zone, vnet link & a record creation
resource "azurerm_private_dns_zone" "private_dns_zone" {
name = jsondecode(azapi_resource.containerapp_environment.output).properties.defaultDomain
resource_group_name = azurerm_resource_group.rg.name
# depends_on = [
# azapi_resource.containerapp_environment
# ]
}
resource "azurerm_private_dns_zone_virtual_network_link" "vnet_link" {
name = "containerapplink"
resource_group_name = azurerm_resource_group.rg.name
private_dns_zone_name = azurerm_private_dns_zone.private_dns_zone.name
virtual_network_id = azurerm_virtual_network.virtual_network.id
}
resource "azurerm_private_dns_a_record" "containerapp_record" {
name = "app01acae"
zone_name = azurerm_private_dns_zone.private_dns_zone.name
resource_group_name = azurerm_resource_group.rg.name
ttl = 300
records = ["${jsondecode(azapi_resource.containerapp_environment.output).properties.staticIp}"]
}
Once the private zone & A record has been deployed, as below – you can see what it creates
The app can now be accessible from within the vnet using the URL: https://app01acae.orangemeadow-be982bb4.uksouth.azurecontainerapps.io and not accessible via the public internet

Full terraform setup is in this GitHub repository & can be ran locally with:
terraform init
terraform apply -var-file="prod.tfvars"
Another blog post to assist in creating Azure Container Apps with Terraform and & AzAPI!