Manage a Cisco ACI cloud application container β the cloud-policy grouping construct (class
cloudApp, DNuni/tn-{name}/cloudapp-{name}) under a tenant on a Cisco Cloud APIC (Cloud Network Controller) that cloud EPGs compose under β as a typed, secure-by-default building block targetingCiscoDevNet/aci ~> 2.20.
This module manages a single ACI cloud application container and its metadata tail as one coherent, secure-by-default unit:
- βοΈ The cloud application container (
aci_cloud_applicationcontainer.this) β the cloud-policy grouping construct on a Cisco Cloud APIC (Cloud Network Controller), addressed by the Distinguished Nameuni/tn-{tenant}/cloudapp-{name}. - π·οΈ The ACI metadata tail this resource exposes β
annotation(preserved asorchestrator:terraformso Terraform-managed objects are identifiable in APIC),name_alias, anddescription. This keystone's live schema has noannotations/tagslists and no typedrelation_to_*relations. - π Scope, not credentials β the container's only required scope is its parent tenant's DN; authentication and the APIC URL are the caller's provider concern and are never module variables.
- π Cloud-only construct β this resource applies to a Cisco Cloud APIC (Cloud Network Controller) tenant, not an on-premises fabric.
π‘ Why it matters: the cloud application container is the anchor cloud EPGs nest under in a Cloud Network Controller tenant β the cloud analog of an on-premises application profile. A consistent, identifiable container is the foundation the cloud-EPG module wires into through its parent DN input.
If these Terraform modules have been helpful to you or your organization, I'd appreciate your support in any of the following ways:
- β Star this repository to help others discover this Terraform module.
- π€ Connect with me on LinkedIn: linkedin.com/in/microsoftexpert
- β Buy me a coffee: buymeacoffee.com/microsoftexpert
Whether it's a star, a professional connection, or a coffee, every gesture helps keep these modules actively maintained and continually improving. Thank you for being part of the community!
graph LR
apic["Cisco Cloud APIC / Cloud Network Controller (provider auth, out of band)"]:::ext
tenant["terraform-aci-tenant"]:::sib
cac["terraform-aci-cloud-application-container (this module)"]:::this
ca["aci_cloud_applicationcontainer - class cloudApp - DN uni/tn-{name}/cloudapp-{name}"]:::keystone
cepg["terraform-aci-cloud-epg"]:::sib
apic -->|"provider configured by caller"| cac
tenant -->|"tenant_dn"| cac
cac -->|"manages"| ca
cac -->|"cloud application container DN"| cepg
classDef this fill:#00BCEB,color:#fff,stroke:#00BCEB;
classDef keystone fill:#0D274D,color:#fff,stroke:#0D274D;
classDef sib fill:#f5f5f5,color:#333,stroke:#cccccc;
classDef ext fill:#eeeeff,color:#333,stroke:#9999ff;
The cloud application container sits directly under the tenant in the cloud-policy tree. It takes the
tenant's DN as its only required parent input and emits an id (its own DN,
uni/tn-{name}/cloudapp-{name}) that a cloud EPG module consumes as its parent DN.
graph TD
n["cloud_application_container.name (required, immutable)"]:::in
p["tenant_dn (required parent)"]:::in
meta["annotation / name_alias / description"]:::in
this["aci_cloud_applicationcontainer.this (keystone, cloudApp)"]:::this
oid["output: id (DN uni/tn-{name}/cloudapp-{name})"]:::out
onm["output: name"]:::out
p --> this
n --> this
meta --> this
this --> oid
this --> onm
classDef this fill:#00BCEB,color:#fff,stroke:#00BCEB;
classDef in fill:#f5f5f5,color:#333,stroke:#cccccc;
classDef out fill:#eeeeff,color:#333,stroke:#9999ff;
Resource inventory
| Resource | Name | Cardinality | Role |
|---|---|---|---|
aci_cloud_applicationcontainer |
this |
1 (keystone) | The cloud application container / cloud-policy grouping construct (cloudApp). |
| Requirement | Value |
|---|---|
| Terraform | >= 1.3.0 (uses optional() object defaults) |
| Provider | CiscoDevNet/aci ~> 2.20 |
| Provider block | None in this module β the caller configures and authenticates the provider (username/password, X.509 signature, or login domain) out of band. |
| Scope | Parent tenant DN only (tenant_dn) β no other scope inputs. |
Schema notes that bite (verified against the live provider schema):
- π
cloud_application_container.nameis immutable. Changing it forces replacement β a brand-new container, and any cloud EPGs created under its DN go with it. Treat renames as migrations. β οΈ This resource has not migrated to the plugin-framework shape. It is a classic (SDKv2) resource, and its live schema exposes the parent only throughtenant_dnβ there is noparent_dnalternative to prefer here, unlike migrated siblings in this suite (e.g. the bridge domain) that deprecatetenant_dnin favor ofparent_dn. This module usestenant_dnbecause it is the only attribute the live schema provides.- βΉοΈ No
annotations/tagslists, norelation_to_*relations. The live schema for this keystone exposes onlyannotation(singular),name_alias, anddescriptionbeyondnameandtenant_dnβ this module models exactly that and nothing more. - βΉοΈ
idis the DN (uni/tn-{name}/cloudapp-{name}), computed by APIC at create. It is the value a cloud-EPG module consumes as its parent DN. - π Cloud Network Controller only.
aci_cloud_applicationcontainertargets a Cisco Cloud APIC (Cloud Network Controller) tenant; it has no equivalent on an on-premises (classic) APIC fabric.
Scope the caller's APIC login to the least privilege this module needs:
- Create / delete a cloud application container: the
tenant-adminrole (or a custom role with cloud-tenant-admin write privilege) scoped to the tenant's security domain on the Cloud Network Controller. - No additional read privileges are required β this keystone carries no relationships to objects outside its own tenant.
The module never sees a credential β authentication is a provider/caller concern supplied out of band (e.g.
ACI_USERNAME / ACI_PASSWORD, or ACI_PRIVATE_KEY / ACI_CERT_NAME for signature-based auth).
- A reachable Cisco Cloud APIC (Cloud Network Controller) (
ACI_URL) whose version is compatible with the~> 2.20provider, with the provider configured and authenticated by the caller. This resource is not supported against an on-premises APIC fabric. - In production, the provider should be configured with
insecure = falseand proper CA trust β the provider's own default (insecure = true, skip TLS verification) is not a safe steady state. - The parent tenant must exist (e.g. via
terraform-aci-tenant, or a tenant already onboarded to the Cloud Network Controller) so the provider's DN validation oftenant_dnpasses.
terraform-aci-cloud-application-container/
βββ providers.tf # terraform{} + required_providers (aci ~> 2.20); no provider block
βββ variables.tf # tenant_dn + the cloud application container object β typed, secure defaults, heredoc schema, validations
βββ main.tf # aci_cloud_applicationcontainer.this (keystone) + metadata tail
βββ outputs.tf # id (the DN) first, then name
βββ README.md # this document
βββ SCOPE.md # cross-module contract (scope, consumes/emits, roles, prerequisites)
βββ LICENSE # MIT
βββ .gitignore # canonical library ignore set
# The caller configures the provider (authentication is out of band).
provider "aci" {
# username / password, or private_key + cert_name for signature auth;
# url = "https://apic.example.com"; set insecure = false in production.
}
module "cloud_app_container" {
source = "git::https://github.com/microsoftexpert/terraform-aci-cloud-application-container.git?ref=v1.0.0"
tenant_dn = "uni/tn-cloud-prod"
cloud_application_container = {
name = "web-tier"
}
}
output "cloud_app_container_dn" {
value = module.cloud_app_container.id # pass this as the parent DN to a cloud-epg module
}Consumes
| Input | Type | Typical source |
|---|---|---|
tenant_dn |
string (DN) |
terraform-aci-tenant (or an existing Cloud Network Controller tenant) |
cloud_application_container |
object({...}) |
caller (name + metadata tail) |
Emits
| Output | Description | Consumed by |
|---|---|---|
id |
Cloud application container DN (uni/tn-{name}/cloudapp-{name}) β primary reference |
terraform-aci-cloud-epg (as the parent DN for cloud EPGs created here) |
name |
Cloud application container name | composition / audit |
1 Β· Minimal β a cloud application container with secure defaults
module "cloud_app_container" {
source = "git::https://github.com/microsoftexpert/terraform-aci-cloud-application-container.git?ref=v1.0.0"
tenant_dn = module.tenant.id
cloud_application_container = {
name = "web-tier"
}
}π‘ The minimal call creates only the container.
annotationis preserved asorchestrator:terraform, so the object is identifiable as Terraform-managed in APIC, and no permissive posture is introduced.
2 Β· Description and GUI alias
module "cloud_app_container" {
source = "git::https://github.com/microsoftexpert/terraform-aci-cloud-application-container.git?ref=v1.0.0"
tenant_dn = module.tenant.id
cloud_application_container = {
name = "web-tier"
name_alias = "Web Tier"
description = "Cloud application container for the web tier β owned by the cloud platform team"
}
}βΉοΈ
name_aliasis a display alias shown in the APIC GUI;nameremains the immutable identity encoded in the DN.
3 Β· A custom annotation marker
module "cloud_app_container" {
source = "git::https://github.com/microsoftexpert/terraform-aci-cloud-application-container.git?ref=v1.0.0"
tenant_dn = module.tenant.id
cloud_application_container = {
name = "web-tier"
annotation = "orchestrator:terraform:cloud-platform-team"
}
}π Keep the
orchestrator:terraformprefix so Terraform-managed objects stay identifiable in APIC. This suite defaultsannotationtoorchestrator:terraform; override it only to extend, not to erase, that marker.
4 Β· Naming rule violation (rejected at plan time)
module "cloud_app_container" {
source = "git::https://github.com/microsoftexpert/terraform-aci-cloud-application-container.git?ref=v1.0.0"
tenant_dn = module.tenant.id
cloud_application_container = {
name = "web tier!" # contains a space and "!" β rejected
}
}
β οΈ cloud_application_container.namemust match^[a-zA-Z0-9_.:-]+$and be 1-64 characters; this call fails plan-time validation rather than reaching APIC with an invalid name.
5 Β· Multiple cloud application containers per tenant
module "web_tier" {
source = "git::https://github.com/microsoftexpert/terraform-aci-cloud-application-container.git?ref=v1.0.0"
tenant_dn = module.tenant.id
cloud_application_container = { name = "web-tier" }
}
module "app_tier" {
source = "git::https://github.com/microsoftexpert/terraform-aci-cloud-application-container.git?ref=v1.0.0"
tenant_dn = module.tenant.id
cloud_application_container = { name = "app-tier" }
}π‘ A tenant commonly hosts several cloud application containers, one per application tier or workload boundary. Each is an independent instance of this module.
6 Β· Many containers from one definition (caller-side for_each)
locals {
cloud_app_containers = {
"web-tier" = { name = "web-tier", description = "Public-facing web tier" }
"app-tier" = { name = "app-tier", description = "Internal application tier" }
"data-tier" = { name = "data-tier", description = "Data tier" }
}
}
module "cloud_app_containers" {
source = "git::https://github.com/microsoftexpert/terraform-aci-cloud-application-container.git?ref=v1.0.0"
for_each = local.cloud_app_containers
tenant_dn = module.tenant.id
cloud_application_container = each.value
}
output "cloud_app_container_dns" {
value = { for k, m in module.cloud_app_containers : k => m.id }
}π‘ Instantiate the module with
for_eachto manage a fleet of cloud application containers from a single, auditable map.
7 Β· Reading outputs for downstream wiring
module "cloud_app_container" {
source = "git::https://github.com/microsoftexpert/terraform-aci-cloud-application-container.git?ref=v1.0.0"
tenant_dn = module.tenant.id
cloud_application_container = { name = "web-tier" }
}
output "cloud_app_container_dn" { value = module.cloud_app_container.id } # uni/tn-cloud-prod/cloudapp-web-tier
output "cloud_app_container_name" { value = module.cloud_app_container.name }8 Β· Wiring the tenant DN into this module
module "tenant" {
source = "git::https://github.com/microsoftexpert/terraform-aci-tenant.git?ref=v1.0.0"
tenant = { name = "cloud-prod" }
}
module "cloud_app_container" {
source = "git::https://github.com/microsoftexpert/terraform-aci-cloud-application-container.git?ref=v1.0.0"
tenant_dn = module.tenant.id # <-- the tenant's DN becomes the container's parent
cloud_application_container = { name = "web-tier" }
}9 Β· Least-privilege operating model (documentation variant)
# Configure the provider with a login scoped to cloud-tenant management only β
# not a fabric-wide admin β for day-2 changes to an existing cloud tenant.
provider "aci" {
# username = "svc-cloud-tenant-admin" # a tenant-admin role, write-scoped to
# # this tenant's security domain
# private_key = var.apic_private_key # signature auth avoids login-rate limits
# cert_name = "terraform-cert"
# url = "https://cloudapic.example.com"
# insecure = false
}
module "cloud_app_container" {
source = "git::https://github.com/microsoftexpert/terraform-aci-cloud-application-container.git?ref=v1.0.0"
tenant_dn = module.tenant.id
cloud_application_container = { name = "web-tier" }
}π Prefer signature-based (X.509) auth for automation to avoid APIC login-rate thresholds.
10 Β· Wiring the container DN into a cloud EPG
module "cloud_app_container" {
source = "git::https://github.com/microsoftexpert/terraform-aci-cloud-application-container.git?ref=v1.0.0"
tenant_dn = module.tenant.id
cloud_application_container = { name = "web-tier" }
}
module "cloud_epg" {
source = "git::https://github.com/microsoftexpert/terraform-aci-cloud-epg.git?ref=v1.0.0"
cloud_application_container_dn = module.cloud_app_container.id # <-- this module's output becomes the EPG's parent
cloud_epg = { name = "web-epg" }
}11 Β· Fully-annotated container (all available metadata)
module "cloud_app_container" {
source = "git::https://github.com/microsoftexpert/terraform-aci-cloud-application-container.git?ref=v1.0.0"
tenant_dn = module.tenant.id
cloud_application_container = {
name = "web-tier"
name_alias = "Web Tier"
description = "Cloud application container for the web tier"
}
}12 Β· Separate containers per environment (dev / prod)
module "cloud_app_container_dev" {
source = "git::https://github.com/microsoftexpert/terraform-aci-cloud-application-container.git?ref=v1.0.0"
tenant_dn = module.tenant_dev.id
cloud_application_container = { name = "web-tier", description = "Development" }
}
module "cloud_app_container_prod" {
source = "git::https://github.com/microsoftexpert/terraform-aci-cloud-application-container.git?ref=v1.0.0"
tenant_dn = module.tenant_prod.id
cloud_application_container = { name = "web-tier", description = "Production" }
}π‘ Because the container's identity is scoped by its parent tenant DN, the same
namecan be reused safely across tenants that represent different environments.
13 Β· ποΈ End-to-end composition β tenant β cloud application container β cloud EPG
provider "aci" {
# configured + authenticated by the caller against a Cloud Network Controller;
# insecure = false in production
}
# 1) The keystone tenant on the Cloud Network Controller.
module "tenant" {
source = "git::https://github.com/microsoftexpert/terraform-aci-tenant.git?ref=v1.0.0"
tenant = { name = "cloud-prod", description = "Cloud production tenant" }
}
# 2) The cloud application container β this module.
module "cloud_app_container" {
source = "git::https://github.com/microsoftexpert/terraform-aci-cloud-application-container.git?ref=v1.0.0"
tenant_dn = module.tenant.id
cloud_application_container = {
name = "web-tier"
description = "Public-facing web tier"
}
}
# 3) A cloud EPG created within the container.
module "cloud_epg" {
source = "git::https://github.com/microsoftexpert/terraform-aci-cloud-epg.git?ref=v1.0.0"
cloud_application_container_dn = module.cloud_app_container.id
cloud_epg = { name = "web-epg" }
}
output "cloud_app_container_dn" { value = module.cloud_app_container.id }ποΈ One tenant in; a cloud application container and its cloud EPGs out. This module is the single grouping construct every cloud EPG in the tenant nests under, mirroring how an on-premises application profile anchors application EPGs.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
tenant_dn |
string |
β | β | DN of the parent tenant (e.g. module.tenant.id), format uni/tn-{name}. |
cloud_application_container |
object({...}) |
β | β | The container: name (required, immutable) plus the metadata tail this resource exposes. |
Full input schema (from variables.tf)
variable "tenant_dn" {
type = string
# validation: must match ^uni/tn-
}
variable "cloud_application_container" {
type = object({
name = string # REQUIRED, immutable (force-new), 1-64 chars
annotation = optional(string, "orchestrator:terraform") # ACI annotation marker (kept identifiable)
name_alias = optional(string, null) # GUI display alias
description = optional(string, null) # free-form description
})
# validation: name is 1-64 chars and matches ^[a-zA-Z0-9_.:-]+$ (ACI naming rules)
}| Output | Description | Notes |
|---|---|---|
id |
Cloud application container Distinguished Name (uni/tn-{name}/cloudapp-{name}) |
Primary cross-module reference β pass as the parent DN to a cloud-epg module. |
name |
Cloud application container name | For composition / audit. |
- One keystone, no children.
aci_cloud_applicationcontainer.thisis the single resource. Cloud EPGs that live under it are owned by their own module and reference this container by DN β keeping this module small and composable along the cloud MIT. - Classic (SDKv2) shape, not migrated. Unlike migrated siblings in this suite, this resource has no typed
relation_to_*attributes and noannotations/tagslists β it exposes onlyname,tenant_dn,annotation,name_alias, anddescription. The module models exactly that surface; nothing is invented. - Parent wiring uses
tenant_dndeliberately. This suite's general convention prefers a resource's currentparent_dnover a deprecatedtenant_dnwhere both exist; here the live schema exposes onlytenant_dn, so that is the correct β and only β choice. - Immutable identity.
cloud_application_container.nameis force-new: a rename destroys and recreates the container (and, server-side, any cloud EPGs under its DN). The twovalidationblocks reject names that violate the ACI length/character rules at plan time, not apply time. - Secure by omission. The minimal call preserves the
orchestrator:terraformannotation and sets no additional posture β there is no permissive knob on this keystone to loosen.
| Concern | Secure default | How to opt out (deliberately) |
|---|---|---|
cloud_application_container.annotation |
orchestrator:terraform β Terraform-managed objects stay identifiable in APIC |
Extend the marker (e.g. add a team suffix); do not blank it. |
| Transport (provider) | This suite instructs callers to set insecure = false with CA trust |
The provider default is insecure = true; do not keep it as a steady state. |
| Secrets | None accepted or emitted | n/a β this keystone carries no secret material; credentials are provider config. |
| Scope | Only the parent tenant DN is required; no fabric-wide or cross-tenant reach | n/a β this resource has no relation attributes to widen. |
# From the module directory (offline, no credentials, no backend):
terraform init -backend=false
terraform validate
terraform fmt -check- Pin the module by immutable tag:
?ref=v1.0.0β never a branch. - This module is plan-only from the library's perspective. A human runs
terraform plan/applyagainst a Cloud Network Controller from their own pipeline, with a login scoped to the permissions above. No cloud apply happens here.
The offline proof gate for this module:
- β
terraform validateβ parses the module, resolves thecloud_application_containerobject type, runs thenamevalidations, and confirms every argument exists in the provider schema. - β
terraform fmt -checkβ canonical formatting. - β Not exercised offline (only a real
plan/applyagainst a Cloud Network Controller covers these): DN validation oftenant_dn, APIC-side name-collision checks within the tenant, and the computed DN returned asid.
$ terraform output
id = "uni/tn-cloud-prod/cloudapp-web-tier"
name = "web-tier"
| Symptom | Cause | Fix |
|---|---|---|
cloud_application_container.name must be 1-64 characters |
Name is empty or too long | Use a 1-64 character name. |
cloud_application_container.name may contain only letters, digits, and the characters _ . : - |
Name has spaces or unsupported characters | Remove spaces/special characters (ACI naming rules). |
tenant_dn must be a tenant DN of the form uni/tn-{name} |
tenant_dn was not a valid tenant DN |
Pass the tenant module's id output (or a valid uni/tn-{name} DN). |
Changing name wants to destroy/recreate the container |
cloud_application_container.name is immutable (force-new) |
Treat a rename as a migration; expect the container and its cloud EPGs to be replaced. |
| Apply fails with a resource-not-found / unsupported-controller error | Applied against an on-premises APIC instead of a Cloud Network Controller | Point the provider at a Cisco Cloud APIC (Cloud Network Controller); this resource is cloud-only. |
Post ... 401 / authentication error |
Provider not configured or wrong credentials | Configure the aci provider with valid credentials and url; prefer signature auth for automation. |
| TLS verification error against the controller | insecure = false (correct) but no CA trust |
Install the controller's CA chain in the caller's trust store rather than reverting to insecure = true. |
- Cisco ACI provider β
aci_cloud_applicationcontainer - Cisco ACI provider β provider configuration & authentication
- Cisco Cloud APIC object model β class
cloudApp(the cloud application container). - Sibling modules:
terraform-aci-tenant,terraform-aci-cloud-epg,terraform-aci-cloud-context-profile,terraform-aci-cloud-external-epg. - This module's
SCOPE.mdβ the cross-module contract.
π "Infrastructure as Code should be standardized, consistent, and secure."