1. Introduction to Terraform
2. Terraform and ACI
3. Explanation of the Terraform configuration files
4. Terraform Remote State and Team Collaboration
5. Terraform Providers – How are they built?
You may already have your own ACI lab to follow along with however if you don’t you might want to use the ACI Simulator in the DevNet Sandbox.
As explained in the previous post, a Terraform provider is responsible for understanding API interactions and exposing resources.
A Terraform resource describes one or more infrastructure objects, for example in an ACI Tenant, EPG, Contract, BD.
This post will cover the ACI Terraform Provider which includes a large number of resources.
The full list of available resources can be found from the following link.
Terraform Resource vs Data Sources
Until now we’ve only looked at the provider resource, for example “aci_tenant”.
resource "aci_tenant" "my_terraform_tenant" {
name = "tenant_for_terraform"
description = "This tenant is created by the Terraform ACI provider"
}
Terraform also includes a concept known as data sources.
Data sources allow a Terraform configuration to make use of information defined outside of Terraform, or defined by another separate Terraform configuration.
It’s important to note that while resources are read/write, data sources are read only. This means we can include information in our configuration file for objects that we may not manage.
For example in the case of ACI, perhaps we want to manage our own app profiles and EPGs in a shared tenant however don’t want Terraform to have any control of the tenant itself.
We can define the shared elements (tenant, BD, VRF, contracts etc) as data sources (read only), and the ANP/EPGs as resources which will be created and deleted by Terraform.
provider "aci" {
# cisco-aci user name
username = "${var.username}"
# cisco-aci password
password = "${var.password}"
# cisco-aci url
url = "${var.apic_url}"
insecure = true
}
data "aci_tenant" "my_shared_tenant" {
name = "my_shared_tenant"
}
data "aci_bridge_domain" "my_shared_bd" {
tenant_dn = "${data.aci_tenant. my_shared_tenant.id}"
name = "my_shared_bd"
}
resource "aci_application_profile" "terraform_app" {
tenant_dn = "${data.aci_tenant. my_shared_tenant.id}"
name = "demo_app_profile"
}
resource "aci_application_epg" "my_web_epg" {
application_profile_dn = "${aci_application_profile.terraform_app.id}"
name = "db_epg"
description = "%s"
annotation = "tag_epg"
exception_tag = "0"
flood_on_encap = "disabled"
fwd_ctrl = "none"
has_mcast_source = "no"
is_attr_based_e_pg = "no"
match_t = "AtleastOne"
name_alias = "alias_epg"
pc_enf_pref = "unenforced"
pref_gr_memb = "exclude"
prio = "unspecified"
shutdown = "no"
}
As you can see above we have defined two data sources (my_shared_tenant and my_shared_bd). These are then referenced in the aci_application_profile resource using the format, “${data.aci_tenant. my_shared_tenant.id}“.
Remember from the previous post that some properties such as IDs are computed behind the scenes without the need to hard code values.
NOTE: You’ll need to ensure that any data sources you’re referencing already exist in the ACI fabric. For example the bridge domain, “my_shared_bd”, already exists in the tenant, “my_shared_tenant” in our lab. If these data sources don’t already exists you will receive an error.
So using these two concepts we can build the desired configuration for our ACI fabric. Some Terraform ACI configuration has already been provided above and in the previous post. To help you get started the ACI Business Unit have created a large number of example configuration files which you can find from the following link.
Additionally, for any customer configuration you may want to create, the following document includes the entire list of available resources for the ACI provider.
These resources should give you a good start on your journey to managing ACI with Terraform.
But wait, there’s more! There are a couple of questions that are often asked in relation to the ACI provider.
◉ Is this only for greenfield deployments?
◉ Can I configure everything through Terraform?
◉ What happens if I manually configure ACI?
Importing With Terraform
ACI may already exist in many customer environments when they start to use Terraform. Alternatively, a customer new to ACI and Terraform may not want to learn both at the same time, choosing to first learn ACI and then migrate configuration to Terraform.
Luckily Terraform supports (for some providers) the importing of existing configuration to address these common scenarios.
terraform import
Remember there are two main files we’re working with, the configuration (.tf) and the state (terraform.tfstate) files.
Currently the “Terraform Import” command will only import what it learns about the existing infrastructure into the state (terraform.tfstate) file. It will not automatically append this into the configuration file.
This is a manual process you must complete.
Step 1 – Add the new resources to the configuration (.tf) file.
resource "aci_tenant" "myTenant" {
}
You only need to define the resource.
If you configure a property such as a name and then import from an existing resource, the values will be overwritten.
resource "aci_tenant" "myTenant" {
name = “myTenant1”
}
In this example if the ACI tenant is named “myTenant”, when first importing Terraform will use “myTenant” in the state file. The configuration file is not updated on an import and therefore “myTenant1” will not be changed. Later when you run the apply command, Terraform will update the ACI fabric with the new name, “myTenant1”
Step 2 – Run the import command
Terraform identifies ACI objects with their Distinguished Name (Dn) and the Terraform resource ID is the absolute path of ACI object in the DMIT.
For example, the ID of an ACI tenant, myTenant, is uni/tn-myTenant. The ID of an ACI VRF, vrf1, in myTenant is uni/tn-myTenant/ctx-vrf1
The import command is used as follows:
terraform import <resource name> <resource id>
e.g terraform import aci_tenant.myTenant uni/tn-myTenant
We added the aci_tenant.myTenant resource to the configuration file in Step 1. This command is now assigning an ID, the ACI Tenant Dn (uni/tn-myTenant), to the resource and will also import existing configuration.
Step 3 – Repeat for all required resources
This used the ACI tenant as an example however you may also need to import other resources such as bridge domains, VRFs, EPGs, contract. You would repeat the steps above for each of these resources. First add them all as resources and then run the import command referencing the name of the resource and the ACI Dn as ID.
ACI REST Resource
There are many properties of ACI that can be configured, however not all exist as Terraform resources in the ACI provider. For this reason the aci_rest resource was created and allows you to configure ACI Objects through the REST API. Any Model Object that is not supported by the provider can be created/managed using this resource.
As a result, anything that can be configured through the ACI REST API can be configured and managed by Terraform. Either through a native resource (e.g. aci_tenant), or using the API (aci_rest resource).
Here’s an example of creating an L3Out.
resource "aci_rest" "rest_l3_ext_out" {
path = "/api/node/mo/${aci_tenant.tenant_for_rest_example.id}/out-test_ext.json"
class_name = "l3extOut"
content = {
"name" = "test_ext"
}
}
These is the same configuration you would find in a Python script making raw calls to the ACI API, only this is wrapped in a Terraform resource.
Note as well that you can still reference existing variables or properties such as the aci_tenant id.
Config Drift
“What happens if someone manually configures a resource Terraform is managing?”
This is a common question not only for Terraform but anytime we are using external tools to manage infrastructure.
In the case of ACI we can test it out and see what happens.
Step 1 – First create a tenant, my_terraform_tenant, with the following description.
resource "aci_tenant" "my_terraform_tenant" {
name = "tenant_for_terraform"
description = "This tenant is created by the Terraform ACI provider"
}
Step 2 – Login to the GUI and under the Tenant -> Policy, update the description.