Skip to content

Terraform で ACI 上に L3out を含む Tenant を作成する

Terraform を使って Cisco ACI に「L3out を作成する」サンプルをメモしておきます。

構成

Terraform で以下の構成を設定します。

file

Terraform の設定ファイル

Terraform の設定ファイルは以下の通りです。

  1. variables.tf
  2. main.tf
  3. aci.tf

variables.tf

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
variable "aci_url" { default = "https://10.0.0.1" }
variable "aci_username" { default = "admin" }
variable "aci_password" { default = "password" }
variable "tenant" { default = "Tenant1" }
variable "vrf" { default = "VRF" }
variable "contract1" { default = "CT" }

variable "l3dom" { default = "ExtRoutedDom" }
variable "l3out1" { default = "L3out" }
variable "lnode1_tdn" { default = "topology/pod-1/node-201" }
variable "lnode1_rtr_id" { default = "10.0.254.201" }
variable "lnode1_rtr_id_loop_back" { default = "no" }
variable "l3out1_att_addr" { default = "10.0.100.254/24" }
variable "l3out1_att_autostate" { default = "enabled" }
variable "l3out1_att_encap" { default = "vlan-100" }
variable "l3out1_att_ifInstT" { default = "ext-svi" }
variable "l3out1_att_mtu" { default = "1500" }
variable "l3out1_att_tDn" { default = "topology/pod-1/paths-201/pathep-[eth1/1]" }

variable "bd1" { default = "VL101_BD" }
variable "bd1_subnet" { default = "10.0.101.254/24" }
variable "ap1" { default = "AP" }
variable "physdom" { default = "PhysDom" }
variable "epg1" { default = "VL101_EPG" }
variable "egp1_port1_tdn" { default = "topology/pod-1/paths-201/pathep-[eth1/2]" }
variable "egp1_port1_vlan" { default = "vlan-101" }
variable "epg1_contract1_type" { default = "provider" }

main.tf

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
terraform {
  required_providers {
    aci = {
      source  = "CiscoDevNet/aci"
      version = "0.5.4"
    }
  }
}

provider "aci" {
  username = var.aci_username
  password = var.aci_password
  url      = var.aci_url
  insecure = true
}

aci.tf

現時点の Terraform には Logical Interface を定義する Resource が無い為、aci_rest Resource を使って REST API を呼び出します。 aci_rest を使うと Terraform が依存関係を自動解決出来なくなってしまう為、aci_restdepends_on で明示的に依存関係を定義しています。 また、Terraform で作成した Tenant を手動で作成すると terraform.tfstate が意図しない内容で残ってしまうのか、それ以降 aci_rest が上手く実行されなくなります。 こういった場合は「Tenant は terraform destroy で削除する」か、または「terraform.tfstate を手動で削除する」ことで回避出来るようです。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# Tenant
resource "aci_tenant" "tenant" {
  name = var.tenant
}

# VRF
resource "aci_vrf" "vrf" {
  tenant_dn = aci_tenant.tenant.id
  name      = var.vrf
}

# Contract / Subject / Filter
resource "aci_filter" "any" {
    tenant_dn = aci_tenant.tenant.id
    name      = "any_Filt"
}

resource "aci_filter_entry" "entry1" {
    name        = "0010"
    filter_dn   = aci_filter.any.id
    ether_t     = "unspecified"
}

resource "aci_contract" "contract1" {
    tenant_dn = aci_tenant.tenant.id
    name      = var.contract1
}

resource "aci_contract_subject" "subject1" {
    contract_dn                  = aci_contract.contract1.id
    name                         = "${var.contract1}_Subj"
    relation_vz_rs_subj_filt_att = [aci_filter.any.id]
}

# Domain
data "aci_l3_domain_profile" "l3dom" {
  name = var.l3dom
}

# L3Out
resource "aci_l3_outside" "l3out1" {
  tenant_dn                    = aci_tenant.tenant.id
  name                         = var.l3out1
  relation_l3ext_rs_ectx       = aci_vrf.vrf.id
  relation_l3ext_rs_l3_dom_att = data.aci_l3_domain_profile.l3dom.id
}

resource "aci_logical_node_profile" "lnprof1" {
  l3_outside_dn = aci_l3_outside.l3out1.id
  name          = "${var.l3out1}_NodeProf"
}

resource "aci_logical_node_to_fabric_node" "lnode1" {
  logical_node_profile_dn  = aci_logical_node_profile.lnprof1.id
  tdn                      = var.lnode1_tdn
  rtr_id                   = var.lnode1_rtr_id
  rtr_id_loop_back         = var.lnode1_rtr_id_loop_back
}

resource "aci_logical_interface_profile" "lifprof1" {
  logical_node_profile_dn = aci_logical_node_profile.lnprof1.id
  name                    = "${var.l3out1}_IntProf"
  relation_l3ext_rs_path_l3_out_att = toset([var.l3out1_att_tDn])
}

resource "aci_rest" "l3out1_att" {
  path       = "/api/mo/uni/tn-${var.tenant}/out-${aci_l3_outside.l3out1.name}/lnodep-${aci_logical_node_profile.lnprof1.name}/lifp-${aci_logical_interface_profile.lifprof1.name}.json"
  class_name = "l3extRsPathL3OutAtt"
  content = {
    "addr"      = var.l3out1_att_addr
    "autostate" = var.l3out1_att_autostate
    "encap"     = var.l3out1_att_encap
    "ifInstT"   = var.l3out1_att_ifInstT
    "mtu"       = var.l3out1_att_mtu
    "tDn"       = var.l3out1_att_tDn
  }
  depends_on = [
    aci_logical_interface_profile.lifprof1,
  ]
}

# L3Out1 External EPG
resource "aci_external_network_instance_profile" "l3out1_epg1" {
  l3_outside_dn       = aci_l3_outside.l3out1.id
  name                = "${var.l3out1}_L3PEG"
  relation_fv_rs_cons = [aci_contract.contract1.id]
}

resource "aci_l3_ext_subnet" "l3out1_subnet1" {
  external_network_instance_profile_dn = aci_external_network_instance_profile.l3out1_epg1.id
  ip                                   = "0.0.0.0/0"
  scope                                = ["import-security"]
}

# BD1
resource "aci_bridge_domain" "bd1" {
  tenant_dn          = aci_tenant.tenant.id
  name               = var.bd1
  relation_fv_rs_ctx = aci_vrf.vrf.id
}

resource "aci_subnet" "bd1_subnet" {
  parent_dn = aci_bridge_domain.bd1.id
  ip        = var.bd1_subnet
}

# Application Profile
resource "aci_application_profile" "ap1" {
  tenant_dn = aci_tenant.tenant.id
  name      = var.ap1
}

# Domain
data "aci_physical_domain" "physdom" {
  name = var.physdom
}

# EPG1
resource "aci_application_epg" "epg1" {
  application_profile_dn = aci_application_profile.ap1.id
  name                   = var.epg1
  relation_fv_rs_bd      = aci_bridge_domain.bd1.id
}

resource "aci_epg_to_domain" "epg1_physdom" {
  application_epg_dn = aci_application_epg.epg1.id
  tdn                = data.aci_physical_domain.physdom.id
}

resource "aci_epg_to_static_path" "egp1_port1" {
  application_epg_dn = aci_application_epg.epg1.id
  tdn                = var.egp1_port1_tdn
  encap              = var.egp1_port1_vlan
}

resource "aci_epg_to_contract" "epg1_contract1" {
    application_epg_dn = aci_application_epg.epg1.id
    contract_dn        = aci_contract.contract1.id
    contract_type      = var.epg1_contract1_type
}