Notice: Function _load_textdomain_just_in_time was called incorrectly. Translation loading for the easy-accordion-free domain was triggered too early. This is usually an indicator for some code in the plugin or theme running too early. Translations should be loaded at the init action or later. Please see Debugging in WordPress for more information. (This message was added in version 6.7.0.) in /home/mother99/jacksonholdingcompany.com/wp-includes/functions.php on line 6114

Notice: Function _load_textdomain_just_in_time was called incorrectly. Translation loading for the zoho-flow domain was triggered too early. This is usually an indicator for some code in the plugin or theme running too early. Translations should be loaded at the init action or later. Please see Debugging in WordPress for more information. (This message was added in version 6.7.0.) in /home/mother99/jacksonholdingcompany.com/wp-includes/functions.php on line 6114

Notice: Function _load_textdomain_just_in_time was called incorrectly. Translation loading for the wordpress-seo domain was triggered too early. This is usually an indicator for some code in the plugin or theme running too early. Translations should be loaded at the init action or later. Please see Debugging in WordPress for more information. (This message was added in version 6.7.0.) in /home/mother99/jacksonholdingcompany.com/wp-includes/functions.php on line 6114

Warning: Cannot modify header information - headers already sent by (output started at /home/mother99/jacksonholdingcompany.com/wp-includes/functions.php:6114) in /home/mother99/jacksonholdingcompany.com/wp-includes/rest-api/class-wp-rest-server.php on line 1893

Warning: Cannot modify header information - headers already sent by (output started at /home/mother99/jacksonholdingcompany.com/wp-includes/functions.php:6114) in /home/mother99/jacksonholdingcompany.com/wp-includes/rest-api/class-wp-rest-server.php on line 1893

Warning: Cannot modify header information - headers already sent by (output started at /home/mother99/jacksonholdingcompany.com/wp-includes/functions.php:6114) in /home/mother99/jacksonholdingcompany.com/wp-includes/rest-api/class-wp-rest-server.php on line 1893

Warning: Cannot modify header information - headers already sent by (output started at /home/mother99/jacksonholdingcompany.com/wp-includes/functions.php:6114) in /home/mother99/jacksonholdingcompany.com/wp-includes/rest-api/class-wp-rest-server.php on line 1893

Warning: Cannot modify header information - headers already sent by (output started at /home/mother99/jacksonholdingcompany.com/wp-includes/functions.php:6114) in /home/mother99/jacksonholdingcompany.com/wp-includes/rest-api/class-wp-rest-server.php on line 1893

Warning: Cannot modify header information - headers already sent by (output started at /home/mother99/jacksonholdingcompany.com/wp-includes/functions.php:6114) in /home/mother99/jacksonholdingcompany.com/wp-includes/rest-api/class-wp-rest-server.php on line 1893

Warning: Cannot modify header information - headers already sent by (output started at /home/mother99/jacksonholdingcompany.com/wp-includes/functions.php:6114) in /home/mother99/jacksonholdingcompany.com/wp-includes/rest-api/class-wp-rest-server.php on line 1893

Warning: Cannot modify header information - headers already sent by (output started at /home/mother99/jacksonholdingcompany.com/wp-includes/functions.php:6114) in /home/mother99/jacksonholdingcompany.com/wp-includes/rest-api/class-wp-rest-server.php on line 1893
{"id":857,"date":"2023-08-15T20:49:53","date_gmt":"2023-08-15T20:49:53","guid":{"rendered":"https:\/\/jacksonholdingcompany.com\/making-your-first-terraform-file-doesnt-have-to-be-scary-quinn-snyder-on-august-15-2023-at-700-pm\/"},"modified":"2023-08-15T20:49:53","modified_gmt":"2023-08-15T20:49:53","slug":"making-your-first-terraform-file-doesnt-have-to-be-scary-quinn-snyder-on-august-15-2023-at-700-pm","status":"publish","type":"post","link":"https:\/\/jacksonholdingcompany.com\/making-your-first-terraform-file-doesnt-have-to-be-scary-quinn-snyder-on-august-15-2023-at-700-pm\/","title":{"rendered":"Making Your First Terraform File Doesn\u2019t Have to Be Scary Quinn Snyder on August 15, 2023 at 7:00 pm"},"content":{"rendered":"

For the past several years, I\u2019ve tried to give at least one Terraform-centric session at Cisco Live. That\u2019s because they\u2019re fun and make for awesome demos. What\u2019s a technical talk without a demo? But \u2026 Read more on Cisco Blogs<\/a><\/p>\n

\u200b<\/p>\n

For the past several years, I\u2019ve tried to give at least one Terraform-centric session at Cisco Live. That\u2019s because they\u2019re fun and make for awesome demos. What\u2019s a technical talk without a demo? But I also see huge crowds every time I talk about Terraform. While I wasn\u2019t an economics major, I do know if demand is this large, we need a larger supply!<\/p>\n

That\u2019s why I decided to step back and focus to the basics of Terraform and its operation. The configuration applied won\u2019t be anything complex, but it should explain some basic structures and requirements for Terraform to do its thing against a single piece of infrastructure, Cisco ACI. Don\u2019t worry if you\u2019re not an ACI expert; deep ACI knowledge isn\u2019t required for what we\u2019ll be configuring.<\/p>\n

The HCL File: What Terraform will configure<\/strong><\/h2>\n

A basic Terraform configuration file is written in Hashicorp Configuration Language (HCL). This domain-specific language (DSL) is similar in structure to JSON, but it adds components for things like control structures, large configuration blocks, and intuitive variable assignments (rather than simple key-value pairs).<\/p>\n

At the top of every Terraform HCL file, we must declare the providers we\u2019ll need to gather from the Terraform registry. A provider supplies the linkage between the Terraform binary and the endpoint to be configured by defining what can be configured and what the API endpoints and the data payloads should look like. In our example, we\u2019ll only need to gather the ACI provider, which is defined like this:<\/p>\n

terraform
\n\u00a0 required_providers
\n\u00a0\u00a0\u00a0 aci =
\n\u00a0\u00a0\u00a0\u00a0\u00a0 source = \u201cCiscoDevNet\/aci\u201d
\n\u00a0\u00a0\u00a0
\n\u00a0
\n <\/p>\n

Once you declare the required providers, you have to tell Terraform how to connect to the ACI fabric, which we do through the provider-specific configuration block:<\/p>\n

provider “aci”
\nusername = “admin”
\npassword = “C1sco12345″
\nurl\u00a0\u00a0\u00a0\u00a0\u00a0 = “https:\/\/10.10.20.14″
\ninsecure = true <\/p>\n

Notice the name we gave the ACI provider (aci<\/strong>) in the terraform<\/strong> configuration block matches the declaration for the provider configuration. We\u2019re telling Terraform the provider we named aci<\/strong> should use the following configuration to connect to the controller. Also, note the username<\/strong>, password<\/strong>, url<\/strong>, and insecure<\/strong> configuration options are nested within curly braces . This indicates to Terraform that all this configuration should all be grouped together, regardless of whitespaces, indentation, or the use of tabs vs. spaces.<\/p>\n

Now that we have a connection method to the ACI controller, we can define the configuration we want to apply to our datacenter fabric. We do this using a resource configuration block. Within Terraform, we call something a resource when we want to change its configuration; it\u2019s a data source when we only want to read in the configuration that already exists. The configuration block contains two arguments, the name of the tenant we\u2019ll be creating and a description for that tenant.<\/p>\n

resource “aci_tenant” “demo_tenant”
\nname\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = “TheU_Tenant”
\ndescription = “Demo tenant for the U” <\/p>\n

Once we write that configuration to a file, we can save it and begin the process to apply this configuration to our fabric using Terraform.<\/p>\n

The Terraform workflow: How Terraform applies configuration<\/strong><\/h2>\n

Terraform\u2019s workflow to apply configuration is straightforward and stepwise. Once we\u2019ve written the configuration, we can perform a terraform init<\/strong>, which will gather the providers from the Terraform registry who have been declared in the HCL file, install them into the project folder, and ensure they are signed with the same PGP key that HashiCorp has on file (to ensure end-to-end security). The output of this will look similar to this:<\/p>\n

[I] theu-terraform \u00bb terraform init
\nInitializing the backend…
\nInitializing provider plugins…
\n– Finding latest version of ciscodevnet\/aci…
\n– Installing ciscodevnet\/aci v2.9.0…
\n– Installed ciscodevnet\/aci v2.9.0 (signed by a HashiCorp partner, key ID 433649E2C56309DE)
\nPartner and community providers are signed by their developers.
\nIf you’d like to know more about provider signing, you can read about it here:
\nhttps:\/\/www.terraform.io\/docs\/cli\/plugins\/signing.html
\nTerraform has created a lock file .terraform.lock.hcl to record the provider
\nselections it made above. Include this file in your version control repository
\nso that Terraform can guarantee to make the same selections by default when
\nyou run “terraform init” in the future.
\nTerraform has been successfully initialized!<\/p>\n

You may now begin working with Terraform. Try running \u201cterraform plan\u201d to see any changes required for your infrastructure. All Terraform commands should now work.<\/p>\n

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.<\/p>\n

Once the provider has been gathered, we can invoke terraform plan<\/strong> to see what changes will occur in the infrastructure prior to applying the config. I\u2019m using the reservable ACI sandbox<\/a> from Cisco DevNet\u00a0 for the backend infrastructure but you can use the Always-On sandbox<\/a> or any other ACI simulator or hardware instance. Just be sure to change the target username<\/strong>, password<\/strong>, and url<\/strong> in the HCL configuration file.<\/p>\n

Performing the plan<\/strong> action will output the changes that need to be made to the infrastructure, based on what Terraform currently knows about the infrastructure (which in this case is nothing, as Terraform has not applied any configuration yet). For our configuration, the following output will appear:<\/p>\n

[I] theu-terraform \u00bb terraform plan
\nTerraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
\n + create
\nTerraform will perform the following actions:
\n# aci_tenant.demo_tenant will be created
\n+ resource “aci_tenant” “demo_tenant”
\n+ annotation\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = “orchestrator:terraform”
\n+ description\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = “Demo tenant for the U”
\n+ id\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = (known after apply)
\n+ name\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = “TheU_Tenant”
\n+ name_alias\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = (known after apply)
\n+ relation_fv_rs_tenant_mon_pol = (known after apply)
\n
\nPlan: 1 to add, 0 to change, 0 to destroy.
\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
\nNote: You didn’t use the -out option to save this plan, so Terraform can’t guarantee to take exactly these actions if
\nyou run “terraform apply” now. <\/p>\n

We can see that the items with a plus symbol (+) next to them are to be created, and they align with what we had in the configuration originally. Great!\u00a0 Now we can apply this configuration. We perform this by using the terraform apply<\/strong> command. After invoking the command, we\u2019ll be prompted if we want to create this change, and we\u2019ll respond with \u201cyes.\u201d<\/p>\n

[I] theu-terraform \u00bb terraform apply\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0
\nTerraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
\nfollowing symbols:
\n\u00a0 + create
\nTerraform will perform the following actions:
\n\u00a0 # aci_tenant.demo_tenant will be created
\n\u00a0 + resource “aci_tenant” “demo_tenant”
\n\u00a0\u00a0\u00a0\u00a0\u00a0 + annotation\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = “orchestrator:terraform”
\n\u00a0\u00a0\u00a0\u00a0\u00a0 + description\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = “Demo tenant for the U”
\n\u00a0\u00a0\u00a0\u00a0\u00a0 + id\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = (known after apply)
\n\u00a0\u00a0\u00a0\u00a0\u00a0 + name\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = “TheU_Tenant”
\n\u00a0\u00a0\u00a0\u00a0\u00a0 + name_alias\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = (known after apply)
\n\u00a0\u00a0\u00a0\u00a0\u00a0 + relation_fv_rs_tenant_mon_pol = (known after apply)
\n\u00a0\u00a0\u00a0
\nPlan: 1 to add, 0 to change, 0 to destroy.
\nDo you want to perform these actions?
\n\u00a0 Terraform will perform the actions described above.
\n Only ‘yes’ will be accepted to approve.
\n\u00a0 Enter a value: yes
\naci_tenant.demo_tenant: Creating…
\naci_tenant.demo_tenant: Creation complete after 3s [id=uni\/tn-TheU_Tenant]
\nApply complete! Resources: 1 added, 0 changed, 0 destroyed. <\/p>\n

The configuration has now been applied to the fabric!\u00a0 If you\u2019d like to verify, log in to the fabric and click on the Tenants tab. You should see the newly created tenant.<\/p>\n

Finally \u2013 if you\u2019d like to delete the tenant the same way you created it, you don\u2019t have to create any complex rollback configuration. Simply invoke terraform destroy<\/strong> from the command line. Terraform will verify the state that exists locally within your project aligns with what exists on the fabric; then it will indicate what will be removed. After a quick confirmation, you\u2019ll see that the tenant is removed, and you can verify in the Tenants tab of the fabric.<\/p>\n

[I] theu-terraform \u00bb terraform destroy\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0
\naci_tenant.demo_tenant: Refreshing state… [id=uni\/tn-TheU_Tenant]
\nTerraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
\nfollowing symbols:
\n\u00a0 – destroy
\nTerraform will perform the following actions:
\n\u00a0 # aci_tenant.demo_tenant will be destroyed
\n\u00a0 – resource “aci_tenant” “demo_tenant”
\n\u00a0\u00a0\u00a0\u00a0\u00a0 – annotation\u00a0 = “orchestrator:terraform” -> null
\n\u00a0\u00a0\u00a0\u00a0\u00a0 – description = “Demo tenant for the U” -> null
\n\u00a0\u00a0\u00a0\u00a0\u00a0 – id\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = “uni\/tn-TheU_Tenant” -> null
\n\u00a0\u00a0\u00a0\u00a0\u00a0 – name\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 = “TheU_Tenant” -> null
\n\u00a0\u00a0\u00a0
\nPlan: 0 to add, 0 to change, 1 to destroy.
\nDo you really want to destroy all resources?
\n\u00a0 Terraform will destroy all your managed infrastructure, as shown above.
\n\u00a0 There is no undo. Only ‘yes’ will be accepted to confirm.
\n\u00a0 Enter a value: yes
\naci_tenant.demo_tenant: Destroying… [id=uni\/tn-TheU_Tenant]
\naci_tenant.demo_tenant: Destruction complete after 1s
\nDestroy complete! Resources: 1 destroyed. <\/p>\n

Complete Infrastructure as Code lifecycle management with a single tool is pretty amazing, huh?<\/p>\n

A bonus tip<\/strong><\/h2>\n

Another tip regarding Terraform and HCL relates to the workflow section above. I described the use of curly braces to avoid the need to ensure whitespace is correct or tab width is uniform within the configuration file. This is generally a good thing, as we can focus on what we want to deploy rather than minutiae of the config. However, sometimes it helps when you format the configuration in a way that\u2019s aligned and easier to read, even if it doesn\u2019t affect the outcome of what is deployed.<\/p>\n

In these instances, you can invoke terraform fmt<\/strong> within your project folder, and it will automatically format all Terraform HCL files into aligned and readable text. You can try this yourself by adding a tab or multiple spaces before an argument or maybe between the = sign within some of the HCL. Save the file, run the formatter, and then reopen the file to see the changes. Pretty neat, huh?<\/p>\n

Want to know more?<\/strong><\/h2>\n

For a deeper dive beyond this introductory video, I have several Terraform videos on our YouTube channel that dive into more complex configurations as well as other options that exist within Terraform. You can also watch the video below, which offers sample code links to get your hands dirty with Terraform.<\/p>\n