Skip to content

Commit e9b360b

Browse files
authored
Merge pull request #29 from gruntwork-io/instance-type
Add instance-type module to help deal with t2.micro / t3.micro issue
2 parents faeffd0 + e72fa36 commit e9b360b

File tree

10 files changed

+202
-1
lines changed

10 files changed

+202
-1
lines changed

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
curl -Ls https://raw.githubusercontent.com/gruntwork-io/gruntwork-installer/master/bootstrap-gruntwork-installer.sh | bash /dev/stdin --version v0.0.21
1616
gruntwork-install --module-name "gruntwork-module-circleci-helpers" --repo "https://github.com/gruntwork-io/module-ci" --tag "v0.13.3"
1717
gruntwork-install --binary-name "terratest_log_parser" --repo "https://github.com/gruntwork-io/terratest" --tag "v0.13.20"
18-
configure-environment-for-gruntwork-module --circle-ci-2 --use-go-dep --go-src-path test --terragrunt-version NONE --terraform-version 0.12.0
18+
configure-environment-for-gruntwork-module --circle-ci-2 --use-go-dep --go-src-path test --terragrunt-version NONE --terraform-version 0.12.28
1919
2020
- run:
2121
name: run tests (with python2)

examples/instance-type/README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Instance types example
2+
3+
This folder shows examples of how to use the [instance-type module](/modules/instance-type) to pick an instance type
4+
that's available in all Availability Zones in the current AWS region.
5+
6+
7+
8+
9+
## How do you run these examples?
10+
11+
1. Install [Terraform](https://www.terraform.io/).
12+
1. `terraform init`.
13+
1. `terraform apply`.
14+
15+
16+

examples/instance-type/main.tf

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
provider "aws" {
2+
region = var.aws_region
3+
}
4+
5+
# ----------------------------------------------------------------------------------------------------------------------
6+
# FIGURE OUT WHAT INSTANCE TYPE IS AVAILABLE IN ALL AZS IN THE CURRENT AWS REGION
7+
# ----------------------------------------------------------------------------------------------------------------------
8+
9+
module "instance_types" {
10+
source = "../../modules/instance-type"
11+
12+
instance_types = var.instance_types
13+
}
14+
15+
# ----------------------------------------------------------------------------------------------------------------------
16+
# USE THAT INSTANCE TYPE TO LAUNCH AN EC2 INSTANCE
17+
# ----------------------------------------------------------------------------------------------------------------------
18+
19+
resource "aws_instance" "example" {
20+
ami = data.aws_ami.ubuntu.id
21+
instance_type = module.instance_types.recommended_instance_type
22+
23+
tags = {
24+
Name = "instance-type-example"
25+
}
26+
}
27+
28+
# ----------------------------------------------------------------------------------------------------------------------
29+
# FETCH THE ID OF AN UBUNTU AMI IN THE CURRENT REGION
30+
# ----------------------------------------------------------------------------------------------------------------------
31+
32+
data "aws_ami" "ubuntu" {
33+
most_recent = true
34+
owners = ["099720109477"] # Canonical
35+
36+
filter {
37+
name = "virtualization-type"
38+
values = ["hvm"]
39+
}
40+
41+
filter {
42+
name = "architecture"
43+
values = ["x86_64"]
44+
}
45+
46+
filter {
47+
name = "image-type"
48+
values = ["machine"]
49+
}
50+
51+
filter {
52+
name = "name"
53+
values = ["ubuntu/images/hvm-ssd/ubuntu-xenial-16.04-amd64-server-*"]
54+
}
55+
}

examples/instance-type/outputs.tf

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
output "instance_type_map" {
2+
description = "A map where the keys are the instance types in var.instance_types and the values are true or false, depending on whether every AZ in the current region contains this instance type."
3+
value = module.instance_types.instance_type_map
4+
}
5+
6+
output "recommended_instance_type" {
7+
description = "The recommended instance type to use in this AWS region. This will be the first instance type in var.instance_types which is available in all AZs in this region."
8+
value = module.instance_types.recommended_instance_type
9+
}

examples/instance-type/variables.tf

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
variable "aws_region" {
2+
description = "The AWS region to run this code in"
3+
type = string
4+
default = "eu-west-1"
5+
}
6+
7+
variable "instance_types" {
8+
description = "A list of instance types to look up in the current AWS region."
9+
type = list(string)
10+
default = ["t3.micro", "t2.micro"]
11+
}

modules/instance-type/README.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Instance Type
2+
3+
This is a module that can be used to look up a list of EC2 instance types and determine which of them is available in
4+
all Availability Zones (AZs) in the current AWS Region. This is useful because certain instance types, such as
5+
`t2.micro` are not available in some of the newer AZs, while `t3.micro` is not available in some of the older AZs, and
6+
if you have code that needs to run on a "small" instance across all AZs in many different regions, you can use this
7+
module to automatically figure out which instance type you should use.
8+
9+
10+
11+
12+
13+
## Example code
14+
15+
See the [instance-type example](/examples/instance-type) for working sample code.
16+
17+
18+
19+
20+
## Usage
21+
22+
Use the module in your Terraform code, replacing `<VERSION>` with the latest version from the [releases
23+
page](https://github.com/gruntwork-io/package-terraform-utilities/releases):
24+
25+
```hcl
26+
module "path" {
27+
source = "git::[email protected]:gruntwork-io/package-terraform-utilities.git//modules/instance-type?ref=<VERSION>"
28+
29+
instance_types = ["t2.micro", "t3.micro"]
30+
}
31+
```
32+
33+
The arguments to pass are:
34+
35+
* `instance_types`: A list of instance types to look up in the current AWS region. We recommend putting them in order
36+
of preference, as the recommended_instance_type output variable will contain the first instance type from this list
37+
that is available in all AZs.
38+
39+
When you run `apply`, the `recommended_instance_type` output variable will contain the recommended instance type to
40+
use. This will be the first instance type from your `instance_types` input that is available in all AZs in the current
41+
region. If no instance type is available in all AZs, you'll get an error.
42+
43+
For example, as of July, 2020, if you run `apply` on the code above in `eu-west-1`, the `recommended_instance_type`
44+
will be `t2.micro`, as that's available in all AZs in `eu-west-1`. However, if you run the same code in
45+
`ap-northeast-2`, the `recommended_instance_type` will be `t3.micro`, as `t2.micro` is only available in 2 of the 4 AZs.

modules/instance-type/main.tf

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
data "aws_ec2_instance_type_offerings" "offerings" {
2+
for_each = toset(data.aws_availability_zones.available.names)
3+
4+
filter {
5+
name = "instance-type"
6+
values = var.instance_types
7+
}
8+
9+
filter {
10+
name = "location"
11+
values = [each.key]
12+
}
13+
14+
location_type = "availability-zone"
15+
}
16+
17+
data "aws_availability_zones" "available" {
18+
state = "available"
19+
}

modules/instance-type/outputs.tf

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
locals {
2+
instance_type_map = { for instance_type in var.instance_types : instance_type => (
3+
! contains([for az, offerings in data.aws_ec2_instance_type_offerings.offerings : contains(offerings.instance_types, instance_type)], false))
4+
}
5+
6+
recommended_instance_type = [for instance_type in var.instance_types : instance_type if local.instance_type_map[instance_type]][0]
7+
}
8+
9+
output "instance_type_map" {
10+
description = "A map where the keys are the instance types in var.instance_types and the values are true or false, depending on whether every AZ in the current region contains this instance type."
11+
value = local.instance_type_map
12+
}
13+
14+
output "recommended_instance_type" {
15+
description = "The recommended instance type to use in this AWS region. This will be the first instance type in var.instance_types which is available in all AZs in this region."
16+
value = local.recommended_instance_type
17+
}

modules/instance-type/variables.tf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
variable "instance_types" {
2+
description = "A list of instance types to look up in the current AWS region. We recommend putting them in order of preference, as the recommended_instance_type output variable will contain the first instance type from this list that is available in all AZs."
3+
type = list(string)
4+
}

test/instance_type_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package test
2+
3+
import (
4+
"github.com/gruntwork-io/terratest/modules/aws"
5+
"github.com/gruntwork-io/terratest/modules/terraform"
6+
"testing"
7+
)
8+
9+
func TestInstanceType(t *testing.T) {
10+
t.Parallel()
11+
12+
awsRegion := aws.GetRandomRegion(t, nil, nil)
13+
14+
terratestOptions := &terraform.Options{
15+
TerraformDir: "../examples/instance-type",
16+
Vars: map[string]interface{}{
17+
"aws_region": awsRegion,
18+
},
19+
}
20+
defer terraform.Destroy(t, terratestOptions)
21+
22+
// We only need to run 'apply' for this test. If the instance launches successfully, it's because the code picked
23+
// the right instance type to use for the current region (note: we pick the region at random).
24+
terraform.InitAndApply(t, terratestOptions)
25+
}

0 commit comments

Comments
 (0)