🌐

【Terraform】 VPC/EC2のよくある構成を作ってみた

2024/08/10に公開

前書き

今回はTerraformを使ってよくある以下の構成リソースを作ってみました。

TerraformのインストールやAWSのクレデンシャルの設定等は割愛しています。
もし設定されていない方は以下をご参照ください。

作ったファイルは以下の5ファイルです。
本当はsubnetとかroutetableもファイルとして分けたほうが良いのだけど横着して一緒に書いてますがご了承ください。

  1. backend.tf
    a. tfのstateを置くバケットの記載
  2. main.tf
    a. バージョンとかproviderの設定記載
  3. variable.tf
    a. 各ファイルで使用する変数の設定記載
  4. ec2.tf
    a. ec2とsecuritygroup等々の設定記載
  5. vpc.tf
    a. vpcとかsubnetとかgateway周り等々の設定記載

backend.tf

もし東京リージョン使っていないよという方はregionもよしなに変更ください。

terraform {
  backend "s3" {
    bucket = "編集箇所:ここにtf.stateを配置したいバケット名。例)dev-tf-backend-ryuko"
    key    = "編集箇所:ここにtf.stateを配置したいキー名。例)dev/state"
    region = "ap-northeast-1"
  }
}

main.tf

ここは使用しているバージョンによって必要であれば変更します。

terraform {
  // terraformのバージョンを指定
  required_version = ">= 1.5.1"

  // awsプロバイダーのバージョンを指定
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.16"
    }
  }
}

// awsプロバイダーの設定
provider "aws" {
  // regionを指定
  region = "ap-northeast-1"
}

variable.tf

locals {
  vpc_name = "編集箇所:VPCの名前"
  vpc_cidr = {
    dev = "10.0.0.0/16"
  }

  aws_id      = "編集箇所:ここはAWSのアカウントID"
  name_prefix = "編集箇所:各リソースにつけたいプレフィックスを指定"
  region      = "ap-northeast-1"
  Environment = "編集箇所:devとかstgとかprodとか"
}

variable "private_subnet" {
  description = "Parameter for private subnet"
  type        = string
  default     = "編集箇所:例)10.0.128.0/24"
}

variable "public_subnet" {
  description = "Parameter for public subnet"
  type        = string
  default     = "編集箇所:例)10.0.127.0/24"
}

variable "default_instance_type" {
  default = "t2.micro"
}

ec2.tf

####################
# ami
####################
data "aws_ami" "latest_amazon_linux2" {
  most_recent = true
  owners      = ["amazon"]

  filter {
    name   = "name"
    values = ["amzn2-ami-hvm-*-x86_64-gp2"]
  }
}

####################
# ec2
####################
resource "aws_instance" "main" {
  instance_type          = var.default_instance_type
  ami                    = data.aws_ami.latest_amazon_linux2.id
  subnet_id              = aws_subnet.private_1a.id
  vpc_security_group_ids = [aws_security_group.main.id]
  iam_instance_profile   = aws_iam_instance_profile.example_profile.name
  root_block_device {
    volume_size           = 8
    volume_type           = "gp3"
    iops                  = 3000
    throughput            = 125
    delete_on_termination = true

    # EBSのNameタグ
    tags = {
      Name = "${local.name_prefix}-${local.Environment}"
    }
  }
  lifecycle {
    ignore_changes = [
      ami,
    ]
  }

  tags = {
    Name = "${local.name_prefix}-${local.Environment}"
  }
}

####################
# security group
####################
resource "aws_security_group" "main" {
  name   = "${local.name_prefix}-${local.Environment}-sg"
  vpc_id = aws_vpc.vpc.id
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
  ingress {
    from_port = 443
    to_port   = 443
    protocol  = "tcp"
    cidr_blocks = [
      "10.0.0.0/16"
    ]
  }
  tags = {
    Name = "${local.name_prefix}-${local.Environment}-sg"
  }
}

####################
# ec2 iam role
####################

# インスタンスプロファイルを作成
resource "aws_iam_instance_profile" "example_profile" {
  name = "${local.name_prefix}-${local.Environment}-ssm"
  role = aws_iam_role.ssm_role.name
}

resource "aws_iam_role" "ssm_role" {
  name               = "${local.name_prefix}-${local.Environment}-ssm"
  assume_role_policy = data.aws_iam_policy_document.assume_role.json
}

data "aws_iam_policy_document" "assume_role" {
  statement {
    actions = ["sts:AssumeRole"]
    principals {
      type        = "Service"
      identifiers = ["ec2.amazonaws.com"]
    }
  }
}

resource "aws_iam_role_policy_attachment" "ssm_managed_instance_core" {
  role       = aws_iam_role.ssm_role.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}

vpc.tf

####################
# vpc
####################

resource "aws_vpc" "vpc" {
  cidr_block           = local.vpc_cidr.dev
  enable_dns_hostnames = true
  enable_dns_support   = true
  tags = {
    Name = local.vpc_name
  }
}

####################
# subnet
####################

# private作成
resource "aws_subnet" "private_1a" {
  vpc_id            = aws_vpc.vpc.id
  cidr_block        = var.private_subnet
  availability_zone = "ap-northeast-1a"
  tags = {
    Name = "${local.name_prefix}-${local.Environment}-private-1a"
  }
}

# public作成
resource "aws_subnet" "public_1a" {
  vpc_id            = aws_vpc.vpc.id
  cidr_block        = var.public_subnet
  availability_zone = "ap-northeast-1a"
  tags = {
    Name = "${local.name_prefix}-${local.Environment}-public-1a"
  }
}

####################
# route table
####################

# private_ルートテーブル作成
resource "aws_route_table" "private_1a" {
  vpc_id = aws_vpc.vpc.id
  tags = {
    Name = "${local.name_prefix}-${local.Environment}-private-1a"
  }
}

# private_ルートテーブル紐づけ
resource "aws_route_table_association" "private_1a" {
  subnet_id      = aws_subnet.private_1a.id
  route_table_id = aws_route_table.private_1a.id
}

# public_ルートテーブル作成
resource "aws_route_table" "public_1a" {
  vpc_id = aws_vpc.vpc.id
  tags = {
    Name = "${local.name_prefix}-${local.Environment}-public-1a"
  }
}

# public_ルートテーブル紐づけ
resource "aws_route_table_association" "public_1a" {
  subnet_id      = aws_subnet.public_1a.id
  route_table_id = aws_route_table.public_1a.id
}

# public subnetとinternet gatewayの紐づけ
resource "aws_route" "public_igw_a" {
  route_table_id         = aws_route_table.public_1a.id
  gateway_id             = aws_internet_gateway.main.id
  destination_cidr_block = "0.0.0.0/0"
}

# private subnetとnat gatewayの紐づけ
resource "aws_route" "public_natgw_a" {
  route_table_id         = aws_route_table.private_1a.id
  nat_gateway_id         = aws_nat_gateway.nat_1a.id
  destination_cidr_block = "0.0.0.0/0"
}

####################
# internet gateway
####################

resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.vpc.id

  tags = {
    Name = "${local.name_prefix}-${local.Environment}-igw"
  }
}

####################
# nat gateway
####################

# Elastic IP
resource "aws_eip" "nat_1a" {
  vpc = true
  tags = {
    Name = "${local.name_prefix}-${local.Environment}-natgw-1a"
  }
}

# NAT Gateway
resource "aws_nat_gateway" "nat_1a" {
  subnet_id     = aws_subnet.public_1a.id # NAT Gatewayを配置するSubnetを指定
  allocation_id = aws_eip.nat_1a.id       # 紐付けるElastic IP

  tags = {
    Name = "${local.name_prefix}-${local.Environment}-1a"
  }
}

####################
# vpc endpoint
####################
resource "aws_vpc_endpoint" "ssm" {
  vpc_id              = aws_vpc.vpc.id
  service_name        = "com.amazonaws.${local.region}.ssm"
  vpc_endpoint_type   = "Interface"
  security_group_ids  = [aws_security_group.main.id]
  subnet_ids          = [aws_subnet.private_1a.id]
  private_dns_enabled = true

  tags = {
    Name = "${local.name_prefix}-${local.Environment}-ssm"
  }
}

resource "aws_vpc_endpoint" "ec2messages" {
  vpc_id              = aws_vpc.vpc.id
  service_name        = "com.amazonaws.${local.region}.ec2messages"
  vpc_endpoint_type   = "Interface"
  security_group_ids  = [aws_security_group.main.id]
  subnet_ids          = [aws_subnet.private_1a.id]
  private_dns_enabled = true

  tags = {
    Name = "${local.name_prefix}-${local.Environment}-ec2messages"
  }
}

resource "aws_vpc_endpoint" "ssmmessages" {
  vpc_id              = aws_vpc.vpc.id
  service_name        = "com.amazonaws.${local.region}.ssmmessages"
  vpc_endpoint_type   = "Interface"
  security_group_ids  = [aws_security_group.main.id]
  subnet_ids          = [aws_subnet.private_1a.id]
  private_dns_enabled = true

  tags = {
    Name = "${local.name_prefix}-${local.Environment}-ssmmessages"
  }
}

cidr_blockやsubnetのIPなどはもし自身の環境で他リソースに使用されていたら適宜変えてみてください。

terraform plan で作成されるリソースを確認して terraform apply してみるとリソースができます。

ちゃんと作れているかの確認としては、AWSコンソールで対象のEC2を開く→セッションマネージャーで接続してみてちゃんとつながることを確認→curl http://abehiroshi.la.coocan.jp/ とかでnatgateway経由でネットに繋がることを確認するで良いと思います。もちろんpingでも。

Discussion