Terraform with AWS and Azure
This is one of my first times with working with Terraform. But what is Terraform and why am I using it?
What is Terraform?
No no, we are not planning on terraforming Mars! Terraform is some sort of system where you can manage things like (cloud) infrastructure and services through… code! This is also called “Infrastructure as Code”.
But what does this mean in practice? It is quite simple, you basically write down the services you want to set up in code and then Terraform will do everything that’s needed to deploy them. I believe that you can compare it to something like a fancy API interface.
For example we can create a AWS lambda fairly easily through code.
resource "aws_lambda_function" "lambda" {
function_name = "amazon-budget-lambda"
filename = "${data.archive_file.zip.output_path}"
source_code_hash = "${data.archive_file.zip.output_base64sha256}"
role = "${aws_iam_role.iam_for_lambda.arn}"
handler = "myhandler.handler"
runtime = "nodejs12.x"
}
Terraform is just more than this! Terraform also keeps track of the “state” of the infrastructure you defined in code. What does this mean? It might be a bit hard to understand at first but basically…
If you define to create service A and service B through Terraform then Terraform will know that it created service A and service B and that they exist. Let’s say that you now remove service B because it is not used anymore but you add a new service C to your infra (through Terraform(!))… Terraform will now intelligently remove service B and create service C and save their states, it does not need to recreate service A because it already exists. One other benefit is that you can delete anything that has been deployed by Terraform fairly easily — think about creating temporary services on a cloud infrastructure for CI/CD tests!
But why Terraform?
As addressed before, Terraform can be used to define what you need in terms of services through code which is pretty neat. There is already support for providers such as AWS and Azure which comes in handy for us.
My plan is to define the infra you need in code (for Terraform), hook it up to a CI/CD environment and run some automated tests with it where the environment is destroyed afterwards so we do not leave any unwanted services running!
What I setup on Terraform
I was thinking about what I could easily setup on Terraform and what is something I could test. This might seem a bit underwhelming at first but personally Terraform feels something that takes time to set up well as in that the learning curve is a bit ‘steep’. There is also IAM/access rules and different services (especially on Azure) you have to take into account and all the overhead in terms of configuring every tiny detail.
What is being deployed to AWS & Azure:
RDS on AWS, MariaDB on Azure
These configurations were pretty straight forward because of the excellent docs from Terraform :)! The databases are going to be used to store & read some data from in future tests.
Azure:
# https://www.terraform.io/docs/providers/azurerm/r/mariadb_database.html# In azure we have to define both the server & database because they are two complete different resources
resource "azurerm_resource_group" "tf" {
name = "Terraform"
location = "westeurope"
}
resource "azurerm_mariadb_server" "tf" {
name = "mariadb-svr"
location = azurerm_resource_group.tf.location
resource_group_name = azurerm_resource_group.tf.name
sku_name = "B_Gen5_2"
storage_profile {
storage_mb = 51200
backup_retention_days = 7
geo_redundant_backup = "Disabled"
}
administrator_login = "admin"
administrator_login_password = "password"
version = "10.2"
ssl_enforcement = "Enabled"
}
resource "azurerm_mariadb_database" "tf" {
name = "mariadb_database"
resource_group_name = azurerm_resource_group.tf.name
server_name = azurerm_mariadb_server.tf.name
charset = "utf8"
collation = "utf8_general_ci"
}
AWS:
# https://www.terraform.io/docs/providers/aws/r/db_instance.html
resource "aws_db_instance" "tf" {
allocated_storage = 20
storage_type = "gp2"
engine = "mysql"
engine_version = "5.7"
instance_class = "db.t2.micro"
name = "tf"
username = "admin"
password = "password"
parameter_group_name = "default.mysql5.7"
}
Lambda on AWS, Function on Azure
Enter the world of serverless! The goal of this is to deploy two different node.js serverless functions that do something basic — in our case just outputting ‘1+1’. The reason why we need two different node.js code bases is that both Lambda and Functions work on a slightly different way. Not super ideal but this is fine for our use case for now
Azure:
# https://www.terraform.io/docs/providers/azurerm/r/function_app.html
resource "azurerm_resource_group" "tf" {
name = "Terraform"
location = "westeurope"
}
resource "azurerm_storage_account" "tf" {
name = "Terraform"
resource_group_name = azurerm_resource_group.tf.name
location = azurerm_resource_group.tf.location
account_tier = "Standard"
account_replication_type = "LRS"
}
resource "azurerm_app_service_plan" "tf" {
name = "Terraform"
location = azurerm_resource_group.tf.location
resource_group_name = azurerm_resource_group.tf.name
kind = "FunctionApp"
sku {
tier = "Dynamic"
size = "Y1"
}
}
resource "azurerm_function_app" "tf" {
name = "Terraform"
location = azurerm_resource_group.tf.location
resource_group_name = azurerm_resource_group.tf.name
app_service_plan_id = azurerm_app_service_plan.tf.id
storage_connection_string = azurerm_storage_account.tf.primary_connection_string
}
AWS:
# https://www.terraform.io/docs/providers/aws/r/lambda_function.html
resource "aws_iam_role" "tf" {
name = "Terraform"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
resource "aws_lambda_function" "tf" {
filename = "tf.zip"
function_name = "Terraform"
role = "${aws_iam_role.iam_for_lambda.arn}"
handler = "exports.index"
source_code_hash = "${filebase64sha256("tf.zip")}"
runtime = "nodejs12.x"
environment {
variables = {
foo = "bar"
}
}
}
So, what’s next?
I tested those little snippets/examples from the docs, set them up with git, configured Terraform (Cloud), did some test runs, adjusted everything to my liking (and fixed the bugs) and then I merged every part of it in one big things… which took me some time and was rather adventurous but in the end it worked!
The idea of Terraform works so that is great. Next up I am going to look into Terraform’s REST API in order to trigger and manage Terraform (Cloud) functionality from within a CI/CD pipeline! Exciting!