【爆速】TerraformでプライベートサブネットのEC2へSession Managerからアクセス可能な環境を構築する: NAT Gateway編

AWS

2024.5.22

Topics

はじめに

こんにちは、Shunです!

EC2を使った検証をする際に、プライベートサブネットにEC2を立てて、NATゲートウェイを作って、ルートテーブルを更新するなど、毎回環境を構築するのは面倒くさいですよね!
そこで、爆速かつセキュアにEC2を検証するTerraformテンプレートをご紹介します!

NATゲートウェイではなく、VPCエンドポイントを使った環境を作りたい方はこちら!

関連記事
【爆速】TerraformでプライベートサブネットのEC2へSession Managerからアクセス可能な環境を構築する: VPCエンドポイント編

爆速で、CloudFront + S3の静的サイトホスティング環境を作りたい方はこちら!

関連記事
【爆速】CloudFormationでCloudFront+S3環境を5分以内に構築

構築するアーキテクチャ

今回、構築するシステム構成図です。
Session Managerからアクセスするために、Internet Gateway、NAT Gateway等を構築します。

terraformファイル構成

今回使用するtfファイルは以下の構成になっています。

├─ provider.tf
├─ vpc.tf
└─ ec2.tf

1.providers.tf

locals内の変数を書き換えるだけで、利用可能です。

locals {
  aws_id      = "[アカウントID]"
  name_prefix = "[名前]"
  region      = "[リージョン]"
  Environment = "[環境名]"
}


terraform {
  required_version = "~> 1.7.0"
  backend "s3" {
    key            = "[S3のkey]"
    bucket         = "[バケット名]"
    region         = "[リージョン]"
  }

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

variable "vpc" {
  description = "Parameter for VPC"
  type        = string
  default     = "10.0.0.0/16"
}

variable "public_subnet" {
  description = "Parameter for public subnet"
  type        = string
  default     = "10.0.0.0/24"
}

variable "private_subnet" {
  description = "Parameter for private subnet"
  type        = string
  default     = "10.0.128.0/24"
}

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

2. vpc.tf

vpc.tfでは、3つのことを実施しています。

  • VPC、サブネットなどネットワークの構築
  • Internet GatewayとNAT Gatewayの作成
  • Internet GatewayとNAT Gatewayへ疎通可能なルートテーブルの作成
####################
# vpc
####################
resource "aws_vpc" "main" {
  cidr_block           = var.vpc
  enable_dns_hostnames = true
  enable_dns_support   = true
  tags = {
    Name = "${local.name_prefix}-${local.Environment}-vpc"
  }
}

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

# プライベートサブネット作成
resource "aws_subnet" "private_1a" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = var.private_subnet
  availability_zone = "ap-northeast-1a"
  tags = {
    Name = "${local.name_prefix}-${local.Environment}-private-1a"
  }
}

####################
# internet gateway
####################
resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id
  tags = {
    Name = "${local.name_prefix}-${local.Environment}-igw"
  }
}

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

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

# public_ルート作成
resource "aws_route" "public_igw_1a" {
  destination_cidr_block = "0.0.0.0/0"
  route_table_id         = aws_route_table.public_1a.id
  gateway_id             = aws_internet_gateway.main.id
}

# private_ルート作成
resource "aws_route" "nat_1a" {
  destination_cidr_block = "0.0.0.0/0"
  nat_gateway_id         = aws_nat_gateway.nat_1a.id
  route_table_id         = aws_route_table.private_1a.id
}

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

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

####################
# nat gateway
####################
# NATゲートウェイ(AZ-1a)に割り当てるEIPを作成
resource "aws_eip" "nat_1a" {
  domain = "vpc"
  tags = {
    Name = "${local.name_prefix}-${local.Environment}-nat-1a"
  }
}

# NATゲートウェイ(AZ-1a)作成
resource "aws_nat_gateway" "nat_1a" {
  allocation_id = aws_eip.nat_1a.id
  subnet_id     = aws_subnet.public_1a.id
  depends_on    = [aws_internet_gateway.main]
  tags = {
    Name = "${local.name_prefix}-${local.Environment}-ngw-1a"
  }
}

3. ec2.tf

ec2.tfでは、3つのことを実施しています。

  • 最新のAmazon LinuxのAMIを取得し、EC2を構築
  • インバウンドを許可しないセキュリティグループを作成
  • Session Managerを使用するためのIAMを作成し、EC2へ付与
####################
# 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.main.id
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  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"
}

環境構築

今回は、CloudShellを用いて、Terraformを実行します。
CloudShellを開き、以下のコマンドを入力し、Terraformをインストールします。

$ cd ~
$ git clone https://github.com/tfutils/tfenv.git ~/.tfenv
$ mkdir -p ~/.local/bin/
$ sudo ln -s ~/.tfenv/bin/* ~/.local/bin/
$ tfenv install 1.7.0
$ tfenv use 1.7.0

続いて、CloudShell内に適当なディレクトリを作成し、今回ご紹介したtfファイルを配置します。
作成したディクトリ配下へ移動し、以下のコマンドを入力します。

$ terraform init
$ terraform fmt
$ terraform plan

問題なければ、以下のコマンドを実行し、環境へデプロイします。

$ terraform apply 

これで環境構築は完了です。

疎通確認

マネジメントコンソールからEC2の画面を開きます。
設定したインスタンスを選択し、セッションマネージャーから接続をします。

以下のように接続ができれば完了です!

さいごに

今回の記事では、プライベートサブネットのEC2へSession Managerからアクセス可能なTerraformテンプレートをご紹介しました!

このテンプレートを使用することで、爆速でセキュアな環境でEC2を利用することができます!
是非、テンプレートを活用し、セキュアなEC2ライフを送ってください!

最後まで読んでいただきありがとうございます!

テックブログ新着情報のほか、AWSやGoogle Cloudに関するお役立ち情報を配信中!

Shun

好きな言葉は生ビール199円です。日本ビール協会認定1冠、Google Cloud11冠、AWS12冠

Recommends

こちらもおすすめ

Special Topics

注目記事はこちら