Configure ACI with Terraform

By | 27/11/2019

Introduction

In this post, we created some EC2 instances on AWS. In this post, we will apply the same principle but instead of creating some servers on AWS, we will create a network on Cisco’s ACI solution using Terraform’s ACI provider. If you want to learn more on ACI, read this and this.

Terraform code

We will create a provider.tf file which contains nothing more than the ACI configuration. The URL is the URL for your APIC. I will take the fast route and put insecure to true, but you can also use certificate. It’s all in the documentation.

provider "aci" {
  username = "admin"
  password = "***"
  url      = "https://10.x.y.1"
  insecure = true
}

resource "aci_tenant" "Tenant_TF_Demo" {
  name        = var.aci_tenant
  description = "Tenant created by TF"
}

resource "aci_vrf" "VRF_TF_Demo" {
  tenant_dn = aci_tenant.Tenant_TF_Demo.id
  name      = var.aci_vrf
}

resource "aci_bridge_domain" "BD_TF_Demo" {
  tenant_dn          = aci_tenant.Tenant_TF_Demo.id
  name               = var.aci_bd"
  description        = "BD created by TF"
  relation_fv_rs_ctx = aci_vrf.VRF_TF_Demo.name
}

I will create a new tenant, a VRF under that tenant and a BD under that VRF. For this blogpost I will limit myself to these three network constructs just to give you an idea of how things work. In a later blog post (when I have more time), I will create a more elaborate infrastructure.

We will be reading the information from a Terraform variable file.

variable "aci_tenant" {
  default = "Tenant_Terraform_Demo"
}

variable "aci_vrf" {
  default = "VRF_Terraform_Demo"
}

variable "aci_bd" {
  default = "BD_Terraform_Demo"
}

Note: since Terraform 0.12, there is no need for putting quotes around the types. Terraform will give you a warning as follows:

Terraform 0.11 and earlier required type constraints to be given in quotes,
but that form is now deprecated and will be removed in a future version of
Terraform. To silence this warning, remove the quotes around “string”.

The easiest way to transform all you Terraform 0.11 files to version 0.12 is to execute the following:

cisco@wauterw-ubuntu-desktop:~/software/Terraform/ACI_Tenant_VRF_BD$ terraform 0.12upgrade

This command will rewrite the configuration files in the given directory so
that they use the new syntax features from Terraform v0.12, and will identify
any constructs that may need to be adjusted for correct operation with
Terraform v0.12.

We recommend using this command in a clean version control work tree, so that
you can easily see the proposed changes as a diff against the latest commit.
If you have uncommited changes already present, we recommend aborting this
command and dealing with them before running this command again.

Would you like to upgrade the module in the current directory?
  Only 'yes' will be accepted to confirm.

  Enter a value: yes

-----------------------------------------------------------------------------

Upgrade complete!

The configuration files were upgraded successfully. Use your version control
system to review the proposed changes, make any necessary adjustments, and
then commit.

Deploy infrastructure

As we did with AWS resources (see here), we will follow exactly the same pattern.

First we will perform ‘terraform init’. This will essentially download the ACI provider from Terraform repo.

cisco@wauterw-main:~/wim/terraform/ACI$ terraform init

Initializing the backend...

Successfully configured the backend "s3"! Terraform will automatically
use this backend unless the backend configuration changes.

Initializing provider plugins...

The following providers do not have any version constraints in configuration,
so the latest version was installed.

To prevent automatic upgrades to new major versions that may contain breaking
changes, it is recommended to add version = "..." constraints to the
corresponding provider blocks in configuration, with the constraint strings
suggested below.

* provider.aci: version = "~> 0.1"

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

You see in above snippet, it’s configuring the S3 backend (I chose to use a shared tfstate file) and it downloads the ACI provider. Next, we’ll perform a ‘terraform plan’ to see what will be created.

cisco@wauterw-ubuntu-desktop:~/software/Terraform/ACI_Tenant_VRF_BD$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

aci_tenant.Tenant_TF_Demo: Refreshing state... [id=uni/tn-Tenant_Terraform_Demo]
aci_vrf.VRF_TF_Demo: Refreshing state... [id=uni/tn-Tenant_Terraform_Demo/ctx-VRF_Terraform_Demo]

------------------------------------------------------------------------

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aci_bridge_domain.BD_TF_Demo will be created
  + resource "aci_bridge_domain" "BD_TF_Demo" {
      + annotation                  = (known after apply)
      + arp_flood                   = (known after apply)
      + bridge_domain_type          = (known after apply)
      + description                 = "BD created by TF"
      + ep_clear                    = (known after apply)
      + ep_move_detect_mode         = (known after apply)
      + host_based_routing          = (known after apply)
      + id                          = (known after apply)
      + intersite_bum_traffic_allow = (known after apply)
      + intersite_l2_stretch        = (known after apply)
      + ip_learning                 = (known after apply)
      + ipv6_mcast_allow            = (known after apply)
      + limit_ip_learn_to_subnets   = (known after apply)
      + ll_addr                     = (known after apply)
      + mac                         = (known after apply)
      + mcast_allow                 = (known after apply)
      + multi_dst_pkt_act           = (known after apply)
      + name                        = "BD_Terraform_Demo"
      + name_alias                  = (known after apply)
      + optimize_wan_bandwidth      = (known after apply)
      + relation_fv_rs_ctx          = "VRF_Terraform_Demo"
      + tenant_dn                   = "uni/tn-Tenant_Terraform_Demo"
      + unicast_route               = (known after apply)
      + unk_mac_ucast_act           = (known after apply)
      + unk_mcast_act               = (known after apply)
      + v6unk_mcast_act             = (known after apply)
      + vmac                        = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

------------------------------------------------------------------------

Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.

Next, let’s apply the configuration. You should know the drill by now 🙂

cisco@wauterw-ubuntu-desktop:~/software/Terraform/ACI_Tenant_VRF_BD$ terraform apply

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aci_bridge_domain.BD_TF_Demo will be created
  + resource "aci_bridge_domain" "BD_TF_Demo" {
      + annotation                  = (known after apply)
      + arp_flood                   = (known after apply)
      + bridge_domain_type          = (known after apply)
      + description                 = "BD created by TF"
      + ep_clear                    = (known after apply)
      + ep_move_detect_mode         = (known after apply)
      + host_based_routing          = (known after apply)
      + id                          = (known after apply)
      + intersite_bum_traffic_allow = (known after apply)
      + intersite_l2_stretch        = (known after apply)
      + ip_learning                 = (known after apply)
      + ipv6_mcast_allow            = (known after apply)
      + limit_ip_learn_to_subnets   = (known after apply)
      + ll_addr                     = (known after apply)
      + mac                         = (known after apply)
      + mcast_allow                 = (known after apply)
      + multi_dst_pkt_act           = (known after apply)
      + name                        = "BD_Terraform_Demo"
      + name_alias                  = (known after apply)
      + optimize_wan_bandwidth      = (known after apply)
      + relation_fv_rs_ctx          = "VRF_Terraform_Demo"
      + tenant_dn                   = (known after apply)
      + unicast_route               = (known after apply)
      + unk_mac_ucast_act           = (known after apply)
      + unk_mcast_act               = (known after apply)
      + v6unk_mcast_act             = (known after apply)
      + vmac                        = (known after apply)
    }

  # aci_tenant.Tenant_TF_Demo will be created
  + resource "aci_tenant" "Tenant_TF_Demo" {
      + annotation  = (known after apply)
      + description = "Tenant created by TF"
      + id          = (known after apply)
      + name        = "Tenant_Terraform_Demo"
      + name_alias  = (known after apply)
    }

  # aci_vrf.VRF_TF_Demo will be created
  + resource "aci_vrf" "VRF_TF_Demo" {
      + annotation             = (known after apply)
      + bd_enforced_enable     = (known after apply)
      + id                     = (known after apply)
      + ip_data_plane_learning = (known after apply)
      + knw_mcast_act          = (known after apply)
      + name                   = "VRF_Terraform_Demo"
      + name_alias             = (known after apply)
      + pc_enf_dir             = (known after apply)
      + pc_enf_pref            = (known after apply)
      + tenant_dn              = (known after apply)
    }

Plan: 3 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aci_tenant.Tenant_TF_Demo: Creating...
aci_tenant.Tenant_TF_Demo: Creation complete after 1s [id=uni/tn-Tenant_Terraform_Demo]
aci_vrf.VRF_TF_Demo: Creating...
aci_vrf.VRF_TF_Demo: Creation complete after 1s [id=uni/tn-Tenant_Terraform_Demo/ctx-VRF_Terraform_Demo]
aci_bridge_domain.BD_TF_Demo: Creating...
aci_bridge_domain.BD_TF_Demo: Creation complete after 1s [id=uni/tn-Tenant_Terraform_Demo/BD-BD_Terraform_Demo]

Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

Log into ACI and see that the tenant is created there:

Also the VRF and the BD are created as you can see in below screenshot:

Pretty neat stuff.

Let’s continue to delete what we created by issuing a ‘terraform destroy’.

cisco@wauterw-ubuntu-desktop:~/software/Terraform/ACI_Tenant_VRF_BD$ terraform destroy
aci_tenant.Tenant_TF_Demo: Refreshing state... [id=uni/tn-Tenant_Terraform_Demo]
aci_vrf.VRF_TF_Demo: Refreshing state... [id=uni/tn-Tenant_Terraform_Demo/ctx-VRF_Terraform_Demo]
aci_bridge_domain.BD_TF_Demo: Refreshing state... [id=uni/tn-Tenant_Terraform_Demo/BD-BD_Terraform_Demo]

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  - destroy

Terraform will perform the following actions:

  # aci_bridge_domain.BD_TF_Demo will be destroyed
  - resource "aci_bridge_domain" "BD_TF_Demo" {
      - arp_flood                   = "no" -> null
      - bridge_domain_type          = "regular" -> null
      - description                 = "BD created by TF" -> null
      - ep_clear                    = "no" -> null
      - host_based_routing          = "no" -> null
      - id                          = "uni/tn-Tenant_Terraform_Demo/BD-BD_Terraform_Demo" -> null
      - intersite_bum_traffic_allow = "no" -> null
      - intersite_l2_stretch        = "no" -> null
      - ip_learning                 = "yes" -> null
      - limit_ip_learn_to_subnets   = "yes" -> null
      - ll_addr                     = "::" -> null
      - mac                         = "00:22:BD:F8:19:FF" -> null
      - mcast_allow                 = "no" -> null
      - multi_dst_pkt_act           = "bd-flood" -> null
      - name                        = "BD_Terraform_Demo" -> null
      - optimize_wan_bandwidth      = "no" -> null
      - relation_fv_rs_ctx          = "VRF_Terraform_Demo" -> null
      - tenant_dn                   = "uni/tn-Tenant_Terraform_Demo" -> null
      - unicast_route               = "yes" -> null
      - unk_mac_ucast_act           = "proxy" -> null
      - unk_mcast_act               = "flood" -> null
      - vmac                        = "not-applicable" -> null
    }

  # aci_tenant.Tenant_TF_Demo will be destroyed
  - resource "aci_tenant" "Tenant_TF_Demo" {
      - description = "Tenant created by TF" -> null
      - id          = "uni/tn-Tenant_Terraform_Demo" -> null
      - name        = "Tenant_Terraform_Demo" -> null
    }

  # aci_vrf.VRF_TF_Demo will be destroyed
  - resource "aci_vrf" "VRF_TF_Demo" {
      - bd_enforced_enable     = "no" -> null
      - id                     = "uni/tn-Tenant_Terraform_Demo/ctx-VRF_Terraform_Demo" -> null
      - ip_data_plane_learning = "enabled" -> null
      - knw_mcast_act          = "permit" -> null
      - name                   = "VRF_Terraform_Demo" -> null
      - pc_enf_dir             = "ingress" -> null
      - pc_enf_pref            = "enforced" -> null
      - tenant_dn              = "uni/tn-Tenant_Terraform_Demo" -> null
    }

Plan: 0 to add, 0 to change, 3 to destroy.

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

aci_bridge_domain.BD_TF_Demo: Destroying... [id=uni/tn-Tenant_Terraform_Demo/BD-BD_Terraform_Demo]
aci_bridge_domain.BD_TF_Demo: Destruction complete after 0s
aci_vrf.VRF_TF_Demo: Destroying... [id=uni/tn-Tenant_Terraform_Demo/ctx-VRF_Terraform_Demo]
aci_vrf.VRF_TF_Demo: Destruction complete after 0s
aci_tenant.Tenant_TF_Demo: Destroying... [id=uni/tn-Tenant_Terraform_Demo]
aci_tenant.Tenant_TF_Demo: Destruction complete after 0s

Destroy complete! Resources: 3 destroyed.

As you can see, the tenant and all underlying objects have been destroyed.

Hope you had some fun, next blog post we’ll be integrating this in a CI/CD pipeline.

Ciao!

Leave a Reply

Your email address will not be published. Required fields are marked *