In the previous section, we successfully deployed a complete ACI fabric. But we leveraged a single plan where every resource was included in the file. What about if I want to break my code into sub-modules (functions) and have the ability to re-use the code. This is where Modules can play an important role in your application development.
From the Terraform documentation, "A module is a container for multiple resources that are used together. Modules can be used to create lightweight abstractions, so that you can describe your infrastructure in terms of its architecture, rather than directly in terms of physical objects."
In order to showcase the power of modules, we will be creating and deploying the following new objects:
It is very important during this step to follow the directions because we need to create the right directory structure. We will be creating 4 directories:
The first step will be to created some root level directories.
mkdir ~/terraform/mod_examples
Now that we have the top of the tree mod_example
created you need to create
the modules directory and the primary module that will be aci_tenant
mkdir ~/terraform/mod_examples/modules
mkdir ~/terraform/mod_examples/aci_tenant
Under the modules directory is where the three modules bd,tenant and vrf
will
be located
mkdir ~/terraform/mod_examples/modules/bd
mkdir ~/terraform/mod_examples/modules/tenant
mkdir ~/terraform/mod_examples/modules/vrf
This directory structure should be visible in the IDE GUI. To assist you with the files in this module, let's create all the files needed directly in the CLI so they are visible to you on the GUI immediately
touch ~/terraform/mod_examples/aci_tenant/main.tf
touch ~/terraform/mod_examples/modules/bd/bd.tf
touch ~/terraform/mod_examples/modules/bd/variables.tf
touch ~/terraform/mod_examples/modules/tenant/tenant.tf
touch ~/terraform/mod_examples/modules/tenant/variables.tf
touch ~/terraform/mod_examples/modules/vrf/vrf.tf
touch ~/terraform/mod_examples/modules/vrf/variables.tf
Under the terraform/mod_examples/aci_tenant, edit main.tf
. This
file is the file that will be invoking the Terraform> modules itself. Think of it as the
parent terraform file.
provider "aci" {
username = "admin"
password = "cisco.123"
url = "http://10.0.226.41"
insecure = true
}
module "my_tenant" {
source = "../modules/tenant"
tenant = "mod_pod03"
}
module "my_vrf" {
source = "../modules/vrf"
vrf = "vrf_pn_03"
tenant_id = module.my_tenant.tenant_id
}
module "my_bd_app" {
source = "../modules/bd"
bd = "pod03_app"
ip = "5.1.1.1/24"
tenant_id = module.my_tenant.tenant_id
vrf_id = module.my_vrf.vrf_id
}
module "my_bd_web" {
source = "../modules/bd"
bd = "pod03_web"
ip = "6.1.1.1/24"
tenant_id = module.my_tenant.tenant_id
vrf_id = module.my_vrf.vrf_id
}
Now you will be able to see the value of modules. Let's suppose that you have a standard set of configuration options that you want your Bridge Domains in ACI to contain across the board. Via Terraform it is possible to define all these specific values as you see fit to your needs and by invoking the module, it will create those configuration options consistently.
For this example there are some things that we want to keep consistent across these bridge domains unless specified otherwise. These will be:
Under the terraform/mod_examples/bd, modify bd.tf
>
resource "aci_bridge_domain" "bd" {
tenant_dn = var.tenant_id
arp_flood = var.arp_flood
unicast_route = var.unicast_route
unk_mac_ucast_act = var.unkunicast_route
relation_fv_rs_ctx = var.vrf_id
name = var.bd
}
resource "aci_subnet" "bd_subnet" {
parent_dn = aci_bridge_domain.bd.id
ip = var.ip
}
Now via the structure of the modules, if nothing is defined in the top requesting terraform file, it will assume the defaults that we are setting in the variable file.
Under the terraform/mod_examples/bd, modify variables.tf
. Here
you can see that we are creating the default values for how we want the
bridge domains to be built. You will notice in the top module we have not defined
these values. So terraform will take the default values and apply them.
This is an easy way to create consistency in how you want these bridge domains configured. You can
override these default values by defining in the top main.tf
file
the values that you would want. We will accomplish this below.
variable "tenant_id" {
default = ""
}
variable "vrf_id" {
default = ""
}
variable "bd" {
default = ""
}
variable "ip" {
default = ""
}
variable "arp_flood" {
default = "yes"
}
variable "unicast_route" {
default = "yes"
}
variable "unkunicast_route" {
default = "flood"
}
Under the terraform/mod_examples/tenant, modify tenant.tf
resource "aci_tenant" "tenant" {
name = var.tenant
}
output "tenant_id" {
value = "${aci_tenant.tenant.id}"
}
Under the terraform/mod_examples/tenant, create the following file variables.tf
variable "tenant" {
default = ""
}
variable "tenant_id" {
default = ""
}
Under the terraform/mod_examples/vrf, create the following file vrf.tf
resource "aci_vrf" "vrf" {
tenant_dn = var.tenant_id
name = var.vrf
}
output "vrf_id" {
value = "${aci_vrf.vrf.id}"
}
Under the terraform/mod_examples/vrf, create the following file variables.tf
variable "tenant_id" {
default = ""
}
variable "vrf" {
default = ""
}
variable "vrf_id" {
default = ""
}
Initialize the project, this process will download the necessary plugins which will allow Terraform to interact with vSphere.
cd ~/terraform/mod_examples/aci_tenant
terraform init
labuser@terra-vm-pod03:~/terraform/mod_examples/aci_tenant$ terraform init Initializing modules... - my_bd_app in ../modules/bd - my_bd_web in ../modules/bd - my_tenant in ../modules/tenant - my_vrf in ../modules/vrf Initializing the backend... Initializing provider plugins... - Checking for available provider plugins... - Downloading plugin for provider "aci" (terraform-providers/aci) 0.3.4... 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.3"
After successfully initialized Terraform, the next step is to execute the terraform plan and terraform apply.
terraform plan -out main.plan
terraform apply "main.plan"
labuser@terra-vm-pod03:~# terraform plan -out main.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. ------------------------------------------------------------------------ An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: + create
labuser@terra-vm-pod03:~# terraform apply "main.plan" module.my_tenant.aci_tenant.tenant: Creating... module.my_tenant.aci_tenant.tenant: Creation complete after 1s [id=uni/tn-mod_pod03] module.my_vrf.aci_vrf.vrf: Creating... module.my_vrf.aci_vrf.vrf: Creation complete after 3s [id=uni/tn-mod_pod03/ctx-pn_03]
Previously we showed you how to use default values in specific configurations so that you can keep consistency in how you want to configure objects. In this step we will make a modification to one of the bridge domains such that we tell Terraform that we want to deviate from our default for a specific configuration.
Back to the main.tf
terraform file that contains the calls for all the modules. You are
going to modify the file to a specific change. You are going to tell Terraform that you wish
to tell the bridge domain to not flood ARP's or unknown unicast. You are going to
enter these values for this bridge and Terraform knows now to override it's default
with the value entered.
provider "aci" {
username = "admin"
password = "cisco.123"
url = "http://10.0.226.41"
insecure = true
}
module "my_tenant" {
source = "../modules/tenant"
tenant = "mod_pod03"
}
module "my_vrf" {
source = "../modules/vrf"
vrf = "vrf_pn_03"
tenant_id = module.my_tenant.tenant_id
}
module "my_bd_app" {
source = "../modules/bd"
bd = "pod03_app"
ip = "5.1.1.1/24"
arp_flood = "no"
unkunicast_route = "proxy"
tenant_id = module.my_tenant.tenant_id
vrf_id = module.my_vrf.vrf_id
}
module "my_bd_web" {
source = "../modules/bd"
bd = "pod03_web"
ip = "6.1.1.1/24"
tenant_id = module.my_tenant.tenant_id
vrf_id = module.my_vrf.vrf_id
}
Now run the terraform plan command.
terraform plan -out main.plan
You will notice that Terraform now shows that it will do a modification:
labuser@terra-vm-pod03:~# terraform apply "main.plan" [CUT] ~ resource "aci_bridge_domain" "bd" { ~ arp_flood = "yes" -> "no" [CUT] ~ unk_mac_ucast_act = "flood" -> "proxy" } Plan: 0 to add, 1 to change, 0 to destroy.
Now you can execute the apply
command to push these new changes to ACI.
terraform apply "main.plan"
And in the fabric only the one Bridge Domain will see the modified policies applied. As you can see this can provide for great structured way to code the policies in the fabric with a default set of values that you would like to use and at the same time provide the capability to change these when needed.