0%
October 13, 2023

Variables and Remote Execution

aws

cloud

terraform

The variables.tf

variable "aws_region" {
  description = "Region where you want to provision"
  type        = string //number, bool
  default     = "ap-northeast-1"
}

variable "port_list" {
  description = "List of port to open for your webserver"
  type        = list(any)
  default     = ["80", "443"]
}

variable "instance_type" {
  description = "EC2 Instance size to provision"
  type        = string
  default     = "t3.micro"
}

variable "tags" {
  description = "Tags to apply to resources"
  type        = map(any)
  default = {
    Owner       = "James Lee"
    Environment = "Prod"
    Project     = "Pheonix"
  }
}

output "latest_ubuntu20_ami_id" {
  value = data.aws_ami.latest_ubuntu20.id
}

output "latest_amazonlinux_ami_id" {
  value = data.aws_ami.latest_amazonlinux.id
}

The main.tf Using variables.tf

provider "aws" { region = var.aws_region }

resource "aws_security_group" "web" {
  name        = "${var.tags["Environment"]} WebServer-SG"
  description = "Security Group for WebServer"
  dynamic "ingress" {
    for_each = var.port_list
    content {
      from_port   = ingress.value
      to_port     = ingress.value
      protocol    = "tcp"
      cidr_blocks = ["0.0.0.0/0"]
    }
  }
  egress {
    description = "Allow All Ports"
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
  tags = merge(var.tags, { Name = "${var.tags["Environment"]} WebServer SG by Terraform" })
}

resource "aws_instance" "web" {
  ami                    = data.aws_ami.latest_amazonlinux.id
  instance_type          = var.instance_type
  vpc_security_group_ids = [aws_security_group.web.id]
  tags                   = merge(var.tags, { Name = "${var.tags["Environment"]} WebServer Built by Terraform" })
}

resource "aws_eip" "web" {
  instance = aws_instance.web.id
  tags     = merge(var.tags, { Name = "${var.tags["Environment"]} Elastic IP by Terraform" })
}

data "aws_ami" "latest_ubuntu20" {
  owners      = ["099720109477"]
  most_recent = true
  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
  }
}

data "aws_ami" "latest_amazonlinux" {
  owners      = ["137112412989"]
  most_recent = true
  filter {
    name   = "name"
    values = ["al2023-ami-2023.*-kernel-6.1-x86_64"]
  }
}

The *.tfvars

This provides the default values to variables.tf in a separate file:

New variables.tf
variable "aws_region" {
  description = "Region where you want to provision"
  type        = string //number, bool
}

variable "port_list" {
  description = "List of port to open for your webserver"
  type        = list(any)
}

variable "instance_type" {
  description = "EC2 Instance size to provision"
  type        = string
}

variable "tags" {
  description = "Tags to apply to resources"
  type        = map(any)
}
terraform.tfvars
aws_region    = "ap-northeast-1"
port_list     = ["80", "443"]
instance_type = "t3.micro"
tags = {
  Owner       = "James Lee"
  Environment = "Prod"
  Project     = "Pheonix"
}
prod.tfvars

Let's rename terraform.tfvars to prod.tfvars and terraform apply again, the *.tfvars does not take effect. In that case, we try:

terraform apply -var-file=prod.tfvars

Local Variables

In main.tf we can write

locals {
  X = 1
  Y = 2
}

locals {
  amazonlinux_ami = data.aws_ami.latest_amazonlinux.id
  ubuntu_ami      = data.aws_ami.latest_ubuntu20.id
  Z = "${local.X} and ${local.Y}"
  some_tags = {
    Owner = "James Lee"
  }
}

resource "aws_instance" "web" {
  ami                    = local.amazonlinux_ami
  instance_type          = var.instance_type
  vpc_security_group_ids = [aws_security_group.web.id]
  tags                   = merge(var.tags, local.some_tags, { Name = "${var.tags["Environment"]} WebServer Built by Terraform" })
}

data "aws_ami" "latest_ubuntu20" {
  owners      = ["099720109477"]
  most_recent = true
  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
  }
}

data "aws_ami" "latest_amazonlinux" {
  owners      = ["137112412989"]
  most_recent = true
  filter {
    name   = "name"
    values = ["al2023-ami-2023.*-kernel-6.1-x86_64"]
  }
}

...

Remote Execution

After an EC2 instance is created, we arrange shell scripts to be executed as subsequent tasks (for example, we may want to install gitlab runner to perform CICD task).

provider "aws" {
  region = "ca-central-1"
}

resource "aws_default_vpc" "default" {} # This need to be added since AWS Provider v4.29+ to get VPC id

resource "aws_instance" "myserver" {
  ami                    = "ami-0c9bfc21ac5bf10eb"
  instance_type          = "t3.nano"
  vpc_security_group_ids = [aws_security_group.web.id]
  key_name               = "denis-key-ca-central-1"
  tags = {
    Name  = "My EC2 with remote-exec"
    Owner = "Denis Astahov"
  }

  provisioner "remote-exec" {
    inline = [
      "mkdir /home/ec2-user/terraform",
      "cd /home/ec2-user/terraform",
      "touch hello.txt",
      "echo 'Terraform was here...' > terraform.txt"
    ]
    connection {
      type        = "ssh"
      user        = "ec2-user"
      host        = self.public_ip //Same as: aws_instance.myserver.public_ip
      private_key = file("denis-key-ca-central-1.pem")
    }
  }
}


resource "aws_security_group" "web" {
  name   = "My-SecurityGroup"
  vpc_id = aws_default_vpc.default.id # This need to be added since AWS Provider v4.29+ to set VPC id
  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  egress {
    description = "Allow ALL ports"
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
  tags = {
    Name  = "SG by Terraform"
    Owner = "Denis Astahov"
  }
}