Automate OpenStack with Terraform

Terraform can be used with Openstack for auto-provisioing.

Today, I will shows a working Terraform example in Openstack.

Firstly, define a Openstack provider for Terraform.

Provider:

provider “openstack” {
user_name = “${var.openstack_user_name}”
password = “${var.openstack_password}”
tenant_name = “project1”
auth_url = “http://keystone.openstack.com.au:5000/v3
domain_name = “DOMAINNAME”
}

Terraform currently support the following Openstack resource type: Compute, Network, Load Balancer, Firewall, Block Storage and Object Storage.

Here, we create a few of basic resources including Compute and Network (network (VXLAN here, but can be VLAN or any other kind of networks), subnet and security group)

Network:

Create a network named “tf-net2

resource “openstack_networking_network_v2” “tf-net2” {
region = “region1”
name = “tf-net2”
admin_state_up = “true”
}

create a subnet “tf_net_sub2” and associate with network tf-net2

resource “openstack_networking_subnet_v2” “tf_net_sub2” {
name = “tf_net_sub2”
region = “region1”
network_id = “${openstack_networking_network_v2.tf-net2.id}”
cidr = “172.16.50.0/24”
ip_version = 4
enable_dhcp = “false”
}

Security Group:

create a security group “secgroup_1” , then add 2 rules

resource “openstack_networking_secgroup_v2” “secgroup_1” {
name = “secgroup_1”
description = “Terraform security group”
}
resource “openstack_networking_secgroup_rule_v2” “secgroup_rule_1” {
direction = “egress”
ethertype = “IPv4”
protocol = “tcp”
port_range_min = 22
port_range_max = 22
remote_ip_prefix = “10.41.129.12/32”
security_group_id = “${openstack_networking_secgroup_v2.secgroup_1.id}”
}

resource “openstack_networking_secgroup_rule_v2” “secgroup_rule_2” {
direction = “ingress”
ethertype = “IPv4”
protocol = “tcp”
port_range_min = 22
port_range_max = 22
remote_ip_prefix = “10.41.129.12/32”
security_group_id = “${openstack_networking_secgroup_v2.secgroup_1.id}”
}

Compute:

create 1 virtual instance using network tf-net2 and security group secgroup_1 which just created.

resource “openstack_compute_instance_v2” “vm_terraform” {

region = “region1”
availability_zone = “az1”
name = “nsx_terraform”
image_id = “b5d00e5c-ab30-4fb4-9ed0-1d99c7ff864b”
flavor_id = “10”
security_groups = [“${openstack_networking_secgroup_v2.secgroup_1.id}”]

metadata {
this = “that”
}

network {
name = “tf-net2”
}
stop_before_destroy = “true”
}

Result:

Openstack Network:

openstack-network

Security Group:

securitygroup

VM:

vm

Create real-world like ASW security groups using Terraform

[dzhang@localhost terraform]$ cat instance.tf
provider “aws” {
access_key = “my_access_key”
secret_key = “my_secret_key”
region = “ap-southeast-2”
}
resource “aws_security_group” “app_server” {
name = “app_server”
description = “app server security group”
vpc_id = “vpc-d808xxxx”

ingress {
from_port = 22
to_port = 22
protocol = “tcp”
cidr_blocks = [“6x.24x.5x.16x/32”]
}

tags {
Name = “APP”
}
}

resource “aws_security_group” “web_server” {
name = “web_server”
description = “Web Server security group”
vpc_id = “vpc-d808xxxx”

ingress {
from_port = 80
to_port = 80
protocol = “tcp”
cidr_blocks = [“0.0.0.0/0”]
}
egress {
from_port = 1024
to_port = 65535
protocol = “tcp”
cidr_blocks = [“0.0.0.0/0”]
}

tags {
Name = “WEB”
}
}

resource “aws_security_group_rule” “internal-sg” {
security_group_id = “${aws_security_group.web_server.id}”
type = “egress”
from_port = 8301
to_port = 8301
protocol = “udp”
self = true
}

resource “aws_security_group_rule” “to_app” {
security_group_id = “${aws_security_group.web_server.id}”
type = “egress”
from_port = 8301
to_port = 8301
protocol = “tcp”
source_security_group_id = “${aws_security_group.app_server.id}”
}

resource “aws_security_group_rule” “from_web” {
security_group_id = “${aws_security_group.app_server.id}”
type = “ingress”
from_port = 8301
to_port = 8301
protocol = “tcp”
source_security_group_id = “${aws_security_group.web_server.id}”
}
[dzhang@localhost terraform]$

[dzhang@localhost terraform]$ terraform apply
aws_security_group.app_server: Creating…
description: “” => “app server security group”
egress.#: “” => “”
ingress.#: “” => “1”
ingress.625464618.cidr_blocks.#: “” => “1”
ingress.625464618.cidr_blocks.0: “” => “6x.24x.5x.16x/32”
ingress.625464618.from_port: “” => “22”
ingress.625464618.protocol: “” => “tcp”
ingress.625464618.security_groups.#: “” => “0”
ingress.625464618.self: “” => “false”
ingress.625464618.to_port: “” => “22”
name: “” => “app_server”
owner_id: “” => “”
tags.%: “” => “1”
tags.Name: “” => “APP”
vpc_id: “” => “vpc-d808xxxx”
aws_security_group.web_server: Creating…
description: “” => “Web Server security group”
egress.#: “” => “1”
egress.1543620397.cidr_blocks.#: “” => “1”
egress.1543620397.cidr_blocks.0: “” => “0.0.0.0/0”
egress.1543620397.from_port: “” => “1024”
egress.1543620397.prefix_list_ids.#: “” => “0”
egress.1543620397.protocol: “” => “tcp”
egress.1543620397.security_groups.#: “” => “0”
egress.1543620397.self: “” => “false”
egress.1543620397.to_port: “” => “65535”
ingress.#: “” => “1”
ingress.2214680975.cidr_blocks.#: “” => “1”
ingress.2214680975.cidr_blocks.0: “” => “0.0.0.0/0”
ingress.2214680975.from_port: “” => “80”
ingress.2214680975.protocol: “” => “tcp”
ingress.2214680975.security_groups.#: “” => “0”
ingress.2214680975.self: “” => “false”
ingress.2214680975.to_port: “” => “80”
name: “” => “web_server”
owner_id: “” => “”
tags.%: “” => “1”
tags.Name: “” => “WEB”
vpc_id: “” => “vpc-d808xxxx”
aws_security_group.app_server: Creation complete
aws_security_group.web_server: Creation complete
aws_security_group_rule.from_web: Creating…
from_port: “” => “8301”
protocol: “” => “tcp”
security_group_id: “” => “sg-ba43ecdd”
self: “” => “false”
source_security_group_id: “” => “sg-b943ecde”
to_port: “” => “8301”
type: “” => “ingress”
aws_security_group_rule.to_app: Creating…
from_port: “” => “8301”
protocol: “” => “tcp”
security_group_id: “” => “sg-b943ecde”
self: “” => “false”
source_security_group_id: “” => “sg-ba43ecdd”
to_port: “” => “8301”
type: “” => “egress”
aws_security_group_rule.internal-sg: Creating…
from_port: “” => “8301”
protocol: “” => “udp”
security_group_id: “” => “sg-b943ecde”
self: “” => “true”
source_security_group_id: “” => “”
to_port: “” => “8301”
type: “” => “egress”
aws_security_group_rule.from_web: Creation complete
aws_security_group_rule.internal-sg: Creation complete
aws_security_group_rule.to_app: Creation complete

Apply complete! Resources: 5 added, 0 changed, 0 destroyed.

The state of your infrastructure has been saved to the path
below. This state is required to modify and destroy your
infrastructure, so keep it safe. To inspect the complete state
use the `terraform show` command.

State path: terraform.tfstate
[dzhang@localhost terraform]$ terraform destory
Usage: terraform [–version] [–help] [args]

The available commands for execution are listed below.
The most common, useful commands are shown first, followed by
less common or more advanced commands. If you’re just getting
started with Terraform, stick with the common commands. For the
other commands, please read the help and docs before usage.

Common commands:
apply Builds or changes infrastructure
console Interactive console for Terraform interpolations
destroy Destroy Terraform-managed infrastructure
fmt Rewrites config files to canonical format
get Download and install modules for the configuration
graph Create a visual graph of Terraform resources
import Import existing infrastructure into Terraform
init Initializes Terraform configuration from a module
output Read an output from a state file
plan Generate and show an execution plan
push Upload this Terraform module to Atlas to run
refresh Update local state file against real resources
remote Configure remote state storage
show Inspect Terraform state or plan
taint Manually mark a resource for recreation
untaint Manually unmark a resource as tainted
validate Validates the Terraform files
version Prints the Terraform version

All other commands:
debug Debug output management (experimental)
state Advanced state management
[dzhang@localhost terraform]$ terraform destroy
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

aws_security_group.app_server: Refreshing state… (ID: sg-ba43ecdd)
aws_security_group.web_server: Refreshing state… (ID: sg-b943ecde)
aws_security_group_rule.internal-sg: Refreshing state… (ID: sgrule-2476559081)
aws_security_group_rule.to_app: Refreshing state… (ID: sgrule-2890481209)
aws_security_group_rule.from_web: Refreshing state… (ID: sgrule-3247970428)
aws_security_group_rule.from_web: Destroying…
aws_security_group_rule.to_app: Destroying…
aws_security_group_rule.internal-sg: Destroying…
aws_security_group_rule.internal-sg: Destruction complete
aws_security_group_rule.from_web: Destruction complete
aws_security_group_rule.to_app: Destruction complete
aws_security_group.app_server: Destroying…
aws_security_group.web_server: Destroying…
aws_security_group.web_server: Destruction complete
aws_security_group.app_server: Destruction complete

Destroy complete! Resources: 5 destroyed.
[dzhang@localhost terraform]$

Build Your First AWS Virtual Instance using Terraform

Step 0: create a API user and make sure that this API user has the proper access

aws_apiuser

Step 1: create instance.tf file as the below. This will creare a micro virtual instance in AWS Sydney

provider “aws” {
access_key = “myaccess_key”
secret_key = “mysecre_key”
region = “ap-southeast-2”
}

resource “aws_instance” “terraform_linux” {
ami = “ami-fe71759d”
instance_type = “t2.micro”
}
Step 2: Terraform appy to start the build
[dzhang@localhost terraform]$ terraform apply
aws_instance.terraform_linux: Creating…
ami: “” => “ami-fe71759d”
associate_public_ip_address: “” => “”
availability_zone: “” => “”
ebs_block_device.#: “” => “”
ephemeral_block_device.#: “” => “”
instance_state: “” => “”
instance_type: “” => “t2.micro”
key_name: “” => “”
network_interface_id: “” => “”
placement_group: “” => “”
private_dns: “” => “”
private_ip: “” => “”
public_dns: “” => “”
public_ip: “” => “”
root_block_device.#: “” => “”
security_groups.#: “” => “”
source_dest_check: “” => “true”
subnet_id: “” => “”
tenancy: “” => “”
vpc_security_group_ids.#: “” => “”
aws_instance.terraform_linux: Still creating… (10s elapsed)
aws_instance.terraform_linux: Still creating… (20s elapsed)
aws_instance.terraform_linux: Creation complete

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

The state of your infrastructure has been saved to the path
below. This state is required to modify and destroy your
infrastructure, so keep it safe. To inspect the complete state
use the `terraform show` command.

State path: terraform.tfstate
Step3: Verify the virtual instance has been created in AWS console

2017-02-13_192341

Step 4: Destroy the created Virtual Instance

[dzhang@localhost terraform]$ terraform destroy
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

aws_instance.terraform_linux: Refreshing state… (ID: i-04e8f2757c43a5da8)
aws_instance.terraform_linux: Destroying…
aws_instance.terraform_linux: Still destroying… (10s elapsed)
aws_instance.terraform_linux: Still destroying… (20s elapsed)
aws_instance.terraform_linux: Still destroying… (30s elapsed)
aws_instance.terraform_linux: Still destroying… (40s elapsed)
aws_instance.terraform_linux: Still destroying… (50s elapsed)
aws_instance.terraform_linux: Still destroying… (1m0s elapsed)
aws_instance.terraform_linux: Still destroying… (1m10s elapsed)
aws_instance.terraform_linux: Destruction complete

Destroy complete! Resources: 1 destroyed.
Step 5: Verify the virtual instance are terminated.

2017-02-13_193356

You can check the history status of the virtual instance by veiwing the content of state files: terraform.tfstate and  terraform.tfstate.backup

[dzhang@localhost terraform]$ cat terraform.tfstate
{
“version”: 3,
“terraform_version”: “0.8.6”,
“serial”: 2,
“lineage”: “7da04b67-0d9d-4337-80a7-9ffe05753f83”,
“modules”: [
{
“path”: [
“root”
],
“outputs”: {},
“resources”: {},
“depends_on”: []
}
]
}
[dzhang@localhost terraform]$ cat terraform.tfstate.backup
{
“version”: 3,
“terraform_version”: “0.8.6”,
“serial”: 1,
“lineage”: “7da04b67-0d9d-4337-80a7-9ffe05753f83”,
“modules”: [
{
“path”: [
“root”
],
“outputs”: {},
“resources”: {
“aws_instance.terraform_linux”: {
“type”: “aws_instance”,
“depends_on”: [],
“primary”: {
“id”: “i-04e8f2757c43a5da8”,
“attributes”: {
“ami”: “ami-fe71759d”,
“associate_public_ip_address”: “true”,
“availability_zone”: “ap-southeast-2b”,
“disable_api_termination”: “false”,
“ebs_block_device.#”: “0”,
“ebs_optimized”: “false”,
“ephemeral_block_device.#”: “0”,
“iam_instance_profile”: “”,
“id”: “i-04e8f2757c43a5da8”,
“instance_state”: “running”,
“instance_type”: “t2.micro”,
“key_name”: “”,
“monitoring”: “false”,
“network_interface_id”: “eni-8aac01f1”,
“private_dns”: “ip-172-31-8-189.ap-southeast-2.compute.internal”,
“private_ip”: “172.31.8.189”,
“public_dns”: “ec2-52-62-134-247.ap-southeast-2.compute.amazonaws.com”,
“public_ip”: “52.62.134.247”,
“root_block_device.#”: “1”,
“root_block_device.0.delete_on_termination”: “true”,
“root_block_device.0.iops”: “100”,
“root_block_device.0.volume_size”: “8”,
“root_block_device.0.volume_type”: “gp2”,
“security_groups.#”: “0”,
“source_dest_check”: “true”,
“subnet_id”: “subnet-4d175128”,
“tags.%”: “0”,
“tenancy”: “default”,
“vpc_security_group_ids.#”: “1”,
“vpc_security_group_ids.3152370517”: “sg-c49495a1”
},
“meta”: {
“schema_version”: “1”
},
“tainted”: false
},
“deposed”: [],
“provider”: “”
}
},
“depends_on”: []
}
]
}

Install Terraform on Centos 7

Firstly, download the latest Terraform from https://www.terraform.io/downloads.html, then upload the zip package to your Centos 7.

  1. Make a new folder for Terraform and unzip the Terraform package
    [dzhang@localhost ~]$ cd terraform/
    [dzhang@localhost terraform]$ unzip ~/terraform_0.8.6_linux_amd64.zip
    Archive: /home/dzhang/terraform_0.8.6_linux_amd64.zip
    inflating: terraform[dzhang@localhost terraform]$ ls -al
    total 91324
    drwxrwxr-x. 2 dzhang dzhang 22 Feb 11 21:37 .
    drwx——. 20 dzhang dzhang 4096 Feb 11 21:36 ..
    -rwxrwxr-x. 1 dzhang dzhang 93508352 Feb 8 04:27 terraform
  2. Change the environment variable:
    [dzhang@localhost terraform]$ export PATH=/home/dzhang/terraform:$PATH
  3. Verify the installation
    [dzhang@localhost terraform]$ terraform –version
    Terraform v0.8.6

[dzhang@localhost terraform]$ terraform –help?
Usage: terraform [–version] [–help] [args]

The available commands for execution are listed below.
The most common, useful commands are shown first, followed by
less common or more advanced commands. If you’re just getting
started with Terraform, stick with the common commands. For the
other commands, please read the help and docs before usage.

Common commands:
apply Builds or changes infrastructure
console Interactive console for Terraform interpolations
destroy Destroy Terraform-managed infrastructure
fmt Rewrites config files to canonical format
get Download and install modules for the configuration
graph Create a visual graph of Terraform resources
import Import existing infrastructure into Terraform
init Initializes Terraform configuration from a module
output Read an output from a state file
plan Generate and show an execution plan
push Upload this Terraform module to Atlas to run
refresh Update local state file against real resources
remote Configure remote state storage
show Inspect Terraform state or plan
taint Manually mark a resource for recreation
untaint Manually unmark a resource as tainted
validate Validates the Terraform files
version Prints the Terraform version

All other commands:
debug Debug output management (experimental)
state Advanced state management