Last time we setup an AWS account. Today we’ll setup a Lightsail site using Terraform!
Pre-requisites:
- An AWS Account. See previous post for more details.
- AWS CLI.
- Terraform CLI.
There are three parts to this:
- Creating a Lightsail Instance
- Setting up Route53/DNS
- Setting up a remote S3 bucket state store for Terraform
We’ll walk through each section in detail, and all the code can be found here.
Creating a Lightsail Instance
First, let’s talk a about the directory structure of our Terraform:
├── README.md
├── main.tf
├── modules
│ └── lightsail
│ ├── main.tf
│ ├── outputs.tf
│ └── variables.tf
├── terraform.tfvars
└── variables.tf
At the top level, we have our main.tf
along with our variables files. The variables.tf
defines the vars we can set in terraform.tfvars
and reference in main.tf
. Note, I do not have terraform.tfvars
checked into my public repo because it’s specific to each environment.
Additionally, we create a modules
directory and add a lightsail
module within it. This is where our generic lightsail
definition will live defined by the main.tf
and variables.tf
. Later, we’ll want to use some of the data generated by this module in another module, so we need to specify our outputs.tf
.
Lightsail Module
├── README.md
├── main.tf
├── modules
│ └── lightsail
│ ├── main.tf
│ ├── outputs.tf
│ └── variables.tf
├── terraform.tfvars
└── variables.tf
Let’s work our way from the lightsail
module up to the top level main.tf
. First and foremost, we need to be able to pass down values into the module, so we need to define our variables.
# modules/lightsail/variables.tf
variable "lightsail_instance_name" {
type = string
description = "Name of the lightsail instance"
}
variable "lightsail_availability_zone" {
type = string
description = "Must be one of [ap-northeast-1{a,c,d}, ap-northeast-2{a,c}, ap-south-1{a,b}, ap-southeast-1{a,b,c}, ap-southeast-2{a,b,c}, ca-central-1{a,b}, eu-central-1{a,b,c}, eu-west-1{a,b,c}, eu-west-2{a,b,c}, eu-west-3{a,b,c}, us-east-1{a,b,c,d,e,f}, us-east-2{a,b,c}, us-west-2{a,b,c}]"
}
variable "lightsail_ip_address_type" {
type = string
description = "The IP address type of the Lightsail Instance. Valid Values: dualstack | ipv4"
default = "ipv4"
}
variable "lightsail_blueprint" {
type = string
description = "One of the blueprints listed by the AWS cli - aws lightsail get-blueprints"
default = "wordpress"
}
variable "lightsail_bundle_id" {
type = string
description = "The bundle id of the instance"
default = "micro_2_0"
}
variable "lightsail_static_ip_name" {
type = string
description = "Unique name for the lightsail static ip resource"
}
These are all options that the Terraform Lightsail provider will use to instantiate the Lightsail instance. Let’s go ahead and write the Terraform we need to build the instance and reference these variables.
# modules/lightsail/main.tf
resource "aws_lightsail_instance" "lightsail" {
name = var.lightsail_instance_name
availability_zone = var.lightsail_availability_zone
ip_address_type = var.lightsail_ip_address_type
blueprint_id = var.lightsail_blueprint
bundle_id = var.lightsail_bundle_id
add_on {
type = "AutoSnapshot"
snapshot_time = "06:00"
status = "Enabled"
}
}
resource "aws_lightsail_static_ip" "lightsail" {
name = var.lightsail_static_ip_name
}
resource "aws_lightsail_static_ip_attachment" "lightsail" {
static_ip_name = aws_lightsail_static_ip.lightsail.id
instance_name = aws_lightsail_instance.lightsail.id
}
We have three resources here:
aws_lightsail_instance
– this is the actual lightsail instance. You have to provide a name, an AZ, specify IPv4, IPv6, or both, a blueprint (machine size), and a bundle (pre-defined container like WordPress). Additionally this resource is going to enable auto snapshots at06:00 UTC
.aws_lightsail_static_ip
– creates astatic_ip
to be used with your Lightsail instance. This will become important as we tie together DNS and routing.aws_lightsail_static_ip_attachment
– attaches thestatic_ip
to thelightsail
instance.
Notice how there isn’t anything specific to your site in this module. It’s all generic and that’s good because we are going to start specifying details in the top level main.tf
.
Main Terraform
├── README.md
├── main.tf
├── modules
│ └── lightsail
│ ├── main.tf
│ ├── outputs.tf
│ └── variables.tf
├── terraform.tfvars
└── variables.tf
Let’s start with our variables.tf
definitions. These are values we’ll set in terraform.tfvars
and will be referenced by our main.tf
.
# ./variables.tf
variable "environment" {
type = string
description = "Unique name for your AWS environment (ie. dev)"
}
variable "user_role_arn" {
type = string
description = "The user_role_arn of the account being used by terraform"
}
variable "bucket_name" {
type = string
description = "The name of the bucket. Must be unique."
}
variable "lightsail_instance_name" {
type = string
description = "Name of the lightsail instance"
}
variable "lightsail_availability_zone" {
type = string
description = "Must be one of [ap-northeast-1{a,c,d}, ap-northeast-2{a,c}, ap-south-1{a,b}, ap-southeast-1{a,b,c}, ap-southeast-2{a,b,c}, ca-central-1{a,b}, eu-central-1{a,b,c}, eu-west-1{a,b,c}, eu-west-2{a,b,c}, eu-west-3{a,b,c}, us-east-1{a,b,c,d,e,f}, us-east-2{a,b,c}, us-west-2{a,b,c}]"
}
variable "lightsail_ip_address_type" {
type = string
description = "The IP address type of the Lightsail Instance. Valid Values: dualstack | ipv4"
default = "ipv4"
}
variable "lightsail_blueprint" {
type = string
description = "One of the blueprints listed by the AWS cli - aws lightsail get-blueprints"
default = "wordpress"
}
variable "lightsail_bundle_id" {
type = string
description = "The bundle id of the instance"
default = "micro_2_0"
}
variable "lightsail_static_ip_name" {
type = string
description = "Unique name for the lightsail static ip resource"
}
These map pretty close to the things I want to be able to customize from the Lightsail Terraform provider documentation. Now, let’s set them to our customized values in terraform.tfvars
. When we we run terraform
commands, it will automatically read the terraform.tfvars
file and used map the custom values to the variables we defined above. You could also use ENV vars or pass them in via the CLI using arguments, but automatically loading them with a *.tfvars
file is pretty simple. Go ahead and create a new file called terraform.tvars
adjacent to the root main.tf
module.
# ./terraform.tfvars
lightsail_instance_name = "fishbits_wordpress"
lightsail_availability_zone = "us-west-2b"
lightsail_blueprint = "wordpress"
lightsail_ip_address_type = "ipv4"
lightsail_bundle_id = "micro_2_0"
lightsail_static_ip_name = "fishbits_static_ip"
It’s time to create the Terraform that will use these values and reference our Lightsail module!
# ./main.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "4.64.0"
}
}
}
# Configure the AWS Provider
provider "aws" {
region = "us-west-2"
}
module "lightsail" {
source = "./modules/lightsail"
lightsail_instance_name = var.lightsail_instance_name
lightsail_availability_zone = var.lightsail_availability_zone
lightsail_ip_address_type = var.lightsail_ip_address_type
lightsail_blueprint = var.lightsail_blueprint
lightsail_bundle_id = var.lightsail_bundle_id
lightsail_static_ip_name = var.lightsail_static_ip_name
}
We have three stanzas defined above:
- Specify the required providers in the
terraform
block. - Configure the
aws
provider. - Import the Lightsail module and configure it.
That’s it! Now you can run terraform plan
and it will show you what Terraform want’s to create in your AWS account. This is just the beginning, it will setup your Lightsail instance with a WordPress container image and assign it a static IP. Navigate to the Lightsail Dashboard and you will see something similar to this:
Well done!
Cool! You can now hit your WordPress via the static IP assigned to the instance if you’d like. The documentation at AWS can walk you through how to retrieve your password and log in the first time much better than I can! Follow the directions here: Step 3 – log in and start using WordPress.
Next time, we’ll integrate Route53 and manage our domain.