Skip to main content

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.

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 in tenant_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 a yaml lint at YAML Lint. Simply copy and paste the content of your *.yaml file and click go.

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.