Guest post by: Adam Friedman
Infrastructure as Code (IAC) is one of the important foundations for cloud automation. In this post we will walk through a minimal configuration scenario to illustrate a practical application of IAC using a tool called Terraform on Dimension Data cloud (MCP). This walk through should demonstrate that Terraform can be used to manage both temporary and more permanent deployments of infrastructure in Dimension Data cloud via its management layer – CloudControl.
Terraform is a tool for declarative management of virtualized infrastructure (think of it as Chef but focused more on infrastructure rather than configuration management).
It can be used to define the infrastructure that comprises an environment, and then manage that infrastructure on an ongoing basis.
CloudControl is the Dimension Data MCP management layer that provides both a UI and API to manage resources. CloudControl API’s have been integrated into several popular automation tools and libraries such as Ansible, Apache Libcloud, Powershell, among others.
Install and Configure
To install Terraform, please follow the directions outlined here. Detailed documentation for Terraform can be found on the product website.
You will also need to download and configure provider (native integration for a cloud platform provider) support for CloudControl (Dimension Data MCP). Download the binary for your OS and follow the instructions under the Getting Started section.
For detailed documentation on Dimension Data provider usage please refer to the GitHub repo.
Usage
The displayed configuration (in HCL format) below will create:
- A network domain
- A VLAN
- A server with its primary network adapter attached to the VLAN
provider "ddcloud" { region = "AU" } resource "ddcloud_networkdomain" "test-domain" { name = "my-terraform-domain-1" description = "My Terraform test domain." datacenter = "AU9" } resource "ddcloud_vlan" "test-vlan" { name = "my-terraform-vlan" description = "My Terraform test VLAN." networkdomain = "${ddcloud_networkdomain.test-domain.id}" ipv4_base_address = "192.168.17.0" ipv4_prefix_size = 24 # 255.255.255.0 = 192.168.17.1 -> 192.168.17.254 } resource "ddcloud_server" "test-vm" { name = "my-test-vm" description = "My Terraform test VM." admin_password = "my admin password" memory_gb = 8 cpu_count = 2 networkdomain = "${ddcloud_networkdomain.test-domain.id}" primary_adapter_vlan = "${ddcloud_vlan.test-vlan.id}" # Will use first available IPv4 address on this VLAN. dns_primary = "8.8.8.8" dns_secondary = "8.8.4.4" os_image_name = "CentOS 7 64-bit 2 CPU" disk { scsi_unit_id = 0 size_gb = 10 } tag { name = "role" value = "tf-test" } }
Evident in the output, Terraform usually treats each resource in CloudControl (or any other system) as a discrete component that can be created, managed, and destroyed.
Resources can also use properties from other resources. Terraform will keep track of the resulting dependencies between them.
If you run terraform plan, the following output is produced:
+ ddcloud_networkdomain.test-domain datacenter: "AU9" description: "My Terraform test domain." name: "my-terraform-domain-1" nat_ipv4_address: "" plan: "ESSENTIALS" + ddcloud_server.test-vm admin_password: "" auto_start: "false" cores_per_cpu: "" cpu_count: "2" cpu_speed: "" customer_image_id: "" customer_image_name: "" description: "My Terraform test VM." disk.#: "1" disk.219226128.disk_id: "" disk.219226128.scsi_unit_id: "0" disk.219226128.size_gb: "10" disk.219226128.speed: "STANDARD" dns_primary: "8.8.8.8" dns_secondary: "8.8.4.4" memory_gb: "8" name: "my-test-vm" networkdomain: "${ddcloud_networkdomain.test-domain.id}" os_image_id: "" os_image_name: "CentOS 7 64-bit 2 CPU" primary_adapter_ipv4: "" primary_adapter_ipv6: "" primary_adapter_vlan: "${ddcloud_vlan.test-vlan.id}" public_ipv4: "" tag.#: "1" tag.3046084505.name: "role" tag.3046084505.value: "tf-test" + ddcloud_vlan.test-vlan description: "My Terraform test VLAN." ipv4_base_address: "192.168.17.0" ipv4_prefix_size: "24" ipv6_base_address: "" ipv6_prefix_size: "" name: "my-terraform-vlan" networkdomain: "${ddcloud_networkdomain.test-domain.id}" Plan: 3 to add, 0 to change, 0 to destroy.
Let’s run terraform apply. Terraform will now create these resources for you.
ddcloud_networkdomain.test-domain: Creating... datacenter: "" => "AU9" description: "" => "My Terraform test domain." name: "" => "my-terraform-domain-1" nat_ipv4_address: "" => "" plan: "" => "ESSENTIALS" ddcloud_networkdomain.test-domain: Creation complete ddcloud_vlan.test-vlan: Creating... description: "" => "My Terraform test VLAN." ipv4_base_address: "" => "192.168.17.0" ipv4_prefix_size: "" => "24" ipv6_base_address: "" => "" ipv6_prefix_size: "" => "" name: "" => "my-terraform-vlan" networkdomain: "" => "11b5cf11-ccbb-460c-ad0a-4093029c0aff" ddcloud_vlan.test-vlan: Creation complete ddcloud_server.test-vm: Creating... admin_password: "" => "" auto_start: "" => "false" cores_per_cpu: "" => "" cpu_count: "" => "2" cpu_speed: "" => "" customer_image_id: "" => "" customer_image_name: "" => "" description: "" => "My Terraform test VM." disk.#: "" => "1" disk.219226128.disk_id: "" => "" disk.219226128.scsi_unit_id: "" => "0" disk.219226128.size_gb: "" => "10" disk.219226128.speed: "" => "STANDARD" dns_primary: "" => "8.8.8.8" dns_secondary: "" => "8.8.4.4" memory_gb: "" => "8" name: "" => "my-test-vm" networkdomain: "" => "11b5cf11-ccbb-460c-ad0a-4093029c0aff" os_image_id: "" => "" os_image_name: "" => "CentOS 7 64-bit 2 CPU" primary_adapter_ipv4: "" => "" primary_adapter_ipv6: "" => "" primary_adapter_vlan: "" => "3794f6e3-ee89-431b-afea-b77b0ceca9bc" public_ipv4: "" => "" tag.#: "" => "1" tag.3046084505.name: "" => "role" tag.3046084505.value: "" => "tf-test" ddcloud_server.test-vm: Creation complete Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
This is certainly a convenient way to deploy infrastructure (especially if there’s a lot of it).
But what if you want to make just one change? Let’s alter the name of the network domain.
resource "ddcloud_networkdomain" "test-domain" { # name = "my-terraform-domain-1" name = "the-terraform-domain" # .... other properties removed for clarity }
Run terraform plan again, and look at the output.
~ ddcloud_networkdomain.test-domain name: "my-terraform-domain-1" => "my-terraform-domain" Plan: 0 to add, 1 to change, 0 to destroy.
Terraform has examined the existing resources in CloudControl, compared them to the local configuration, and correctly detected that the only thing that has changed is the name of the network domain. Run terraform apply again, and note that all it does is update the network domain name.
ddcloud_networkdomain.test-domain: Modifying... name: "my-terraform-domain-1" => "the-terraform-domain" ddcloud_networkdomain.test-domain: Modifications complete Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
Now try changing the value of the server’s `dns_primary` property.
resource "ddcloud_server" "test-vm" { # .... other properties removed for clarity # dns_secondary = "8.8.4.4" dns_secondary = "8.8.1.1" # .... other properties removed for clarity }
Run terraform plan and observe the following output:
-/+ ddcloud_server.test-vm admin_password: "" => "" (attribute changed) auto_start: "false" => "false" cores_per_cpu: "1" => "" cpu_count: "2" => "2" cpu_speed: "STANDARD" => "" customer_image_id: "" => "" customer_image_name: "" => "" description: "My Terraform test VM." => "My Terraform test VM." disk.#: "1" => "1" disk.219226128.disk_id: "c325711d-003c-4aa1-bc0c-fa2cd0443fc1" => "" disk.219226128.scsi_unit_id: "0" => "0" disk.219226128.size_gb: "10" => "10" disk.219226128.speed: "STANDARD" => "STANDARD" dns_primary: "8.8.8.8" => "8.8.8.8" dns_secondary: "8.8.4.4" => "8.8.1.1" (forces new resource) memory_gb: "8" => "8" name: "my-test-vm" => "my-test-vm" networkdomain: "11b5cf11-ccbb-460c-ad0a-4093029c0aff" => "11b5cf11-ccbb-460c-ad0a-4093029c0aff" os_image_id: "e1b4e0cc-35ba-47be-a2d7-1b5601b87119" => "" os_image_name: "CentOS 7 64-bit 2 CPU" => "CentOS 7 64-bit 2 CPU" primary_adapter_ipv4: "192.168.17.6" => "" primary_adapter_ipv6: "2402:9900:111:1454:5b20:7083:820b:a24e" => "" primary_adapter_vlan: "3794f6e3-ee89-431b-afea-b77b0ceca9bc" => "3794f6e3-ee89-431b-afea-b77b0ceca9bc" public_ipv4: "" => "" tag.#: "1" => "1" tag.3046084505.name: "role" => "role" tag.3046084505.value: "tf-test" => "tf-test" Plan: 1 to add, 0 to change, 1 to destroy.
Note that, this time, Terraform has detected that you’ve attempted to change a property that cannot be changed after deployment. It will therefore offer to destroy and re-create the server if you choose to proceed.
Let’s clean up by running terraform destroy and typing yes when prompted.
Do you really want to destroy? Terraform will delete all your managed infrastructure. There is no undo. Only 'yes' will be accepted to confirm. Enter a value: yes ddcloud_server.test-vm: Destroying... ddcloud_server.test-vm: Destruction complete ddcloud_vlan.test-vlan: Destroying... ddcloud_vlan.test-vlan: Destruction complete ddcloud_networkdomain.test-domain: Destroying... ddcloud_networkdomain.test-domain: Destruction complete. Apply complete! Resources: 0 added, 0 changed, 3 destroyed.
If now you confirm via the CloudControl UI you will see that the network domain and its contents have been deleted.
This concludes the walk through of a minimal configuration scenario using Terraform. Hopefully this practical example gives you an appreciation of what Infrastructure as Code brings to bear. In part 2 of this series we will build on this scenario and explore how Ansible can be utilized as a Configuration Management tool to deploy applications in concert with Terraform to form a single comprehensive solution for automating infrastructure and applications.
November 12, 2016 at 5:46 am
BTW, here’s the source for the Terraform configuration in the article above:
Terraform and Dimension Data CloudControl (a tutorial)
Introduction
Terraform is a tool for declarative management of virtualised infrastructure (think of it as Chef but focused more on infrastructure rather than configuration management).
It can be used to define the infrastructure that comprises an environment, and then manage that infrastructure on an ongoing basis.
CloudControl provides both a UI and API to manage resources (as well as integrations for several popular automation tools and libraries such as Ansible, libcloud, Powershell, and Terraform).
Let’s look at the minimum configuration required to create an environment. Hopefully this initial demo will show you that Terraform can be used to manage both temporary and more permanent deployments of infrastructure in CloudControl.
Step 1
The following configuration will create:
As you can see, Terraform usually treats each resource in CloudControl (or any other system) as a discreet component that can be created, managed, and destroyed. Resources can also use properties from other resources (and Terraform will keep track of the resulting dependencies between them).
If you run
terraform plan
, the following output is produced:Let’s run
terraform apply
. Terraform will now create these resources for you.So this is a nice way to deploy infrastructure (especially if there’s a lot of it).
But what if you want to make just one change? Let’s alter the name of the network domain.
Run
terraform plan
again, and look at the output.As you can see, the Terraform has examined the existing resources in CloudControl, compared them to the local configuration, and correctly detected that the only thing that has changed is the name of the network domain. Run
terraform apply
again, and note that all it does is update the network domain name.Now try changing the value of the server’s
dns_primary
property.Run
terraform plan
.Note that, this time, Terraform has detected that you’ve attempted to change a property that cannot be changed after deployment. It will therefore offer to destroy and re-create the server if you choose to proceed.
Let’s clean up by running
terraform destroy
and typing “yes” when prompted.If you now check the CloudControl UI you will see that the network domain and its contents have been deleted.
terraform-tutorial.md
hosted with ❤ by GitHub
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
tutorial-step-1.tf
hosted with ❤ by GitHub
LikeLike
November 12, 2016 at 5:49 am
Ugh, that is SO not what I wanted (all I wanted to insert was a link to the gist, not the gist itself). Thanks, wordpress
https-colon-slashslash-gist.github.com/tintoy/e043911b41fb3988793f3e491e22b629
LikeLike
November 12, 2016 at 5:51 am
Ugh. WordPress is annoying. I wanted to insert the link, not the entire gist!
LikeLike
November 12, 2016 at 5:58 am
Ugh, wanted to post link to gist, not the entire gist!
LikeLike