Skip to content

microsoftexpert/terraform-aci-application-profile

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

💙 Cisco ACI Application Profile Terraform Module

Manage a Cisco ACI application profile — the container that groups an application's EPGs within a tenant (class fvAp, DN uni/tn-{tenant}/ap-{name}) — as a typed, secure-by-default building block targeting CiscoDevNet/aci ~> 2.20.

Terraform Provider Module Version Type Resources

🧩 Overview

This module manages a single ACI application profile and its metadata tail as one coherent, secure-by-default unit:

  • 📦 The application profile (aci_application_profile.this) — the container in the ACI Management Information Tree (MIT) that groups the EPGs representing one application's tiers within a tenant, addressed by the Distinguished Name uni/tn-{tenant}/ap-{name}.
  • 🎚️ QoS priority classpriority, left at the safe unspecified default; a fabric-wide QoS class is opt-in.
  • 🏷️ The ACI metadata tailannotation (preserved as orchestrator:terraform so Terraform-managed objects are identifiable in APIC), name_alias, and description, plus owner_key / owner_tag and the annotations / tags key-value lists.
  • 🔗 A typed monitoring-policy relation — an optional relation_to_monitoring_policy that binds the application profile to a monitoring policy by name.
  • 🔑 Scope via the tenant DN — a required tenant_dn wires this module under its parent tenant; authentication and the APIC URL remain the caller's provider concern and are never module variables.

💡 Why it matters: the application profile is the organizational unit that every application EPG nests under — it is how a tenant's application tiers are grouped, delegated, and reasoned about independently of the tenant's other applications. A consistent, identifiable, least-privilege application profile is the anchor every downstream EPG module wires into through its application_profile_dn input.

❤️ Support this project

If these Terraform modules have been helpful to you or your organization, I'd appreciate your support in any of the following ways:

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!

🗺️ Where this fits in the family

graph LR
  apic["Cisco APIC fabric (provider auth, out of band)"]:::ext
  tn["terraform-aci-tenant"]:::sib
  ap["terraform-aci-application-profile (this module)"]:::this
  fv["aci_application_profile - class fvAp - DN uni/tn-{tenant}/ap-{name}"]:::keystone
  epg["terraform-aci-application-epg"]:::sib
  mon["terraform-aci-monitoring-policy"]:::sib

  apic -->|"provider configured by caller"| ap
  tn -->|"tenant_dn"| ap
  ap -->|"manages"| fv
  ap -->|"application_profile_dn"| epg
  mon -->|"relation_to_monitoring_policy (by name)"| ap

  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;
Loading

The application profile sits directly under its parent tenant. It takes tenant_dn as its only parent variable and emits an id (its DN, uni/tn-{tenant}/ap-{name}) that the application-EPG module consumes as application_profile_dn.

🧬 What this module builds

graph TD
  tdn["tenant_dn (required, parent DN)"]:::in
  n["application_profile.name (required, immutable)"]:::in
  pr["application_profile.priority (QoS class)"]:::in
  meta["annotation / name_alias / description / owner_key / owner_tag"]:::in
  at["annotations / tags (key/value maps)"]:::in
  rel["relation_to_monitoring_policy (typed)"]:::in
  this["aci_application_profile.this (keystone, fvAp)"]:::this
  oid["output: id (DN uni/tn-{tenant}/ap-{name})"]:::out
  onm["output: name"]:::out

  tdn --> this
  n --> this
  pr --> this
  meta --> this
  at --> this
  rel --> 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;
Loading

Resource inventory

Resource Name Cardinality Role
aci_application_profile this 1 (keystone) The application profile / EPG container (fvAp).

✅ Provider / Versions

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 tenant_dn — the parent tenant's DN (uni/tn-{name}).

Schema notes that bite (verified against the live provider schema):

  • 🔒 application_profile.name is immutable. Changing it forces replacement — a brand-new application profile, and every EPG created under its DN goes with it. Treat renames as migrations.
  • ⚠️ The parent is wired through parent_dn, not tenant_dn. The live schema still exposes a tenant_dn resource attribute alongside parent_dn; this module always sets parent_dn (the current attribute) and never the legacy one.
  • ℹ️ aci_application_profile is a migrated (plugin-framework) resource in this provider line. annotations and tags are typed { key, value } lists and relation_to_monitoring_policy is a typed nested attribute — all assigned with =, not as HCL block {} stanzas.
  • ⚠️ annotation (singular) and annotations (plural) are different fields. annotation is the single orchestrator marker (defaults to orchestrator:terraform); annotations is the separate user-defined tagAnnotation list. This module manages both.
  • ℹ️ priority has a documented enum plus an undocumented sibling. The valid priority values are level1-level6 and unspecified; the schema also exposes an undocumented prio attribute, which this module does not manage.
  • ℹ️ Classic relations are retained but unused here. The provider still exposes a flat relation_fv_rs_ap_mon_pol attribute from before migration; this module uses the migrated typed relation_to_monitoring_policy and leaves the classic attribute to the provider.
  • ℹ️ id is the DN (uni/tn-{tenant}/ap-{name}), computed by APIC at create. It is the value downstream EPG modules consume as application_profile_dn.
  • ⚠️ validate_relation_dn (provider default true) means a relation_to_monitoring_policy pointing at a monitoring policy that does not yet exist will fail at apply — create the policy first, or wire it by reference.

🔑 Required APIC Roles & Privileges

Scope the caller's APIC login to the least privilege this module needs:

  • Create / delete an application profile: the tenant-admin role (or a custom role with application-profile write privilege) scoped to the tenant's security domain. This is a tenant-scoped action, not a fabric-wide one.
  • Day-2 management: the same tenant-admin write scope to the tenant's security domain covers ongoing changes.
  • Referenced monitoring policy: read on the monitoring policy targeted by relation_to_monitoring_policy.

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).

Cisco ACI Prerequisites

  • A reachable Cisco APIC (ACI_URL) whose version is compatible with the ~> 2.20 provider, with the provider configured and authenticated by the caller.
  • The parent tenant (tenant_dn) must already exist — this module does not create tenants.
  • In production, the provider should be configured with insecure = false and proper CA trust — the provider's own default (insecure = true, skip TLS verification) is not a safe steady state.
  • If you set relation_to_monitoring_policy, the referenced monitoring policy must exist (or be created in the same configuration) so the provider's DN validation passes.

📁 Module Structure

terraform-aci-application-profile/
├── providers.tf     # terraform{} + required_providers (aci ~> 2.20); no provider block
├── variables.tf     # tenant_dn + the application_profile object — deeply typed, secure defaults, heredoc schema, validations
├── main.tf          # aci_application_profile.this (keystone) + metadata tail + typed monitoring relation
├── 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

⚙️ Quick Start

# 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 "tenant" {
  source = "git::https://github.com/microsoftexpert/terraform-aci-tenant.git?ref=v1.0.0"
  tenant = { name = "core-prod" }
}

module "application_profile" {
  source = "git::https://github.com/microsoftexpert/terraform-aci-application-profile.git?ref=v1.0.0"

  tenant_dn = module.tenant.id
  application_profile = {
    name = "payments"
  }
}

output "application_profile_dn" {
  value = module.application_profile.id # pass this as application_profile_dn to the EPG module
}

🔌 Cross-Module Contract

Consumes

Input Type Typical source
tenant_dn string (DN) terraform-aci-tenant output id
application_profile object({...}) caller (name + QoS priority + metadata tail + optional monitoring relation)

Emits

Output Description Consumed by
id Application profile DN (uni/tn-{tenant}/ap-{name}) — primary reference terraform-aci-application-epg (as application_profile_dn)
name Application profile name composition / audit

📚 Example Library

1 · Minimal — an application profile with secure defaults
module "application_profile" {
  source = "git::https://github.com/microsoftexpert/terraform-aci-application-profile.git?ref=v1.0.0"

  tenant_dn = module.tenant.id
  application_profile = {
    name = "payments"
  }
}

💡 The minimal call creates only the application profile. annotation is preserved as orchestrator:terraform, priority stays unspecified, and no relations are forced.

2 · Description and GUI alias
module "application_profile" {
  source = "git::https://github.com/microsoftexpert/terraform-aci-application-profile.git?ref=v1.0.0"

  tenant_dn = module.tenant.id
  application_profile = {
    name        = "payments"
    name_alias  = "Payments Platform"
    description = "EPG container for the payments application tiers"
  }
}

ℹ️ name_alias is a display alias shown in the APIC GUI; name remains the immutable identity encoded in the DN.

3 · A dedicated QoS priority class
module "application_profile" {
  source = "git::https://github.com/microsoftexpert/terraform-aci-application-profile.git?ref=v1.0.0"

  tenant_dn = module.tenant.id
  application_profile = {
    name     = "payments"
    priority = "level1"
  }
}

⚠️ priority widens QoS treatment fabric-wide for traffic in this application profile's EPGs. Leave it at the default unspecified unless a QoS design calls for a dedicated class.

4 · A custom annotation marker
module "application_profile" {
  source = "git::https://github.com/microsoftexpert/terraform-aci-application-profile.git?ref=v1.0.0"

  tenant_dn = module.tenant.id
  application_profile = {
    name       = "payments"
    annotation = "orchestrator:terraform:platform-team"
  }
}

🔒 Keep the orchestrator:terraform prefix so Terraform-managed objects stay identifiable in APIC. This suite defaults annotation to orchestrator:terraform; override it only to extend, not to erase, that marker.

5 · User metadata via the annotations (tagAnnotation) list
module "application_profile" {
  source = "git::https://github.com/microsoftexpert/terraform-aci-application-profile.git?ref=v1.0.0"

  tenant_dn = module.tenant.id
  application_profile = {
    name = "payments"
    annotations = {
      "cost-center" = "CC-4021"
      "environment" = "production"
      "owner"       = "app-platform"
    }
  }
}

ℹ️ annotations is given as an ergonomic { key = value } map and rendered as the ACI tagAnnotation {key, value} list. Keys are unique by construction.

6 · Classification tags (tagTag) list
module "application_profile" {
  source = "git::https://github.com/microsoftexpert/terraform-aci-application-profile.git?ref=v1.0.0"

  tenant_dn = module.tenant.id
  application_profile = {
    name = "payments"
    tags = {
      "tier" = "gold"
      "pci"  = "in-scope"
    }
  }
}

ℹ️ tags maps to the ACI tagTag list — use it for classification/labeling distinct from the free-form annotations.

7 · Ownership and correlation keys
module "application_profile" {
  source = "git::https://github.com/microsoftexpert/terraform-aci-application-profile.git?ref=v1.0.0"

  tenant_dn = module.tenant.id
  application_profile = {
    name      = "payments"
    owner_key = "cmdb-9931"
    owner_tag = "provisioned-by-app-automation"
  }
}

ℹ️ owner_key and owner_tag let external systems correlate this object with their own records — neither carries security posture.

8 · A typed monitoring-policy relation
module "application_profile" {
  source = "git::https://github.com/microsoftexpert/terraform-aci-application-profile.git?ref=v1.0.0"

  tenant_dn = module.tenant.id
  application_profile = {
    name = "payments"
    relation_to_monitoring_policy = {
      monitoring_policy_name = "prod-monitoring"
    }
  }
}

⚠️ The referenced monitoring policy must exist (or be created in the same apply). With the provider's validate_relation_dn default of true, a dangling relation fails at apply — fix the missing policy rather than disabling validation.

9 · A fully-annotated application profile (all metadata)
module "application_profile" {
  source = "git::https://github.com/microsoftexpert/terraform-aci-application-profile.git?ref=v1.0.0"

  tenant_dn = module.tenant.id
  application_profile = {
    name        = "payments"
    name_alias  = "Payments Platform"
    description = "EPG container for the payments application tiers"
    priority    = "level2"
    owner_key   = "cmdb-9931"
    owner_tag   = "app-automation"
    annotations = { "cost-center" = "CC-4021", "environment" = "production" }
    tags        = { "tier" = "gold" }
  }
}
10 · Least-privilege operating model (documentation variant)
# Configure the provider with a login scoped to tenant management only —
# not a fabric-wide admin — for day-2 changes to an existing application profile.
provider "aci" {
  # username    = "svc-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://apic.example.com"
  # insecure    = false
}

module "application_profile" {
  source    = "git::https://github.com/microsoftexpert/terraform-aci-application-profile.git?ref=v1.0.0"
  tenant_dn = module.tenant.id
  application_profile = { name = "payments" }
}

🔒 Delegate application-profile management to a tenant-admin role scoped to the tenant's security domain. Prefer signature-based (X.509) auth for automation to avoid APIC login-rate thresholds.

11 · Many application profiles from one definition (caller-side for_each)
locals {
  application_profiles = {
    "payments" = { name = "payments", description = "Payments application tiers" }
    "orders"   = { name = "orders", description = "Order-management application tiers" }
    "reporting" = { name = "reporting", description = "Reporting application tiers" }
  }
}

module "application_profiles" {
  source   = "git::https://github.com/microsoftexpert/terraform-aci-application-profile.git?ref=v1.0.0"
  for_each = local.application_profiles

  tenant_dn            = module.tenant.id
  application_profile  = each.value
}

output "application_profile_dns" {
  value = { for k, m in module.application_profiles : k => m.id }
}

💡 Instantiate the module with for_each to manage a fleet of application profiles under one tenant from a single, auditable map.

12 · Reading outputs for downstream wiring
module "application_profile" {
  source    = "git::https://github.com/microsoftexpert/terraform-aci-application-profile.git?ref=v1.0.0"
  tenant_dn = module.tenant.id
  application_profile = { name = "payments" }
}

output "application_profile_dn"   { value = module.application_profile.id }   # uni/tn-core-prod/ap-payments
output "application_profile_name" { value = module.application_profile.name }
13 · Wiring the tenant DN into an application profile
module "tenant" {
  source = "git::https://github.com/microsoftexpert/terraform-aci-tenant.git?ref=v1.0.0"
  tenant = { name = "core-prod" }
}

module "application_profile" {
  source    = "git::https://github.com/microsoftexpert/terraform-aci-application-profile.git?ref=v1.0.0"
  tenant_dn = module.tenant.id # <-- the tenant's DN becomes the application profile's parent
  application_profile = { name = "payments" }
}
14 · Wiring the application profile DN toward an EPG
module "application_profile" {
  source    = "git::https://github.com/microsoftexpert/terraform-aci-application-profile.git?ref=v1.0.0"
  tenant_dn = module.tenant.id
  application_profile = { name = "payments" }
}

module "epg" {
  source                  = "git::https://github.com/microsoftexpert/terraform-aci-application-epg.git?ref=v1.0.0"
  application_profile_dn  = module.application_profile.id # <-- the application profile's DN becomes the EPG's parent
  application_epg         = { name = "payments-web" }
}
15 · 🏗️ End-to-end composition — tenant → VRF → bridge domain → application profile → EPG → contract
provider "aci" {
  # configured + authenticated by the caller; insecure = false in production
}

# 1) The keystone tenant — the root everything nests under.
module "tenant" {
  source = "git::https://github.com/microsoftexpert/terraform-aci-tenant.git?ref=v1.0.0"
  tenant = { name = "core-prod", description = "Core production tenant" }
}

# 2) A VRF in the tenant.
module "vrf" {
  source    = "git::https://github.com/microsoftexpert/terraform-aci-vrf.git?ref=v1.0.0"
  tenant_dn = module.tenant.id
  vrf       = { name = "core-vrf" }
}

# 3) A bridge domain bound to the VRF.
module "bd" {
  source        = "git::https://github.com/microsoftexpert/terraform-aci-bridge-domain.git?ref=v1.0.0"
  tenant_dn     = module.tenant.id
  bridge_domain = { name = "app-bd", relation_to_vrf = { vrf_name = "core-vrf" } }
  subnets       = { "10.0.1.1/24" = { ip = "10.0.1.1/24" } }
}

# 4) This module — the application profile that groups the payments EPGs.
module "application_profile" {
  source    = "git::https://github.com/microsoftexpert/terraform-aci-application-profile.git?ref=v1.0.0"
  tenant_dn = module.tenant.id
  application_profile = { name = "payments" }
}

# 5) An application EPG under the application profile.
module "epg" {
  source                 = "git::https://github.com/microsoftexpert/terraform-aci-application-epg.git?ref=v1.0.0"
  application_profile_dn = module.application_profile.id
  application_epg        = { name = "payments-web", relation_to_bridge_domain = { bridge_domain_name = "app-bd" } }
}

# 6) A contract in the same tenant, consumed by the EPG.
module "contract" {
  source    = "git::https://github.com/microsoftexpert/terraform-aci-contract.git?ref=v1.0.0"
  tenant_dn = module.tenant.id
  contract  = { name = "web-to-app" }
}

output "application_profile_dn" { value = module.application_profile.id }

🏗️ One tenant, VRF, and bridge domain in; an application profile and its EPG out. module.application_profile.id is the single reference every EPG under this application nests into via application_profile_dn.

📥 Inputs

Name Type Required Default Description
tenant_dn string (DN) DN of the parent tenant, e.g. module.tenant.id (format uni/tn-{name}).
application_profile object({...}) The application profile: name (required, immutable) plus QoS priority, the metadata tail, and an optional typed monitoring relation.
Full input schema (from variables.tf)
variable "tenant_dn" {
  type = string
  # validation: must match ^uni/tn- (a tenant DN)
}

variable "application_profile" {
  type = object({
    name        = string                                     # REQUIRED, immutable (force-new), 1-64 chars
    priority    = optional(string, "unspecified")             # level1-level6 | unspecified
    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
    owner_key   = optional(string, null)                     # client correlation key
    owner_tag   = optional(string, null)                     # client correlation tag
    annotations = optional(map(string), {})                  # tagAnnotation {key,value} pairs as a {key = value} map
    tags        = optional(map(string), {})                  # tagTag {key,value} pairs as a {key = value} map
    relation_to_monitoring_policy = optional(object({        # typed relation fvRsApMonPol, by name
      monitoring_policy_name = string
    }), null)
  })
  # validation: name is 1-64 chars and matches ^[a-zA-Z0-9_.:-]+$ (ACI naming rules)
  # validation: priority is one of level1-level6, unspecified
}

🧾 Outputs

Output Description Notes
id Application profile Distinguished Name (uni/tn-{tenant}/ap-{name}) Primary cross-module reference — pass as application_profile_dn.
name Application profile name For composition / audit.

🧠 Architecture Notes

  • One keystone, no children. aci_application_profile.this is the single resource. The application profile is an organizational container; the EPGs that live under it are owned by terraform-aci-application-epg and reference this application profile by DN — keeping each module small and composable along the MIT.
  • Migrated (plugin-framework) shape. annotations, tags, and relation_to_monitoring_policy are typed attributes, not HCL blocks. The module accepts annotations / tags as { key = value } maps and converts each to the provider's [{ key, value }] list form — ergonomic for callers, faithful to the schema for the provider.
  • Unmanaged when empty. When annotations / tags are empty the module passes null rather than an empty list, so it never fights provider-computed state or produces a spurious diff.
  • Current attribute, not the deprecated one. The parent is wired through parent_dn; the live schema retains a legacy tenant_dn resource attribute that this module deliberately does not set.
  • Immutable identity. application_profile.name is force-new: a rename destroys and recreates the application profile (and, server-side, every EPG under its DN). The two name validation blocks reject names that violate the ACI length/character rules at plan time, not apply time.
  • Secure by omission. The minimal call preserves the orchestrator:terraform annotation, leaves priority at unspecified, and sets no relations — nothing permissive is created by default.

🧱 Design Principles

Concern Secure default How to opt out (deliberately)
application_profile.annotation orchestrator:terraform — Terraform-managed objects stay identifiable in APIC Extend the marker (e.g. add a team suffix); do not blank it.
application_profile.priority unspecified — no fabric-wide QoS class forced Set level1-level6 when a QoS design calls for it.
annotations / tags {} — no user metadata managed, no spurious diffs Populate the maps explicitly.
relation_to_monitoring_policy null — no relation forced Set it to bind a monitoring policy by name.
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 — the application profile carries no secret material; credentials are provider config.

🚀 Runbook

# 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 / apply against a sub-production APIC from their own pipeline, with a login scoped to the permissions above. No cloud apply happens here.

🧪 Testing

The offline proof gate for this module:

  • terraform validate — parses the module, resolves the application_profile object type, runs the name and priority validations, and confirms every argument exists in the provider schema.
  • terraform fmt -check — canonical formatting.
  • Not exercised offline (only a real plan / apply against an APIC covers these): DN validation of relation_to_monitoring_policy (server-side validate_relation_dn), APIC-side name-collision checks against the parent tenant, and the computed DN returned as id.

💬 Example Output

$ terraform output
id   = "uni/tn-core-prod/ap-payments"
name = "payments"

🔍 Troubleshooting

Symptom Cause Fix
tenant_dn must be a tenant DN of the form uni/tn-{name} tenant_dn was not the tenant's DN Pass the tenant module's id output, e.g. module.tenant.id.
application_profile.name must be 1-64 characters Name is empty or too long Use a 1-64 character name.
application_profile.name may contain only letters, digits, and the characters _ . : - Name has spaces or unsupported characters Remove spaces/special characters (ACI naming rules).
application_profile.priority must be one of: level1, level2, level3, level4, level5, level6, unspecified An invalid priority value was supplied Use one of the documented enum values.
Changing name wants to destroy/recreate the application profile application_profile.name is immutable (force-new) Treat a rename as a migration; expect the application profile and its EPGs to be replaced.
Apply fails validating the monitoring-policy relation relation_to_monitoring_policy targets a policy that does not exist Create the monitoring policy first (or in the same apply); do not disable validate_relation_dn.
Post ... 401 / authentication error Provider not configured or wrong credentials Configure the aci provider with valid credentials and url; prefer signature auth for automation.

🔗 Related Docs

  • Cisco ACI provider — aci_application_profile
  • Cisco ACI provider — provider configuration & authentication
  • Cisco APIC object model — class fvAp (the application profile / EPG container).
  • Sibling modules: terraform-aci-tenant, terraform-aci-application-epg, terraform-aci-vrf, terraform-aci-bridge-domain, terraform-aci-contract, terraform-aci-monitoring-policy.
  • This module's SCOPE.md — the cross-module contract.

💙 "Infrastructure as Code should be standardized, consistent, and secure."

Releases

Packages

Contributors

Languages