Simple Example
This section provides an easy to understand, simple example of deploying a tenant to a new or existing Application Centric Infrastructure (ACI) deployment. This helps to get familiar using Nexus-as-Code to manage ACI. Because Terraform assumes full control of the lifecycle of the resources it creates, this example only contains the tenant module and definitions. That way any existing ACI configuration will not be impacted.
The repository used in this example can be found at: https://github.com/netascode/nac-aci-simple-example
This example assumes you have installed the following prerequisites.
- Terraform 1.3.0 or later.
- Git
- Your preferred text editor / Integrated Development Environment (IDE) (such as VisualStudio Code)
Getting started
Start by cloning the repository to your local system:
git clone https://github.com/netascode/nac-aci-simple-example.git
Open the folder in your preferred editor / IDE.
The working area should look like this (the editor shown here is vsCode):
As explained in the introduction, the main.tf
file contain the required providers, provider settings and points to the location of all *.yaml file definitions. This simple example has manage_tenants
set to true
meaning this will only manage tenants and its children. The other configuration areas have been set to false
in this example.
module "aci" {
source = "netascode/nac-aci/aci"
version = "0.7.0"
yaml_directories = ["data"]
manage_access_policies = false
manage_fabric_policies = false
manage_pod_policies = false
manage_node_policies = false
manage_interface_policies = false
manage_tenants = true
}
Note that if you do not have an ACI environment available you can leverage the DevNet Sandbox environment. For this scenario is it is recommended to use the always-on sandbox. Please refer to the Sandbox section in the menu.
Tenant definition
An example tenant definition is provided in data/tenant_DEV.yaml
. This is where the data (variable definition) is abstracted from the logic (infrastructure declaration).
Before diving into this file it is worth to understand the *.yaml
files and structure in the data
folder a bit better. As different teams might be responsible for different parts of the infrastructure, these definitions need to be flexible. Some organisations might want to use a single *.yaml
file for their entire fabric, whilst others prefer *.yaml
file per Virtual routing and Forwarding (VRF). The name of the yaml
files are completely arbitrary. These two examples are explained below.
It is possible to group multiple tenants in the same *.yaml
file. This yaml
file contains two tenants DEV
and TEST
.
cat tenants.yaml
---
apic:
tenants:
- name: DEV
- name: TEST
Similarly it is possible to split the configuration for a single tenant into multiple *.yaml
files. The two yaml
files refer to the same tenant DEV
but contain different VRFs DEV.VRF-1
and DEV.VRF-2
.
cat tenant_DEV_vrf1.yaml
---
apic:
tenants:
- name: DEV
vrfs:
- name: DEV.VRF-1
cat tenant_DEV_vrf2.yaml
---
apic:
tenants:
- name: DEV
vrfs:
- name: DEV.VRF-2
Now that you are familiar with the structure it is time to get into the example.
Step 1: Tenant Validation
This is where the exciting bit starts. Navigate to data/tenant_DEV.yaml
and take note of the structure. This example contains a single tenant named DEV
, a Virtual Routing and Forwarding (VRF), a few Bridge Domains (BDs), and an Application Profile (APs) with Endpoint Groups (EPGs).
Note that if you already have a tenant named
DEV
in your ACI infrastructure, you must alter the tenant name intenant_DEV.yaml
to avoid overlap.
---
apic:
tenants:
- name: DEV #Make sure there is no existing tenant named DEV
vrfs:
- name: DEV.DEV-VRF
bridge_domains:
- name: 10.1.200.0_24
vrf: DEV.DEV-VRF
subnets:
- ip: 10.1.200.1/24
- name: 10.1.201.0_24
vrf: DEV.DEV-VRF
subnets:
- ip: 10.1.201.1/24
- name: 10.1.202.0_24
vrf: DEV.DEV-VRF
subnets:
- ip: 10.1.202.1/24
application_profiles:
- name: VLANS
endpoint_groups:
- name: VLAN200
bridge_domain: 10.1.200.0_24
- name: VLAN201
bridge_domain: 10.1.201.0_24
- name: VLAN202
bridge_domain: 10.1.202.0_24
Note that it is possible to alter the content in
tenants_DEV.yaml
with your own values. Making changes to the configuration is discussed in a later step.
Step 2: Tenant Deployment
Make sure to update provider block in the main.tf
file in the root folder with the right credentials and APIC IP address. It is recommended to use signature based authentication. For more information see: Terraform Provider Documentation. The reason signature based authentication is preferred is due to APIC rate limiting requests from username and password-based authentication.
provider "aci" {
username = "username"
password = "password"
url = "https://apic.url"
}
Initialise Terraform: (This initializes the working directory containing the terraform configuration files, and installs all required providers and modules. This could take several minutes depending on the available bandwidth, as all the Nexus-as-Code modules are downloaded automatically.)
terraform init
Run terraform apply
: (This applies the changes defined by your Terraform configuration to create, update or destroy resources)
terraform apply
Followed by yes
to approve.
Upon success you should receive the following output:
Apply complete! Resources: 20 added, 0 changed, 0 destroyed.
Note that
terraform plan
has been omitted as. If you want to preview the changes, you could use this function.
Navigate to your APIC and verify that your new tenant DEV
has been deployed successfully:
Before expanding on additional use-cases it is worth understanding how you can add and remove resources. This will be covered in step 3 and 4.
Step 3: Adding additional resources
Now that the new tenant is deployed it becomes easy to add additional resources to the Tenant. Add a new Bridge Domain definition by adding the following lines to the bridge_domains
section in tenant_DEV.yaml
:
- name: 10.1.203.0_24
vrf: DEV.DEV-VRF
subnets:
- ip: 10.1.203.1/24
Note that indentation is important here. Make sure the indentation matches with the other Bridge Domains. You can verify whether your
yaml
is valid by doing ayaml lint
at YAML Lint. Simply copy and paste the content of your*.yaml
file and clickgo
.
The tenant_DEV.yaml
file with the new Bridge Domain should look like this:
---
apic:
tenants:
- name: DEV
vrfs:
- name: DEV.DEV-VRF
bridge_domains:
- name: 10.1.200.0_24
vrf: DEV.DEV-VRF
subnets:
- ip: 10.1.200.1/24
- name: 10.1.201.0_24
vrf: DEV.DEV-VRF
subnets:
- ip: 10.1.201.1/24
- name: 10.1.202.0_24
vrf: DEV.DEV-VRF
subnets:
- ip: 10.1.202.1/24
- name: 10.1.203.0_24
vrf: DEV.DEV-VRF
subnets:
- ip: 10.1.203.1/24
application_profiles:
- name: VLANS
endpoint_groups:
- name: VLAN200
bridge_domain: 10.1.200.0_24
- name: VLAN201
bridge_domain: 10.1.201.0_24
- name: VLAN202
bridge_domain: 10.1.202.0_24
Save the file and run terraform apply
terraform apply
Terraform will compare the state file with the new plan and calculate any resources that need to be added, changed or destroyed. In this case 3 new resources will be created. Terraform will create a new Bridge Domain, Subnet and link it to the existing VRF while the existing resources will not be impacted, meaning if you re execute the existing configuration stays as it is.
Output:
Plan: 3 to add, 0 to change, 0 to destroy.
Followed by yes
to approve.
Navigate to APIC and make sure that the new Bridge Domain 10.1.203.0_24
was added to tenant DEV
Step 4: Removing resources
In this step you will remove Bridge Domain 10.1.203.0_24
, which was created in step 4.
Remove the Bridge Domain 10.1.203.0_24
section from tenant_DEV.yaml
- name: 10.1.203.0_24
vrf: DEV.DEV-VRF
subnets:
- ip: 10.1.203.1/24
The tenant_DEV.yaml
file should once again look like this:
---
apic:
tenants:
- name: DEV
vrfs:
- name: DEV.DEV-VRF
bridge_domains:
- name: 10.1.200.0_24
vrf: DEV.DEV-VRF
subnets:
- ip: 10.1.200.1/24
- name: 10.1.201.0_24
vrf: DEV.DEV-VRF
subnets:
- ip: 10.1.201.1/24
- name: 10.1.202.0_24
vrf: DEV.DEV-VRF
subnets:
- ip: 10.1.202.1/24
application_profiles:
- name: VLANS
endpoint_groups:
- name: VLAN200
bridge_domain: 10.1.200.0_24
- name: VLAN201
bridge_domain: 10.1.201.0_24
- name: VLAN202
bridge_domain: 10.1.202.0_24
Save the file and run terraform apply
:
terraform apply
Terraform will compare the state file with the new plan and calculate any resources that need to be added, changed or destroyed. In this case 3 resources will be destroyed.
Output:
Plan: 0 to add, 0 to change, 3 to destroy.
Followed by yes
to approve.
Navigate to APIC and make sure that the Bridge Domain 10.1.203.0_24
was removed from tenant DEV
Step 5: Making changes
You have now added and removed resources, but what if you want to deploy additional configuration settings? For example; how would you enable host route advertisement on a bridge domain
? Which attributes can you use, and which values are expected? This is where the Datamodel Docs come in. Following the structure of the six different configuration areas, this section contains a list of all the resources that are currently supported. Under each resource you can find a list of attributes, their type, any constraints, whether they are required and whether they have a default value. It also contains a short and comprehensive example of that resource.
If you navigate to Datamodel Docs
> Tenants
> Bridge Domain
, you can see that for advertise_host_routes
the allowed values are true
, false
. For this particular resource, it is not mandatory and the default value is false
. If you wish to change this setting for all Bridge Domains that do not have this attribute specified explicitly, you can update advertise_host_routes
value in defaults.yaml
. This however would change this setting for all Bridge Domains that do not have a setting for advertise_host_routes
configured explicitly.
Update the tenant_DEV.yaml
file by adding the line advertise_host_routes: true
under Bridge Domain 10.1.202.0_24
.
advertise_host_routes: true
The section for 10.1.202.0_24
should look like this:
- name: 10.1.202.0_24
vrf: DEV.DEV-VRF
advertise_host_routes: true
subnets:
- ip: 10.1.202.1/24
Save the file and run terraform apply
:
terraform apply
Terraform will compare the existing state file with the new plan and calculate any resources that need to be added, changed or destroyed. In this case 1 resource will be changed. The resource will be updated in-place.
Output:
Terraform will perform the following actions:
# module.aci.module.aci_bridge_domain["DEV/10.1.202.0_24"].aci_rest_managed.fvBD will be updated in-place
~ resource "aci_rest_managed" "fvBD" {
~ content = {
~ "hostBasedRouting" = "no" -> "yes"
# (27 unchanged elements hidden)
}
id = "uni/tn-DEV/BD-10.1.202.0_24"
# (2 unchanged attributes hidden)
}
Plan: 0 to add, 1 to change, 0 to destroy.
Followed by yes
to approve.
This will update the existing Bridge Domain with the new setting for host route advertisement. Navigate to APIC and make sure that the Bridge Domain 10.1.202.0_24
was updated with the new advertise_host_routes
configuration.
Step 6: Working with default values
The previous step explained how to change a setting on a single resource. If you have specific configuration you wish to apply to each instance of that resource you can leverage a defaults.yaml
file. The main.tf
plan passes the content of defaults.yaml
to the module. This allows a user to modify any default settings to reflect their requirements. Navigate to defaults.yaml
and explore the file:
# Overwrite default values here
defaults:
# apic:
# tenants:
# bridge_domains:
# unicast_routing: false
The defaults.yaml
file follows the same structure as the other yaml
files in the inventory. Uncomment the lines and run terraform apply
. The output will show that the terraform apply action will update the bridge domains with its new configuration setting.
</snip output for subnet 200, 201>
# module.aci.module.aci_bridge_domain["DEV/10.1.202.0_24"].aci_rest_managed.fvBD will be updated in-place
~ resource "aci_rest_managed" "fvBD" {
~ content = {
~ "unicastRoute" = "yes" -> "no"
# (27 unchanged elements hidden)
}
id = "uni/tn-DEV/BD-10.1.202.0_24"
# (2 unchanged attributes hidden)
}
Plan: 0 to add, 3 to change, 0 to destroy.
Step 7: Adding contracts
The next objective is to create a new type of resource. In this step a new contract will be added, allowing HTTP
and HTTPS
traffic between VLAN200
and VLAN201
. It is encouraged to make use of the Datamodel Docs. By navigating to Datamodel Docs
> Tenants
> Contract
, and Filters
you can find how to add the contract and filter definition to the tenant_DEV.yaml
file. Also make sure to reference Datamodel Docs
> Tenants
> Endpoint Group
to understand how to consume and provide the contract. As a reminder, simple and full examples are provided under each resource.
Your code might look slightly different based your naming compared to the final code below. This can be used as a reference for a good practice for configuring contracts, subjects and filters.
The final tenant_DEV.yaml
file should look like this:
---
apic:
tenants:
- name: DEV
vrfs:
- name: DEV.DEV-VRF
bridge_domains:
- name: 10.1.200.0_24
vrf: DEV.DEV-VRF
subnets:
- ip: 10.1.200.1/24
- name: 10.1.201.0_24
vrf: DEV.DEV-VRF
subnets:
- ip: 10.1.201.1/24
- name: 10.1.202.0_24
vrf: DEV.DEV-VRF
advertise_host_routes: true
subnets:
- ip: 10.1.202.1/24
application_profiles:
- name: VLANS
endpoint_groups:
- name: VLAN200
bridge_domain: 10.1.200.0_24
contracts:
consumers:
- PERMIT-TO-VLAN201
- name: VLAN201
bridge_domain: 10.1.201.0_24
contracts:
providers:
- PERMIT-TO-VLAN201
- name: VLAN202
bridge_domain: 10.1.202.0_24
filters:
- name: TCP-SRC-ANY-TO-DST-80
entries:
- name: SRC-ANY-TO-DST-80
destination_from_port: 80
destination_to_port: 80
- name: TCP-SRC-ANY-TO-DST-443
entries:
- name: SRC-ANY-TO-DST-443
destination_from_port: 443
destination_to_port: 443
contracts:
- name: PERMIT-TO-VLAN201
subjects:
- name: TCP-ENTRIES
filters:
- filter: TCP-SRC-ANY-TO-DST-80
- filter: TCP-SRC-ANY-TO-DST-443
Save the file and run Terraform Apply
terraform apply
Output:
Plan: 10 to add, 0 to change, 0 to destroy.
Followed by yes
to approve.
Navigate to APIC and make sure that the contract is now applied between VLAN200
and VLAN201
Step 8: Changing configuration in the GUI
Imagine that someone unaware of the automation efforts makes a change directly in the GUI to one of the objects created by Terraform.
Delete the contract between EPGs VLAN200
and VLAN201
via the GUI:
Because the Terraform state now differs from the running configuration a simple terraform apply
will prompt you to recreate the contract. Terraform is able to detect configuration drift and reconcile it.
</snip>
Plan: 4 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.
This even works when changing properties of an object. The resource will be modified back to its desired state when terraform apply
is executed. Feel free to make modifications via the GUI and restore those manual actions.
Step 9: Cleaning up
That is it for this simple example. Feel free to experiment with additional resources. For a more comprehensive example you can navigate to the comprehensive example. The final step is to clean up the configuration.
Run terraform destroy
to remove the configuration:
terraform destroy
Output:
Plan: 0 to add, 0 to change, 30 to destroy.
Followed by yes
to approve.
Navigate to APIC and make sure that tenant DEV
has been removed.