Terraform on Flexible Engine
Introduction
This articles present how to build you Flexible Engine cloud infrastructure with code (IaC) using Terraform.
Infrastructure as Code
Infrastructure as Code (IaC) is a powerful paradigm for envisioning, configuring, and deploying an IT environment. In the IaC model, your target infrastructure is not constructed manually but is described in a file using a specialized language. An automation tool then reads the file and builds the system to the user’s specifications. The efficiency and versatility of IaC have made it a popular complement to DevOps and the cloud computing revolution.
Flexible Engine and Terraform
Hashicorp Terraform is the most popular open–source infrastructure as code software tool that enables you to safely and predictably create, change, and improve infrastructure.
Flexible Engine is certified by Hashicorp and the Terraform Common Vendor List. It means that Terraform can be used to create, manage and update infrastructure resources such as physical machines, virtual machines, network switches, containers, etc. Almost any type of infrastructure can be represented as a Terraform resource on Flexible Engine.
Terraform providers
As Flexible Engine is an OpenStack based solution , you can use the two following providers to deploy your infrastructure with Terraform :
We recommend to use the Flexible Engine provider if you wish to deploy advanced resources as a Relational Database, Managed Kubernetes cluster, Big Data services and many more that are not part of Openstack. But for interoperability with other Openstack environment you can also use the second provider.
You can also access our Github dedicated to the Flexible Engine provider here :
https://github.com/FlexibleEngineCloud/terraform-provider-flexibleengine
About Terraform
Terraform State (Terraform docs)
“Terraform must store state about your managed infrastructure and configuration. This state is used by Terraform to map real world resources to your configuration, keep track of metadata, and to improve performance for large infrastructures.
This state is stored by default in a local file named “terraform.tfstate”, but it can also be stored remotely, which works better in a team environment.
Terraform uses this local state to create plans and make changes to your infrastructure. Prior to any operation, Terraform does a refresh to update the state with the real infrastructure.
The primary purpose of Terraform state is to store bindings between objects in a remote system and resource instances declared in your configuration. When Terraform creates a remote object in response to a change of configuration, it will record the identity of that remote object against a particular resource instance, and then potentially update or delete that object in response to future configuration changes…”
Terraform Cycle
In Terraform the life cycle of your resources consists of the following steps : init, plan, apply and destroy.
- Terraform init initializes the working directory which contains all the configuration files
- Terraform plan is used to create an execution plan to achieve the desired state of the infrastructure. Changes to the configuration files are made in order to reach the desired state.
- Terraform apply then performs the changes to the infrastructure as defined in the plan, and the infrastructure reaches the desired state.
- Terraform destroy is used to delete all old infrastructure resources, which are marked as corrupt after the apply phase.
Install Terraform
The primary distribution packages for Terraform are .zip
archives containing single executable files that you can extract anywhere on your system. However, for easier integration with configuration management tools and other systematic system configuration strategies, Terraform also offer package repositories for Debian, Ubuntu, Red Hat Enterprise Linux, CentOS, or Fedora systems, which allow you to install Terraform using the APT or YUM command.
Build Infrastructure
Prerequisites
To follow this tutorial you will need:
- The Terraform CLI (0.14.9+) installed.
- A Flexible Engine account
- Your FE credentials or you can create a new Access Key / Secret Key (How to create AK/SK : page 53).
Write Configuration
The set of files used to describe infrastructure in Terraform is known as a Terraform configuration. You will write your first configuration to define a single FE ECS (VM) instance.
Each Terraform configuration must be in its own working directory. Create a directory for your configuration.
$ mkdir learn-FE-terraform
Change into the directory
$ cd learn-FE-terraform
Provider
When using the Flexible Engine Cloud Provider with Terraform 0.13 and later, the recommended approach is to declare Provider versions in the root module Terraform configuration, using a required_providers
block as per the following example. For previous versions, please continue to pin the version within the provider block.
To start we need to create a provider.tf file to defined which Terraform provider to use and how to authenticate on FE.
$ touch provider.tf
Then edit this file with your preferred text editor :
- Add FlexibleEngineCloud/flexibleengine to your
required_providers
.# provider.tf
terraform {
required_version = “>= 0.13”
required_providers {
flexibleengine = {
source = “FlexibleEngineCloud/flexibleengine”
version = “>= 1.20.0”
}
}
} - Run
terraform init -upgrade
to download/upgrade the provider.
Authentication
Before deploying your first resources you need to add credentials to your provider. You can authenticate with two methods
- AK/SK Authenticate
# provider.tf
# Configure the FlexibleEngine Provider with AK/SK
provider "flexibleengine" {
access_key = "access key"
secret_key = "secret key"
domain_name = "domain name"
region = "eu-west-0"
}
- Username/Password Authenticate
# provider.tf
# Configure the FlexibleEngine Provider with Username/Password
provider "flexibleengine" {
user_name = "user name"
password = "password"
domain_name = "domain name"
region = "eu-west-0"
}
Create resources
First we need to go through the Console to gather the following ID’s :
- network ID
- security_groups ID
Navigate to the FE Network Console in the web UI, select an existing VPC in your region, select a subnet and copy the ID to your clipboard add it to the following configuration of your ECS. Then do the same for the security group ID (vpc_security_group_ids
).
# main.tf
# Create an Elastic Cloud Server resource
resource "flexibleengine_compute_instance_v2" "myinstance" {
name = "myinstance"
image_name = "OBS Ubuntu 20.04"
flavor_id = "s3.large.2"
key_pair = "my_KeyPair"
security_groups = ["a85055fa-857a-4cfd-b317-c4091577aa73"]
network {
uuid = "37c7ac09-e9e4-4527-b3e4-64f04841f9d3"
}
}
Initialize directory
When you create a new configuration — or check out an existing configuration from version control — you need to initialize the directory with terraform init
.
Initializing a configuration directory downloads and installs the providers defined in the configuration, which in this case is the flexibleengine
provider.
Initialize the directory.
$ terraform init
Initializing the backend...
Initializing provider plugins...
- Finding flexibleenginecloud/flexibleengine versions matching ">= 1.20.0"...
- Installing flexibleenginecloud/flexibleengine v1.27.0...
- Installed flexibleenginecloud/flexibleengine v1.27.0 (self-signed, key ID 0D0EDE6AC300F5EE)
Partner and community providers are signed by their developers.
If you'd like to know more about provider signing, you can read about it here:
https://www.terraform.io/docs/cli/plugins/signing.html
Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
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.
Terraform downloads the flexibleengine
provider and installs it in a hidden subdirectory of your current working directory, named .terraform
. The terraform init
command prints out which version of the provider was installed. Terraform also creates a lock file named .terraform.lock.hcl
which specifies the exact provider versions used, so that you can control when you want to update the providers used for your project.
Format and validate the configuration
Before applying your configuration you can verify what will be deployed and also make sure your configuration is syntactically valid and internally consistent by using the terraform plan
command.
$ terraform plan
Create infrastructure
Apply the configuration now with the terraform apply
command. Terraform will print output similar to what is shown below. We have truncated some of the output to save space.
$ terraform apply
The output format is similar to the diff format generated by tools such as Git. The output has a +
next to flexibleengine_compute_instance_v2.myinstance
, meaning that Terraform will create this resource.
Terraform will now pause and wait for your approval before proceeding. If anything in the plan seems incorrect or dangerous, it is safe to abort here with no changes made to your infrastructure.
In this case the plan is acceptable, so type yes
at the confirmation prompt to proceed. Executing the plan will take a few minutes since Terraform waits for the ECS instance to become available.
Plan: 1 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.
Enter a value: yes
flexibleengine_compute_instance_v2.myinstance: Creating...
flexibleengine_compute_instance_v2.myinstance: Still creating... [10s elapsed]
flexibleengine_compute_instance_v2.myinstance: Still creating... [20s elapsed]
flexibleengine_compute_instance_v2.myinstance: Creation complete after 27s [id=886eb986-c5f9-4bf3-8b64-4100ee671097]
Inspect state
When you applied your configuration, Terraform wrote data into a file called terraform.tfstate
. Terraform stores the IDs and properties of the resources it manages in this file, so that it can update or destroy those resources going forward.
The Terraform state file is the only way Terraform can track which resources it manages, and often contains sensitive information, so you must store your state file securely and restrict access to only trusted team members who need to manage your infrastructure. In production, we recommend storing your state remotely with Flexible Engine Object Storage Service.
Inspect the current state using terraform show
.
$ terraform show
# flexibleengine_compute_instance_v2.myinstance:
resource "flexibleengine_compute_instance_v2" "myinstance" {
access_ip_v4 = "192.168.0.38"
all_metadata = {
"charging_mode" = "0"
"image_name" = "OBS Ubuntu 20.04"
"metering.image_id" = "c2280a5f-159f-4489-a107-7cf0c7efdb21"
"metering.imagetype" = "gold"
"metering.resourcespeccode" = "s3.large.2.linux"
"os_bit" = "64"
"vpc_id" = "ff8ef2c5-8eec-4853-9680-425bf73f8dda"
}
auto_recovery = true
availability_zone = "eu-west-0b"
flavor_id = "s3.large.2"
flavor_name = "s3.large.2"
id = "886eb986-c5f9-4bf3-8b64-4100ee671097"
image_id = "c2280a5f-159f-4489-a107-7cf0c7efdb21"
image_name = "OBS Ubuntu 20.04"
key_pair = "my_KeyPair"
name = "myinstance"
region = "eu-west-0"
security_groups = [
"a85055fa-857a-4cfd-b317-c4091577aa73",
]
status = "ACTIVE"
stop_before_destroy = false
system_disk_id = "2167f49f-3d79-4440-8bba-88e099b64bf3"
volume_attached = [
{
boot_index = 0
pci_address = ""
size = 40
type = "SATA"
uuid = "2167f49f-3d79-4440-8bba-88e099b64bf3"
},
]
network {
access_network = false
fixed_ip_v4 = "192.168.0.38"
mac = "fa:16:3e:72:07:96"
port = "a1f53b8b-6af8-4026-9f64-5a60bd88b54a"
uuid = "37c7ac09-e9e4-4527-b3e4-64f04841f9d3"
}
}
Manually Managing State
Terraform has a built-in command called terraform state
for advanced state management. Use the list
subcommand to list of the resources in your project’s state.
$ terraform state list
flexibleengine_compute_instance_v2.myinstance
Change Infrastructure
Now update the image_name
of your instance. Change the flexibleengine_compute_instance_v2.myinstance
resource under the provider block in main.tf
by replacing the current IMS image name with a new one.
# main.tf
# Create an Elastic Cloud Server resource
resource "flexibleengine_compute_instance_v2" "myinstance" {
name = "myinstance"
image_name = "OBS Ubuntu 18.04"
flavor_id = "s3.large.2"
key_pair = "my_KeyPair"
security_groups = ["a85055fa-857a-4cfd-b317-c4091577aa73"]
network {
uuid = "37c7ac09-e9e4-4527-b3e4-64f04841f9d3"
}
}
This update changes the IMS to an Ubuntu 18.04 AMI. The FE provider knows that it cannot change the IMS of an instance after it has been created, so Terraform will destroy the old instance and create a new one. You can verify what will be changed using terraform plan
command :
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement
Terraform will perform the following actions:
# flexibleengine_compute_instance_v2.myinstance must be replaced
~ image_name = "OBS Ubuntu 20.04" -> "OBS Ubuntu 18.04" # forces replacement
Apply Changes
After verifying the configuration, run terraform apply
again to see how Terraform will apply this change to the existing resources.
Plan: 1 to add, 0 to change, 1 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
flexibleengine_compute_instance_v2.myinstance: Destroying... [id=886eb986-c5f9-4bf3-8b64-4100ee671097]
flexibleengine_compute_instance_v2.myinstance: Still destroying... [id=886eb986-c5f9-4bf3-8b64-4100ee671097, 10s elapsed]
flexibleengine_compute_instance_v2.myinstance: Destruction complete after 11s
flexibleengine_compute_instance_v2.myinstance: Creating...
flexibleengine_compute_instance_v2.myinstance: Still creating... [10s elapsed]
flexibleengine_compute_instance_v2.myinstance: Still creating... [20s elapsed]
flexibleengine_compute_instance_v2.myinstance: Creation complete after 20s [id=6994d7ac-1519-403b-8c1c-680afa3f1fd6]
Apply complete! Resources: 1 added, 0 changed, 1 destroyed.
As indicated by the execution plan, Terraform first destroyed the existing instance and then created a new one in its place. You can use terraform show
again to have Terraform print out the new values associated with this instance.
Destroy Infrastructure
You have now created and updated an ECS instance on FE with Terraform. Now, you will use Terraform to destroy this infrastructure.
Once you no longer need infrastructure, you may want to destroy it to reduce your security exposure and costs. For example, you may remove a production environment from service, or manage short-lived environments like build or testing systems. In addition to building and modifying infrastructure, Terraform can destroy or recreate the infrastructure it manages.
Destroy
The terraform destroy
command terminates resources managed by your Terraform project. This command is the inverse of terraform apply
in that it terminates all the resources specified in your Terraform state. It does not destroy resources running elsewhere that are not managed by the current Terraform project.
Destroy the resources you created.
$ terraform destroy
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
- destroy
Terraform will perform the following actions:
# flexibleengine_compute_instance_v2.myinstance will be destroyed
- resource "flexibleengine_compute_instance_v2" "myinstance" {
- access_ip_v4 = "192.168.0.40" -> null
- all_metadata = {
- "charging_mode" = "0"
- "image_name" = "OBS Ubuntu 18.04"
- "metering.image_id" = "0249222b-c9be-419b-a953-f47e91c3fc81"
- "metering.imagetype" = "gold"
- "metering.resourcespeccode" = "s3.large.2.linux"
- "os_bit" = "64"
- "vpc_id" = "ff8ef2c5-8eec-4853-9680-425bf73f8dda"
} -> null
- auto_recovery = true -> null
- availability_zone = "eu-west-0b" -> null
- flavor_id = "s3.large.2" -> null
- flavor_name = "s3.large.2" -> null
- id = "6994d7ac-1519-403b-8c1c-680afa3f1fd6" -> null
- image_id = "0249222b-c9be-419b-a953-f47e91c3fc81" -> null
- image_name = "OBS Ubuntu 18.04" -> null
- key_pair = "KeyPair-ae7a" -> null
- metadata = {} -> null
- name = "myinstance" -> null
- region = "eu-west-0" -> null
- security_groups = [
- "sg-front",
] -> null
- status = "ACTIVE" -> null
- stop_before_destroy = false -> null
- system_disk_id = "b1f22f7b-7c0c-484e-a3bd-6d748ecbbc78" -> null
- tags = {} -> null
- volume_attached = [
- {
- boot_index = 0
- pci_address = ""
- size = 40
- type = "SATA"
- uuid = "b1f22f7b-7c0c-484e-a3bd-6d748ecbbc78"
},
] -> null
- network {
- access_network = false -> null
- fixed_ip_v4 = "192.168.0.40" -> null
- mac = "fa:16:3e:36:d9:49" -> null
- port = "c896b636-d8ea-4cdd-9087-c948cd12871e" -> null
- uuid = "37c7ac09-e9e4-4527-b3e4-64f04841f9d3" -> null
}
}
The -
prefix indicates that the instance will be destroyed. As with apply, Terraform shows its execution plan and waits for approval before making any changes.
Answer yes
to execute this plan and destroy the infrastructure.
Enter a value: yes
flexibleengine_compute_instance_v2.myinstance: Destroying... [id=6994d7ac-1519-403b-8c1c-680afa3f1fd6]
flexibleengine_compute_instance_v2.myinstance: Still destroying... [id=6994d7ac-1519-403b-8c1c-680afa3f1fd6, 10s elapsed]
flexibleengine_compute_instance_v2.myinstance: Destruction complete after 10s
Destroy complete! Resources: 1 destroyed.
Just like with apply
, Terraform determines the order to destroy your resources. In this case, Terraform identified a single instance with no other dependencies, so it destroyed the instance. In more complicated cases with multiple resources, Terraform will destroy them in a suitable order to respect dependencies.