AWS

Table of Contents

Reference

Resources

awseip

resource "aws_eip" "main" {
  count    = "${var.num_hosts_eip}"
  instance = "${aws_instance.main.*.id}"

  # terraform tries to modify all eips when `count` changes.
  # This is a workaround for this.
  # SEE: https://github.com/hashicorp/terraform/issues/4944#issuecomment-248884637
  lifecycle {
    ignore_changes = ["instance"]
  }
}

awselasticsearchdomain

resource "aws_elasticsearch_domain" "main" {
  domain_name           = "my-domain"
  elasticsearch_version = "5.1"

  cluster_config {
    instance_type  = "m3.medium.elasticsearch"
    instance_count = 3
  }

  ebs_options {
    ebs_enabled = true
    volume_type = "gp2"
    volume_size = "30" # in GBs per data instance
  }
}

awselb

resource "aws_elb" "endpoint" {
  name            = "my-elb"
  subnets         = ["${var.subnet_ids}"]
  security_groups = ["${var.security_groups}"]
  instances       = ["${aws_instance.cluster.*.id}"]

  internal = true

  listener {
    instance_port     = 8080
    instance_protocol = "http"
    lb_port           = 80
    lb_protocol       = "http"
  }

  health_check {
    target              = "HTTP:8080/"
    timeout             = 5
    interval            = 30
    unhealthy_threshold = 2
    healthy_threshold   = 10
  }

  tags {
    Cluster = "${var.cluster}"
  }
}

awsiamrole

resource "aws_iam_role" "main" {
  name = "${var.name}"

  assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
  {
    "Effect": "Allow",
    "Principal": {
      "Service": "lambda.amazonaws.com"
    },
    "Action": "sts:AssumeRole"
  }
]
}
EOF
}

resource "aws_iam_role_policy" "kinesis" {
  name = "${aws_iam_role.main.name}-kinesis"
  role = "${aws_iam_role.main.id}"

  policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
  {
    "Effect": "Allow",
    "Action": [
      "kinesis:DescribeStream",
      "kinesis:GetRecords",
      "kinesis:GetShardIterator",
      "kinesis:ListStreams",
      "logs:CreateLogGroup",
      "logs:CreateLogStream",
      "logs:PutLogEvents"
    ],
    "Resource": "${var.kinesis_arn}"
  }
]
}
EOF
}

resource "aws_iam_role_policy" "cloudwatch" {
  name = "${aws_iam_role.main.name}-cloudwatch"
  role = "${aws_iam_role.main.id}"

  policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
  {
    "Effect": "Allow",
    "Action": [
      "logs:CreateLogGroup",
      "logs:CreateLogStream",
      "logs:PutLogEvents"
    ],
    "Resource": "*"
  }
]
}
EOF
}

# Needs a few seconds to replicate your new role through all regions.
# SEE: http://stackoverflow.com/questions/37503075/invalidparametervalueexception-the-role-defined-for-the-function-cannot-be-assu
resource "null_resource" "sleep" {
  triggers {
    role = "${aws_iam_role.main.arn}"
  }

  provisioner "local-exec" {
    command = "sleep 15"
  }
}

awslambdafunction

data "archive_file" "code" {
  type        = "zip"
  source_file = "${path.module}/main.py"
  output_path = "${path.module}/lambda.zip"
}

resource "aws_lambda_function" "main" {
  function_name    = "lambda-kinesis"
  filename         = "${data.archive_file.code.output_path}"
  source_code_hash = "${data.archive_file.code.output_base64sha256}"
  handler          = "main.handle"
  runtime          = "python2.7"
  role             = "${var.role_arn}"
  memory_size      = 128
  timeout          = 10

  environment {
    variables {
      MY_ENV = "test"
    }
  }
}

resource "aws_lambda_event_source_mapping" "kinesis" {
  function_name     = "${aws_lambda_function.main.arn}"
  event_source_arn  = "${var.kinesis_arn}"
  batch_size        = 1000
  starting_position = "LATEST"
}

awsrdscluster

awsrdsclusterinstance

resource "aws_rds_cluster" "cluster" {
  db_cluster_parameter_group_name = "default.aurora5.6"
  cluster_identifier              = "mydb"

  db_subnet_group_name   = "default-vpc-abcd1234"
  vpc_security_group_ids = ["${data.terraform_remote_state.vpc.sg_default}"]
  availability_zones     = ["ap-northeast-1a", "ap-northeast-1c"]

  skip_final_snapshot = true

  database_name   = "mydb"
  master_username = "root"
  master_password = "1234"
}

resource "aws_rds_cluster_instance" "node" {
  count          = 2
  instance_class = "db.t2.medium"
  identifier     = "mydb-${count.index}"

  cluster_identifier   = "${aws_rds_cluster.cluster.id}"
  db_subnet_group_name = "default-vpc-abcd1234"
}

awsdefaultsecuritygroups

When creating a VPC, AWS automatically creates a default security group that can not be deleted To manage it with Terraform, Terraform provides a dedicated resource type. Before you make any changes, be sure to read the link below.

resource "aws_default_security_group" "default" {
  vpc_id = "${aws_vpc.main.id}"

  ingress {
    from_port       = 0
    to_port         = 0
    protocol        = "-1"
    security_groups = []
    self            = true
  }

  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"]
    ipv6_cidr_blocks = ["::/0"]
  }
}

awssecuritygroup

resource "aws_security_group" "default" {
  name   = "${var.project}-default"
  vpc_id = "${aws_vpc.main.id}"

  ingress {
    from_port       = 0
    to_port         = 0
    protocol        = "-1" # all
    security_groups = []
    self            = true
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1" # all
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_security_group" "icmp" {
  name   = "${var.project}-icmp"
  vpc_id = "${aws_vpc.main.id}"

  ingress {
    from_port   = -1 # all
    to_port     = -1
    protocol    = "icmp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_security_group" "ping" {
  name   = "${var.project}-ping"
  vpc_id = "${aws_vpc.main.id}"

  ingress {
    from_port   = 8 # echo
    to_port     = -1
    protocol    = "icmp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_security_group" "ssh" {
  name   = "${var.project}-ssh"
  vpc_id = "${aws_vpc.main.id}"

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

Data Sources

awsami

data "aws_ami" "ubuntu1604" {
  most_recent = true

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-xenial-16.04-amd64-server-*"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }

  owners = ["099720109477"] # Canonical
}
data "aws_ami" "my_image1" {
  most_recent = true

  filter {
    name   = "state"
    values = ["available"]
  }

  filter {
    name   = "tag:Name"
    values = ["My Image"]
  }
}
data "aws_ami" "my_image2" {
  most_recent = true

  filter {
    name   = "state"
    values = ["available"]
  }

  filter {
    name   = "name"
    values = ["my-image-*"]
  }
}

How-to

Set up a VPC

resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"

  tags {
    Name = "main"
  }
}

resource "aws_subnet" "main_public_a" {
  vpc_id            = "${aws_vpc.main.id}"
  cidr_block        = "10.0.0.0/20"
  availability_zone = "eu-west-1a"

  tags {
    Name = "main-public-a"
  }
}

resource "aws_internet_gateway" "main" {
  vpc_id = "${aws_vpc.main.id}"

  tags {
    Name = "main"
  }
} 
resource "aws_route_table" "main" {
  vpc_id = "${aws_vpc.main.id}"

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = "${aws_internet_gateway.main.id}"
  }

  tags {
    Name = "main"
  }
}

resource "aws_main_route_table_association" "main" {
  vpc_id         = "${aws_vpc.main.id}"
  route_table_id = "${aws_route_table.main.id}"
}

Create an instance

data "aws_ami" "ubuntu1404" {
  most_recent = true

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-trusty-14.04-amd64-server-*"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }

  owners = ["099720109477"] # Canonical
}

resource "aws_instance" "main" {
  ami           = "${data.aws_ami.ubuntu1404.id}"
  instance_type = "t2.micro"

  # Even though there's aws_key_pair,
  # it's better to create a new one on web console manually.
  key_name      = "${var.key_name}"
  subnet_id     = "${data.terraform_remote_state.vpc.main_public_a}"
}

resource "aws_eip" "main" {
  instance = "${aws_instance.main.id}"
}

Create an instance profile

resource "aws_iam_instance_profile" "main" {
  name = "my-profile"
  role = "${aws_iam_role.main.name}"
}

resource "aws_iam_role" "main" {
  name = "my-role"

  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}

resource "aws_iam_role_policy" "main" {
  name = "my-role-allow-s3"
  role = "${aws_iam_role.main.id}"

  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:ListBucket"
      ],
      "Resource": [
        "${aws_s3_bucket.main.arn}"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:GetObject",
        "s3:DeleteObject"
      ],
      "Resource": [
        "${aws_s3_bucket.main.arn}/*"
      ]
    }
  ]
}
EOF
}

Set up static website with S3

resource "aws_s3_bucket" "main" {
  bucket = "${var.bucket}"
  policy = <<EOF
{
  "Version":"2012-10-17",
  "Statement":[
    {
      "Effect":"Allow",
      "Principal": "*",
      "Action": ["s3:GetObject"],
      "Resource": ["arn:aws:s3:::${var.bucket}/*"]
    }
  ]
}
EOF

  website {
    index_document = "index.html"
    error_document = "index.html" # SPA
  }
}