Module 3 : Modèle Objet ACI
Objectifs du Module
À la fin de ce module, vous serez capable de :
- Expliquer la hiérarchie des objets ACI
- Créer et gérer des Tenants
- Comprendre les VRFs et leur isolation
- Configurer des Bridge Domains et Subnets
- Maîtriser les EPGs (Endpoint Groups)
- Associer les EPGs aux domaines physiques/virtuels
Durée estimée : 4 heures
Hiérarchie des Objets ACI
Vue d'Ensemble
Le modèle objet ACI est hiérarchique. Chaque objet a un parent et peut avoir des enfants.
graph TB
subgraph "Modèle Objet ACI"
ROOT["🌐 Root (uni)"]
ROOT --> TENANT["🏢 Tenant"]
ROOT --> FABRIC["🔧 Fabric"]
ROOT --> INFRA["⚙️ Infra"]
TENANT --> VRF["🔒 VRF<br/>(Context)"]
TENANT --> AP["📱 Application Profile"]
TENANT --> CONTRACT["📋 Contract"]
VRF --> BD["🌉 Bridge Domain"]
BD --> SUBNET["📍 Subnet"]
AP --> EPG["📦 EPG<br/>(Endpoint Group)"]
EPG --> EP["💻 Endpoint<br/>(VM, Serveur)"]
end
style TENANT fill:#4caf50,color:#fff
style VRF fill:#2196f3,color:#fff
style BD fill:#FF9800800800,color:#fff
style EPG fill:#9C27B0,color:#fff
Relations Entre Objets
graph LR
subgraph "Relations Logiques"
EPG["EPG"]
BD["Bridge Domain"]
VRF["VRF"]
CONTRACT["Contract"]
EPG -->|"1 EPG → 1 BD"| BD
BD -->|"1 BD → 1 VRF"| VRF
EPG -->|"Consomme/Fournit"| CONTRACT
end
style EPG fill:#9C27B0,color:#fff
style BD fill:#FF9800800800,color:#fff
style VRF fill:#2196f3,color:#fff
style CONTRACT fill:#9c27b0,color:#fff
Règles fondamentales :
| Relation | Description |
|---|---|
| EPG → BD | Un EPG appartient à un seul Bridge Domain |
| BD → VRF | Un BD appartient à un seul VRF |
| EPG ↔ Contract | Un EPG peut consommer/fournir plusieurs Contracts |
| VRF → BD | Un VRF peut contenir plusieurs Bridge Domains |
Le Tenant : Conteneur Logique
Qu'est-ce qu'un Tenant ?
Un Tenant est le conteneur de plus haut niveau pour isoler les ressources. C'est l'équivalent d'un "projet" ou d'une "organisation".
graph TB
subgraph "Fabric ACI"
subgraph "Tenant: Worldline-Prod"
VRF_WL["VRF Production"]
APP_WL["App: Payment"]
end
subgraph "Tenant: ClientA"
VRF_A["VRF ClientA"]
APP_A["App: E-commerce"]
end
subgraph "Tenant: common"
SHARED["Objets partagés<br/>(DNS, NTP, etc.)"]
end
end
style VRF_WL fill:#4caf50,color:#fff
style VRF_A fill:#2196f3,color:#fff
style SHARED fill:#FF9800800800,color:#fff
Tenants Système
ACI crée automatiquement 3 Tenants système :
| Tenant | Rôle | Modifiable ? |
|---|---|---|
| infra | Infrastructure fabric (TEP, VXLAN) | Non |
| common | Objets partagés entre Tenants | Oui (limité) |
| mgmt | Management (OOB, In-band) | Oui (limité) |
Bonne Pratique : Tenant common
Utilisez le Tenant common pour les ressources partagées :
- Contracts pour accès DNS, NTP, AD
- L3Outs partagés (Internet, WAN)
- Filtres réutilisables
Terraform : Créer un Tenant
# providers.tf
terraform {
required_providers {
aci = {
source = "CiscoDevNet/aci"
version = "~> 2.0"
}
}
}
provider "aci" {
username = var.apic_username
password = var.apic_password
url = var.apic_url
insecure = true # Désactive vérification SSL (lab only)
}
# tenant.tf
resource "aci_tenant" "worldline_prod" {
name = "Worldline-Prod"
description = "Tenant production Worldline - PCI-DSS"
# Annotation pour traçabilité
annotation = "orchestrator:terraform"
}
# Output pour référence
output "tenant_dn" {
value = aci_tenant.worldline_prod.id
description = "Distinguished Name du Tenant"
}
Résultat :
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Outputs:
tenant_dn = "uni/tn-Worldline-Prod"
Le VRF : Isolation de Routage
Qu'est-ce qu'un VRF ?
VRF = Virtual Routing and Forwarding
Un VRF est une instance de routage isolée. Les endpoints dans des VRFs différents ne peuvent pas communiquer (sauf configuration explicite).
graph TB
subgraph "Tenant: Worldline-Prod"
subgraph "VRF: Production"
BD_PROD1["BD: Web"]
BD_PROD2["BD: App"]
BD_PROD3["BD: DB"]
end
subgraph "VRF: Management"
BD_MGMT["BD: Monitoring"]
end
subgraph "VRF: PCI-CDE"
BD_PCI1["BD: Payment"]
BD_PCI2["BD: Cardholder"]
end
end
BD_PROD1 -.->|"❌ Isolé"| BD_PCI1
BD_PROD1 -.->|"❌ Isolé"| BD_MGMT
style BD_PCI1 fill:#f44336,color:#fff
style BD_PCI2 fill:#f44336,color:#fff
Policy Control Enforcement
Le VRF a un paramètre crucial : Policy Control Enforcement Preference
| Valeur | Comportement |
|---|---|
| enforced | Les Contracts sont appliqués (whitelist) |
| unenforced | Tout le trafic intra-VRF est autorisé |
Production : Toujours enforced
En production, utilisez toujours enforced pour bénéficier de la micro-segmentation. Le mode unenforced désactive la sécurité !
Terraform : Créer un VRF
# vrf.tf
resource "aci_vrf" "production" {
tenant_dn = aci_tenant.worldline_prod.id
name = "Production"
description = "VRF Production - Applications métier"
# Sécurité : Contracts enforced
pc_enf_pref = "enforced"
# Direction d'enforcement (ingress = plus performant)
pc_enf_dir = "ingress"
annotation = "orchestrator:terraform"
}
resource "aci_vrf" "pci_cde" {
tenant_dn = aci_tenant.worldline_prod.id
name = "PCI-CDE"
description = "VRF PCI-DSS Cardholder Data Environment"
pc_enf_pref = "enforced"
pc_enf_dir = "ingress"
# Désactiver ICMP redirect pour sécurité PCI
bd_enforced_enable = "yes"
annotation = "orchestrator:terraform"
}
Leaking Entre VRFs
Pour permettre la communication entre VRFs (ex: Management → Production) :
graph LR
subgraph "VRF Leaking"
VRF1["VRF: Management"]
VRF2["VRF: Production"]
VRF1 <-->|"Route Leak<br/>(Contract requis)"| VRF2
end
style VRF1 fill:#2196f3,color:#fff
style VRF2 fill:#4caf50,color:#fff
# Route leaking entre VRFs (configuration avancée)
resource "aci_vrf_leak_epg_bd_subnet" "mgmt_to_prod" {
vrf_dn = aci_vrf.management.id
leak_to_vrf_dn = aci_vrf.production.id
subnet = "10.1.0.0/24"
}
Le Bridge Domain : Domaine de Broadcast
Qu'est-ce qu'un Bridge Domain ?
Un Bridge Domain (BD) est l'équivalent ACI d'un VLAN, mais avec plus de fonctionnalités.
graph TB
subgraph "Bridge Domain"
BD["🌉 Bridge Domain: Web-Servers"]
BD --> FEAT1["🔀 Unicast Routing"]
BD --> FEAT2["📍 Subnets (Gateway)"]
BD --> FEAT3["📡 ARP Flooding/Proxy"]
BD --> FEAT4["🔗 Lié à 1 VRF"]
end
subgraph "EPGs dans ce BD"
EPG1["EPG: Apache"]
EPG2["EPG: Nginx"]
end
BD --> EPG1
BD --> EPG2
style BD fill:#FF9800800800,color:#fff
style EPG1 fill:#9C27B0,color:#fff
style EPG2 fill:#9C27B0,color:#fff
Paramètres Importants du BD
| Paramètre | Description | Recommandation |
|---|---|---|
| arp_flood | Flood ARP dans le BD | no (utiliser ARP Proxy) |
| unicast_route | Activer routage L3 | yes |
| unk_mac_ucast_act | Action si MAC inconnue | proxy |
| unk_mcast_act | Action si multicast inconnu | flood |
| limit_ip_learn_to_subnets | Limiter apprentissage IP | yes (sécurité) |
BD Hardware vs Software Proxy
graph TB
subgraph "ARP Proxy Mode"
VM1["VM1 : Qui a 10.1.1.20 ?"]
LEAF["Leaf (VTEP)"]
SPINE["Spine / APIC"]
VM2["VM2 : 10.1.1.20"]
VM1 -->|"1. ARP Request"| LEAF
LEAF -->|"2. Lookup local"| LEAF
LEAF -.->|"3. Si inconnu, query COOP"| SPINE
SPINE -.->|"4. Réponse COOP"| LEAF
LEAF -->|"5. ARP Reply proxy"| VM1
end
style LEAF fill:#4caf50,color:#fff
COOP (Council of Oracles Protocol) : Base de données distribuée sur les Spines qui connaît tous les endpoints.
Terraform : Créer un Bridge Domain
# bridge_domain.tf
resource "aci_bridge_domain" "web" {
tenant_dn = aci_tenant.worldline_prod.id
name = "BD-Web"
# Lien obligatoire vers le VRF
relation_fv_rs_ctx = aci_vrf.production.id
# Paramètres réseau
arp_flood = "no" # Utiliser proxy ARP
unicast_route = "yes" # Activer routage L3
unk_mac_ucast_act = "proxy" # Proxy pour MAC inconnue
unk_mcast_act = "flood" # Flood multicast
# Sécurité
limit_ip_learn_to_subnets = "yes"
ep_move_detect_mode = "garp" # Détection mobilité
annotation = "orchestrator:terraform"
}
# Subnet associé au BD
resource "aci_subnet" "web_subnet" {
parent_dn = aci_bridge_domain.web.id
ip = "10.1.1.1/24" # Gateway
scope = ["public"] # Annoncé en externe
description = "Subnet serveurs Web"
# Options avancées
preferred = "yes" # Subnet préféré pour ce BD
virtual = "no" # IP réelle (pas anycast)
}
Multi-Subnet BD
Un BD peut avoir plusieurs subnets :
# BD avec plusieurs subnets
resource "aci_bridge_domain" "app" {
tenant_dn = aci_tenant.worldline_prod.id
name = "BD-App"
relation_fv_rs_ctx = aci_vrf.production.id
}
resource "aci_subnet" "app_primary" {
parent_dn = aci_bridge_domain.app.id
ip = "10.1.2.1/24"
scope = ["public"]
preferred = "yes"
}
resource "aci_subnet" "app_secondary" {
parent_dn = aci_bridge_domain.app.id
ip = "10.1.3.1/24"
scope = ["private"] # Non annoncé en externe
preferred = "no"
}
L'Application Profile : Conteneur d'EPGs
Qu'est-ce qu'un Application Profile ?
Un Application Profile (AP) est un conteneur logique qui regroupe les EPGs d'une même application.
graph TB
subgraph "Tenant: Worldline-Prod"
subgraph "App Profile: E-Commerce"
EPG_WEB["EPG: Frontend"]
EPG_API["EPG: API"]
EPG_DB["EPG: Database"]
end
subgraph "App Profile: Payment-Gateway"
EPG_PAY["EPG: Payment-API"]
EPG_HSM["EPG: HSM"]
end
end
style EPG_WEB fill:#9C27B0,color:#fff
style EPG_API fill:#9C27B0,color:#fff
style EPG_DB fill:#9C27B0,color:#fff
style EPG_PAY fill:#f44336,color:#fff
style EPG_HSM fill:#f44336,color:#fff
Terraform : Créer un Application Profile
# application_profile.tf
resource "aci_application_profile" "ecommerce" {
tenant_dn = aci_tenant.worldline_prod.id
name = "E-Commerce"
description = "Application e-commerce B2C"
annotation = "orchestrator:terraform"
}
resource "aci_application_profile" "payment" {
tenant_dn = aci_tenant.worldline_prod.id
name = "Payment-Gateway"
description = "Gateway de paiement PCI-DSS"
annotation = "orchestrator:terraform"
}
L'EPG : Endpoint Group
Qu'est-ce qu'un EPG ?
L'EPG (Endpoint Group) est le concept central d'ACI. C'est un groupe d'endpoints (VMs, serveurs) qui partagent les mêmes politiques de sécurité.
graph TB
subgraph "EPG: Web-Servers"
VM1["VM: web-01"]
VM2["VM: web-02"]
VM3["VM: web-03"]
BM["Bare-metal: web-04"]
end
subgraph "Caractéristiques"
SAME_BD["✅ Même Bridge Domain"]
SAME_POL["✅ Mêmes Contracts"]
DIFF_VLAN["⚠️ Peut avoir différents VLANs"]
end
style VM1 fill:#9C27B0,color:#fff
style VM2 fill:#9C27B0,color:#fff
style VM3 fill:#9C27B0,color:#fff
style BM fill:#9C27B0,color:#fff
EPG vs VLAN
| Aspect | VLAN Traditionnel | ACI EPG |
|---|---|---|
| Base | Port physique | Politique applicative |
| Mobilité | Reconfigurer le port | Automatique |
| Sécurité | ACL sur routeur | Contract natif |
| Visibilité | Par switch | Par application |
| Limite | 4094 VLANs | Illimité (logique) |
Terraform : Créer un EPG
# epg.tf
resource "aci_application_epg" "frontend" {
application_profile_dn = aci_application_profile.ecommerce.id
name = "Frontend"
description = "Serveurs web frontend (Apache/Nginx)"
# Lien vers le Bridge Domain
relation_fv_rs_bd = aci_bridge_domain.web.id
# Préférence de flooding (optimize)
flood_on_encap = "disabled"
pref_gr_memb = "exclude"
annotation = "orchestrator:terraform"
}
resource "aci_application_epg" "api" {
application_profile_dn = aci_application_profile.ecommerce.id
name = "API"
description = "API backend (Java/Node)"
relation_fv_rs_bd = aci_bridge_domain.app.id
annotation = "orchestrator:terraform"
}
resource "aci_application_epg" "database" {
application_profile_dn = aci_application_profile.ecommerce.id
name = "Database"
description = "Base de données PostgreSQL"
relation_fv_rs_bd = aci_bridge_domain.db.id
annotation = "orchestrator:terraform"
}
Association aux Domaines
Types de Domaines
Pour que des endpoints rejoignent un EPG, il faut associer l'EPG à un domaine :
graph TB
subgraph "Types de Domaines"
PHY["🔌 Physical Domain<br/>Serveurs bare-metal"]
VMM["🖥️ VMM Domain<br/>VMware, HyperV, ACI-managed"]
L2["🔗 L2 External Domain<br/>Switch externe L2"]
L3["🌐 L3 External Domain<br/>Routeur externe"]
end
EPG["EPG: Frontend"]
EPG --> PHY
EPG --> VMM
style EPG fill:#9C27B0,color:#fff
style PHY fill:#4caf50,color:#fff
style VMM fill:#2196f3,color:#fff
Association EPG → VMM Domain
Pour les environnements VMware :
# Association à un VMM Domain (VMware vCenter)
data "aci_vmm_domain" "vmware_prod" {
provider_profile_dn = "uni/vmmp-VMware"
name = "vCenter-Prod"
}
resource "aci_epg_to_domain" "frontend_vmware" {
application_epg_dn = aci_application_epg.frontend.id
tdn = data.aci_vmm_domain.vmware_prod.id
# VLAN dynamique ou statique
vmm_allow_promiscuous = "reject"
vmm_forged_transmits = "reject"
vmm_mac_changes = "reject"
# Mode d'encapsulation
instr_imedcy = "immediate" # Déploie immédiatement le port-group
res_imedcy = "immediate" # Résout immédiatement les VLANs
}
Association EPG → Physical Domain
Pour les serveurs physiques :
# Association à un Physical Domain
data "aci_physical_domain" "baremetal" {
name = "PhysDom-Baremetal"
}
resource "aci_epg_to_domain" "frontend_physical" {
application_epg_dn = aci_application_epg.frontend.id
tdn = data.aci_physical_domain.baremetal.id
}
# Static binding pour un port physique
resource "aci_epg_to_static_path" "frontend_server" {
application_epg_dn = aci_application_epg.frontend.id
tdn = "topology/pod-1/paths-101/pathep-[eth1/10]"
encap = "vlan-100"
mode = "regular" # trunk, native, regular
}
Schéma Complet : Du Tenant à l'Endpoint
graph TB
subgraph "Tenant: Worldline-Prod"
subgraph "VRF: Production"
subgraph "BD: Web (10.1.1.0/24)"
SUBNET_WEB["Subnet: 10.1.1.1/24"]
end
subgraph "BD: App (10.1.2.0/24)"
SUBNET_APP["Subnet: 10.1.2.1/24"]
end
subgraph "BD: DB (10.1.3.0/24)"
SUBNET_DB["Subnet: 10.1.3.1/24"]
end
end
subgraph "App Profile: E-Commerce"
EPG_FE["EPG: Frontend"]
EPG_API["EPG: API"]
EPG_DB["EPG: Database"]
end
end
EPG_FE -->|"Lié à"| SUBNET_WEB
EPG_API -->|"Lié à"| SUBNET_APP
EPG_DB -->|"Lié à"| SUBNET_DB
subgraph "Endpoints"
VM1["web-01<br/>10.1.1.10"]
VM2["api-01<br/>10.1.2.10"]
VM3["db-01<br/>10.1.3.10"]
end
EPG_FE --> VM1
EPG_API --> VM2
EPG_DB --> VM3
style EPG_FE fill:#9C27B0,color:#fff
style EPG_API fill:#9C27B0,color:#fff
style EPG_DB fill:#9C27B0,color:#fff
style SUBNET_WEB fill:#FF9800800800,color:#fff
style SUBNET_APP fill:#FF9800800800,color:#fff
style SUBNET_DB fill:#FF9800800800,color:#fff
Code Terraform Complet
Voici un exemple complet qui crée toute la structure :
# main.tf - Structure complète d'un Tenant
# Variables
variable "apic_url" {
description = "URL de l'APIC"
type = string
}
variable "apic_username" {
description = "Utilisateur APIC"
type = string
}
variable "apic_password" {
description = "Mot de passe APIC"
type = string
sensitive = true
}
# Provider
terraform {
required_providers {
aci = {
source = "CiscoDevNet/aci"
version = "~> 2.0"
}
}
}
provider "aci" {
username = var.apic_username
password = var.apic_password
url = var.apic_url
insecure = true
}
# Tenant
resource "aci_tenant" "prod" {
name = "Worldline-Prod"
description = "Production environment"
annotation = "orchestrator:terraform"
}
# VRF
resource "aci_vrf" "prod" {
tenant_dn = aci_tenant.prod.id
name = "Production"
pc_enf_pref = "enforced"
pc_enf_dir = "ingress"
annotation = "orchestrator:terraform"
}
# Bridge Domains
resource "aci_bridge_domain" "web" {
tenant_dn = aci_tenant.prod.id
name = "BD-Web"
relation_fv_rs_ctx = aci_vrf.prod.id
arp_flood = "no"
unicast_route = "yes"
annotation = "orchestrator:terraform"
}
resource "aci_bridge_domain" "app" {
tenant_dn = aci_tenant.prod.id
name = "BD-App"
relation_fv_rs_ctx = aci_vrf.prod.id
arp_flood = "no"
unicast_route = "yes"
annotation = "orchestrator:terraform"
}
resource "aci_bridge_domain" "db" {
tenant_dn = aci_tenant.prod.id
name = "BD-Database"
relation_fv_rs_ctx = aci_vrf.prod.id
arp_flood = "no"
unicast_route = "yes"
annotation = "orchestrator:terraform"
}
# Subnets
resource "aci_subnet" "web" {
parent_dn = aci_bridge_domain.web.id
ip = "10.1.1.1/24"
scope = ["public"]
}
resource "aci_subnet" "app" {
parent_dn = aci_bridge_domain.app.id
ip = "10.1.2.1/24"
scope = ["public"]
}
resource "aci_subnet" "db" {
parent_dn = aci_bridge_domain.db.id
ip = "10.1.3.1/24"
scope = ["private"] # DB non exposé en externe
}
# Application Profile
resource "aci_application_profile" "ecommerce" {
tenant_dn = aci_tenant.prod.id
name = "E-Commerce"
description = "Application e-commerce"
annotation = "orchestrator:terraform"
}
# EPGs
resource "aci_application_epg" "frontend" {
application_profile_dn = aci_application_profile.ecommerce.id
name = "Frontend"
relation_fv_rs_bd = aci_bridge_domain.web.id
annotation = "orchestrator:terraform"
}
resource "aci_application_epg" "api" {
application_profile_dn = aci_application_profile.ecommerce.id
name = "API"
relation_fv_rs_bd = aci_bridge_domain.app.id
annotation = "orchestrator:terraform"
}
resource "aci_application_epg" "database" {
application_profile_dn = aci_application_profile.ecommerce.id
name = "Database"
relation_fv_rs_bd = aci_bridge_domain.db.id
annotation = "orchestrator:terraform"
}
# Outputs
output "tenant_dn" {
value = aci_tenant.prod.id
}
output "epg_dns" {
value = {
frontend = aci_application_epg.frontend.id
api = aci_application_epg.api.id
database = aci_application_epg.database.id
}
}
Exercice Pratique
Lab 3.1 : Créer une Structure Tenant Complète
Objectif : Déployer avec Terraform un Tenant avec VRF, BDs et EPGs.
Scénario : Créer l'infrastructure pour une application 3-tier :
Tenant: Lab-Formation
└── VRF: Lab-VRF
├── BD: BD-Frontend (10.10.1.0/24)
├── BD: BD-Backend (10.10.2.0/24)
└── BD: BD-Data (10.10.3.0/24)
App Profile: WebApp
├── EPG: Web (→ BD-Frontend)
├── EPG: App (→ BD-Backend)
└── EPG: DB (→ BD-Data)
Étapes :
- Créez un répertoire
lab3/et un fichiermain.tf - Configurez le provider ACI
- Créez le Tenant, VRF, Bridge Domains
- Créez l'Application Profile et les EPGs
- Exécutez
terraform init,plan,apply - Vérifiez dans l'APIC GUI
Contraintes :
- Tous les objets doivent avoir l'annotation
orchestrator:terraform - Le VRF doit être en mode
enforced - Le subnet DB doit être
private(non annoncé)
Solution Lab 3.1
# lab3/main.tf
terraform {
required_providers {
aci = {
source = "CiscoDevNet/aci"
version = "~> 2.0"
}
}
}
provider "aci" {
username = "admin"
password = "C1sco123!"
url = "https://sandboxapicdc.cisco.com"
insecure = true
}
# Tenant
resource "aci_tenant" "lab" {
name = "Lab-Formation"
description = "Tenant de formation Terraform ACI"
annotation = "orchestrator:terraform"
}
# VRF
resource "aci_vrf" "lab" {
tenant_dn = aci_tenant.lab.id
name = "Lab-VRF"
pc_enf_pref = "enforced"
annotation = "orchestrator:terraform"
}
# Bridge Domains
resource "aci_bridge_domain" "frontend" {
tenant_dn = aci_tenant.lab.id
name = "BD-Frontend"
relation_fv_rs_ctx = aci_vrf.lab.id
arp_flood = "no"
unicast_route = "yes"
annotation = "orchestrator:terraform"
}
resource "aci_bridge_domain" "backend" {
tenant_dn = aci_tenant.lab.id
name = "BD-Backend"
relation_fv_rs_ctx = aci_vrf.lab.id
arp_flood = "no"
unicast_route = "yes"
annotation = "orchestrator:terraform"
}
resource "aci_bridge_domain" "data" {
tenant_dn = aci_tenant.lab.id
name = "BD-Data"
relation_fv_rs_ctx = aci_vrf.lab.id
arp_flood = "no"
unicast_route = "yes"
annotation = "orchestrator:terraform"
}
# Subnets
resource "aci_subnet" "frontend" {
parent_dn = aci_bridge_domain.frontend.id
ip = "10.10.1.1/24"
scope = ["public"]
}
resource "aci_subnet" "backend" {
parent_dn = aci_bridge_domain.backend.id
ip = "10.10.2.1/24"
scope = ["public"]
}
resource "aci_subnet" "data" {
parent_dn = aci_bridge_domain.data.id
ip = "10.10.3.1/24"
scope = ["private"] # Non annoncé en externe
}
# Application Profile
resource "aci_application_profile" "webapp" {
tenant_dn = aci_tenant.lab.id
name = "WebApp"
description = "Application web 3-tier"
annotation = "orchestrator:terraform"
}
# EPGs
resource "aci_application_epg" "web" {
application_profile_dn = aci_application_profile.webapp.id
name = "Web"
relation_fv_rs_bd = aci_bridge_domain.frontend.id
annotation = "orchestrator:terraform"
}
resource "aci_application_epg" "app" {
application_profile_dn = aci_application_profile.webapp.id
name = "App"
relation_fv_rs_bd = aci_bridge_domain.backend.id
annotation = "orchestrator:terraform"
}
resource "aci_application_epg" "db" {
application_profile_dn = aci_application_profile.webapp.id
name = "DB"
relation_fv_rs_bd = aci_bridge_domain.data.id
annotation = "orchestrator:terraform"
}
# Outputs
output "structure" {
value = {
tenant = aci_tenant.lab.name
vrf = aci_vrf.lab.name
epgs = {
web = aci_application_epg.web.name
app = aci_application_epg.app.name
db = aci_application_epg.db.name
}
}
}
Exécution :
Vérification APIC :
- Tenants > Lab-Formation > Networking > VRFs
- Tenants > Lab-Formation > Networking > Bridge Domains
- Tenants > Lab-Formation > Application Profiles > WebApp > EPGs
Points Clés à Retenir
Résumé du Module 3
Hiérarchie des Objets
Tenant (isolation administrative)
└── VRF (isolation routage)
└── Bridge Domain (domaine L2/L3)
└── Subnet (gateway IP)
└── Application Profile (conteneur logique)
└── EPG (groupe de sécurité)
Relations Clés
- EPG → BD : Un EPG appartient à un seul BD
- BD → VRF : Un BD appartient à un seul VRF
- EPG ↔ Domain : Association physique/virtuel
- EPG ↔ Contract : Règles de communication
Bonnes Pratiques
- VRF en mode
enforceden production - Annotation
orchestrator:terraformsur tous les objets - Subnets DB en
private(non annoncés) - Un EPG = un rôle applicatif (pas un VLAN)
Prochaine Étape
Les EPGs ne peuvent pas communiquer sans Contracts. Module 4 !
Exercice : À Vous de Jouer
Mise en Pratique
Objectif : Créer une infrastructure réseau complète avec Tenant, VRF, Bridge Domains et EPGs en Terraform
Contexte : Vous devez déployer un tenant ACI pour une application web 3-tiers. L'application nécessite une séparation réseau entre les couches Web, Application et Base de données, tout en restant dans le même VRF pour faciliter le routage. Chaque couche aura son propre Bridge Domain et EPG.
Tâches à réaliser :
- Créer un Tenant nommé "WebApp-Prod"
- Créer un VRF "Production" avec policy enforcement activé
- Créer 3 Bridge Domains : BD-Web (10.1.1.0/24), BD-App (10.1.2.0/24), BD-DB (10.1.3.0/24)
- Créer un Application Profile "3Tier-App"
- Créer 3 EPGs : Web-Frontend, App-Backend, Database
- Associer chaque EPG à son Bridge Domain correspondant
Critères de validation :
- [ ] Le Tenant est créé avec une annotation "managed-by:terraform"
- [ ] Le VRF est en mode "enforced" (whitelist par défaut)
- [ ] Chaque BD a un subnet avec scope "public" pour permettre le routage
- [ ] Les EPGs sont dans le même Application Profile
- [ ] La hiérarchie Tenant → VRF → BD → EPG est correcte
- [ ] Le code utilise des variables pour les noms et les subnets
Solution
variables.tf
variable "apic_url" {
description = "URL de l'APIC"
type = string
}
variable "apic_username" {
description = "Username pour l'APIC"
type = string
sensitive = true
}
variable "apic_password" {
description = "Password pour l'APIC"
type = string
sensitive = true
}
variable "tenant_name" {
description = "Nom du tenant"
type = string
default = "WebApp-Prod"
}
variable "subnets" {
description = "Subnets pour chaque tier"
type = object({
web = string
app = string
db = string
})
default = {
web = "10.1.1.1/24"
app = "10.1.2.1/24"
db = "10.1.3.1/24"
}
}
main.tf
terraform {
required_version = ">= 1.0"
required_providers {
aci = {
source = "CiscoDevNet/aci"
version = "~> 2.13"
}
}
}
provider "aci" {
username = var.apic_username
password = var.apic_password
url = var.apic_url
insecure = true
}
# ===================
# TENANT
# ===================
resource "aci_tenant" "webapp_prod" {
name = var.tenant_name
description = "Tenant pour application Web 3-tiers en production"
annotation = "managed-by:terraform"
}
# ===================
# VRF
# ===================
resource "aci_vrf" "production" {
tenant_dn = aci_tenant.webapp_prod.id
name = "Production"
description = "VRF Production avec policy enforcement"
# Policy Enforcement : enforced = whitelist (tout bloqué par défaut)
pc_enf_pref = "enforced"
pc_enf_dir = "ingress"
annotation = "managed-by:terraform"
}
# ===================
# BRIDGE DOMAINS
# ===================
# Bridge Domain - Web
resource "aci_bridge_domain" "web" {
tenant_dn = aci_tenant.webapp_prod.id
name = "BD-Web"
description = "Bridge Domain pour la couche Web"
relation_fv_rs_ctx = aci_vrf.production.id
# Optimisations ACI
arp_flood = "no"
unicast_route = "yes"
unk_mac_ucast_act = "proxy"
limit_ip_learn_to_subnets = "yes"
annotation = "managed-by:terraform"
}
resource "aci_subnet" "web" {
parent_dn = aci_bridge_domain.web.id
ip = var.subnets.web
scope = ["public"]
description = "Gateway pour couche Web"
}
# Bridge Domain - App
resource "aci_bridge_domain" "app" {
tenant_dn = aci_tenant.webapp_prod.id
name = "BD-App"
description = "Bridge Domain pour la couche Application"
relation_fv_rs_ctx = aci_vrf.production.id
arp_flood = "no"
unicast_route = "yes"
unk_mac_ucast_act = "proxy"
limit_ip_learn_to_subnets = "yes"
annotation = "managed-by:terraform"
}
resource "aci_subnet" "app" {
parent_dn = aci_bridge_domain.app.id
ip = var.subnets.app
scope = ["public"]
description = "Gateway pour couche Application"
}
# Bridge Domain - Database
resource "aci_bridge_domain" "db" {
tenant_dn = aci_tenant.webapp_prod.id
name = "BD-DB"
description = "Bridge Domain pour la couche Database"
relation_fv_rs_ctx = aci_vrf.production.id
arp_flood = "no"
unicast_route = "yes"
unk_mac_ucast_act = "proxy"
limit_ip_learn_to_subnets = "yes"
annotation = "managed-by:terraform"
}
resource "aci_subnet" "db" {
parent_dn = aci_bridge_domain.db.id
ip = var.subnets.db
scope = ["private"] # BD Database en private (pas de routage externe)
description = "Gateway pour couche Database"
}
# ===================
# APPLICATION PROFILE
# ===================
resource "aci_application_profile" "three_tier" {
tenant_dn = aci_tenant.webapp_prod.id
name = "3Tier-App"
description = "Application Profile pour architecture 3-tiers"
annotation = "managed-by:terraform"
}
# ===================
# EPGs
# ===================
# EPG - Web Frontend
resource "aci_application_epg" "web" {
application_profile_dn = aci_application_profile.three_tier.id
name = "Web-Frontend"
description = "EPG pour les serveurs Web (nginx, apache)"
relation_fv_rs_bd = aci_bridge_domain.web.id
# Preferred Group : désactivé pour forcer les contracts
pref_gr_memb = "exclude"
annotation = "managed-by:terraform,tier:web"
}
# EPG - App Backend
resource "aci_application_epg" "app" {
application_profile_dn = aci_application_profile.three_tier.id
name = "App-Backend"
description = "EPG pour les serveurs applicatifs (Java, Python)"
relation_fv_rs_bd = aci_bridge_domain.app.id
pref_gr_memb = "exclude"
annotation = "managed-by:terraform,tier:application"
}
# EPG - Database
resource "aci_application_epg" "database" {
application_profile_dn = aci_application_profile.three_tier.id
name = "Database"
description = "EPG pour les bases de données (PostgreSQL, MySQL)"
relation_fv_rs_bd = aci_bridge_domain.db.id
pref_gr_memb = "exclude"
annotation = "managed-by:terraform,tier:data"
}
outputs.tf
output "tenant_dn" {
description = "DN du tenant créé"
value = aci_tenant.webapp_prod.id
}
output "vrf_dn" {
description = "DN du VRF Production"
value = aci_vrf.production.id
}
output "bridge_domains" {
description = "Liste des Bridge Domains créés"
value = {
web = {
dn = aci_bridge_domain.web.id
name = aci_bridge_domain.web.name
subnet = var.subnets.web
}
app = {
dn = aci_bridge_domain.app.id
name = aci_bridge_domain.app.name
subnet = var.subnets.app
}
db = {
dn = aci_bridge_domain.db.id
name = aci_bridge_domain.db.name
subnet = var.subnets.db
}
}
}
output "epgs" {
description = "Liste des EPGs créés"
value = {
web = {
dn = aci_application_epg.web.id
name = aci_application_epg.web.name
}
app = {
dn = aci_application_epg.app.id
name = aci_application_epg.app.name
}
database = {
dn = aci_application_epg.database.id
name = aci_application_epg.database.name
}
}
}
output "architecture_summary" {
description = "Résumé de l'architecture déployée"
value = {
tenant = var.tenant_name
vrf = "Production (enforced)"
application = "3Tier-App"
bridge_domains = 3
epgs = 3
policy_enforcement = "Whitelist (contracts requis pour communication)"
}
}
Déploiement :
# Initialisation
terraform init
# Validation
terraform validate
# Plan
terraform plan
# Application
terraform apply
# Vérification
terraform output architecture_summary
Résultat attendu :
Une infrastructure réseau complète pour une application 3-tiers est créée dans ACI : - 1 Tenant avec 1 VRF en mode enforced - 3 Bridge Domains avec leurs subnets - 1 Application Profile contenant 3 EPGs - Prêt pour l'ajout de Contracts au module 4
Navigation
| Précédent | Suivant |
|---|---|
| ← Module 2 : Architecture ACI | Module 4 : Contracts & Filters → |
Navigation
| ← Module 2 : Architecture Cisco ACI | Module 4 : Contracts & Filters → |