- 公開日
- 最終更新日
【Terraform】Terraformで作るAWSインフラ!ALBとEC2構成で「Hello World!」表示
この記事を共有する
目次
はじめに
こんにちは、サービスGの羽生です。
クラウド環境を管理コンソールから手作業で作るの面倒ですよね!
今回は Terraform を使って、ブラウザから 「Hello World!」 を表示できる環境を作ってみます!
さらに、 Amazon EC2(以下、EC2)にAWS Systems Manager(以下、SSM)で接続し、外部通信が可能な状態まで確認していきます。
前提
以下の準備が整っていることを前提に進めます。
- 作業用の EC2 にTerraform がインストール済みであること
- 作業用の EC2 に AWS CLI がインストール済みで、認証設定が完了していること
環境準備ができていない場合は、以下のドキュメントをもとに設定作業を実施してください。
・Terraform インストール Install Terraform
・AWS CLI インストール AWS CLI の最新バージョンのインストールまたは更新
概要
構築
以下の環境を構築します。
- Amazon VPC(以下、VPC):パブリック+プライベートサブネット
- EC2:プライベートサブネットに2台、Webサーバー(Apache)を起動
- Application Load Balancer(以下、ALB):パブリックサブネットに配置、EC2 をターゲットにHTTPを転送
- NAT ゲートウェイ:プライベートサブネットからの外部通信用
- インターネットゲートウェイ:ALBのインターネットアクセス
動作確認
構築後、以下の動作確認を行います。
- Webブラウザからのアクセス:ブラウザからALB のDNS名にアクセスし、「Hello World! <Hostname>」 が表示されること
- SSMによるEC2への接続:管理コンソールから接続可能なこと
- EC2から外部への通信:EC2へ接続後、外部へ通信が可能であること
構成図

手順
- 作業用EC2にログイン後、作業用ディレクトリに移動します。
$ cd terraform-dev/ - 移動したディレクトリを以下のようなファイル構成にします。
terraform-dev/ ├── provider.tf ├── vpc.tf ├── ec2.tf └── alb.tf - provider.tfファイル内に以下を記述して保存します。
ここを押すと展開します
provider "aws" { region = "ap-northeast-1" // AWS CLIの認証プロファイル名を指定してください。 profile = "psw-terraform" } - vpc.tfファイル内に以下を記述して保存します。
ここを押すと展開します
// VPC作成 resource "aws_vpc" "main_vpc" { cidr_block = "10.0.0.0/16" enable_dns_support = true enable_dns_hostnames = true tags = { Name = "terraform-dev-vpc" } } // インターネットゲートウェイ作成 resource "aws_internet\_gateway" "igw" { vpc_id = aws_vpc.main_vpc.id tags = { Name = "terraform-dev-igw" } } // リージョナルNATゲートウェイ作成 resource "aws_nat_gateway" "regional_ngw" { connectivity_type = "public" availability_mode = "regional" vpc_id = aws_vpc.main_vpc.id tags = { Name = "terraform-dev-ngw" } } // ローカルブロックでサブネットCIDRを定義 locals { public_subnets = { az1 = { az = "ap-northeast-1a" cidr = "10.0.1.0/24" } az2 = { az = "ap-northeast-1c" cidr = "10.0.2.0/24" } } private_subnets = { az1 = { az = "ap-northeast-1a" cidr = "10.0.10.0/24" } az2 = { az = "ap-northeast-1c" cidr = "10.0.20.0/24" } } } // パブリックサブネット作成 resource "aws_subnet" "public" { for_each = local.public_subnets vpc_id = aws_vpc.main_vpc.id cidr_block = each.value.cidr availability_zone = each.value.az tags = { Name = "terraform-dev-public-subnet-${each.value.az}" } } // プライベートサブネット作成 resource "aws_subnet" "private" { for_each = local.private_subnets vpc_id = aws_vpc.main_vpc.id cidr_block = each.value.cidr availability_zone = each.value.az tags = { Name = "terraform-dev-private-subnet-${each.value.az}" } } // ルートテーブル作成 resource "aws_route_table" "public" { vpc_id = aws_vpc.main_vpc.id route { cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.igw.id } tags = { Name = "terraform-dev-public-rt" } } resource "aws_route_table" "private" { vpc_id = aws_vpc.main_vpc.id route { cidr_block = "0.0.0.0/0" nat_gateway_id = aws_nat_gateway.regional_ngw.id } tags = { Name = "terraform-dev-private-rt" } } // サブネットとルートテーブルの関連付け resource "aws_route_table_association" "public_assoc" { for_each = aws_subnet.public subnet_id = each.value.id route_table_id = aws_route_table.public.id } resource "aws_route_table_association" "private_assoc" { for_each = aws_subnet.private subnet_id = each.value.id route_table_id = aws_route_table.private.id } - ec2.tfファイル内に以下を記述して保存します。
ここを押すと展開します
// プライベートサブネットリスト化 locals { private_subnet_ids = values(aws_subnet.private)[*].id } // EC2用セキュリティグループ resource "aws_security_group" "web_sg" { name = "terraform-dev-web-sg" vpc_id = aws_vpc.main_vpc.id ingress { from_port = 80 to_port = 80 protocol = "tcp" security_groups = [aws_security_group.alb_sg.id] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } tags = { Name = "terraform-dev-web-sg" } } // Amazon Linux2023の最新AMI取得 data "aws_ssm_parameter" "al2023" { name = "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64" } // webインスタンス作成(2台) resource "aws_instance" "web" { count = 2 ami = data.aws_ssm_parameter.al2023.value instance_type = "t2.micro" subnet_id = local.private_subnet_ids[ count.index % length(local.private_subnet_ids) ] vpc_security_group_ids = [aws_security_group.web_sg.id] iam_instance_profile = aws_iam_instance_profile.ssm_profile.name // ユーザーデータ指定 user_data = <<-EOF //!/bin/bash yum update -y yum install -y httpd systemctl enable httpd systemctl start httpd /* Amazon Linux 2023 では SSM Agent はデフォルトでインストール・起動されていますが、念のため明示的に起動しています */ systemctl enable amazon-ssm-agent systemctl start amazon-ssm-agent HOSTNAME=$(hostname) echo "Hello World!
Hostname: $HOSTNAME
" > /var/www/html/index.html EOF tags = { Name = "terraform-dev-web-ec2-${count.index + 1}" } } // IAMロール作成 resource "aws_iam_role" "ssm_role" { name = "terraform-dev-ssmrole" assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Principal = { Service = "ec2.amazonaws.com" } Action = "sts:AssumeRole" } ] }) } resource "aws_iam_role_policy_attachment" "ssm_attach" { role = aws_iam_role.ssm_role.name policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" } resource "aws_iam_instance_profile" "ssm_profile" { name = "terraform-dev-ssmprofile" role = aws_iam_role.ssm_role.name } - alb.tfファイル内に以下を記述して保存します。
ここを押すと展開します
// ALB用セキュリティグループ resource "aws_security_group" "alb_sg" { name = "terraform-dev-alb-sg" vpc_id = aws_vpc.main_vpc.id ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } tags = { Name = "terraform-dev-alb-sg" } } // ALB作成 resource "aws_lb" "alb" { name = "terraform-dev-alb" internal = false load_balancer_type = "application" security_groups = [ aws_security_group.alb_sg.id ] subnets = values(aws_subnet.public)[*].id tags = { Name = "terraform-dev-alb" } } // ターゲットグループ作成 resource "aws_lb_target_group" "tg" { name = "terraform-dev-tg" port = 80 protocol = "HTTP" target_type = "instance" vpc_id = aws_vpc.main_vpc.id health_check { path = "/" interval = 30 timeout = 5 healthy_threshold = 3 unhealthy_threshold = 3 } tags = { Name = "terraform-dev-tg" } } // HTTPリスナー作成 resource "aws_lb_listener" "http" { load_balancer_arn = aws_lb.alb.arn port = 80 protocol = "HTTP" default_action { type = "forward" target_group_arn = aws_lb_target_group.tg.arn } } // EC2インスタンスをターゲットグループに登録 resource "aws_lb_target_group_attachment" "web" { count = 2 target_group_arn = aws_lb_target_group.tg.arn target_id = aws_instance.web[count.index].id port = 80 } - 以下コマンドを順番に実行します。
// モジュールインストール $ terraform init
// 実行内容を確認 $ terraform plan
// 実行 $ terraform apply - マネジメントコンソールを開き、構築予定のリソースが作成されていることを確認します。
動作確認
- EC2→ロードバランサーの順に操作し、「詳細」タブで、DNS名を確認します。
- Webブラウザ(Chromeなど)を起動し、アドレスバーに以下を入力しアクセスします。
http://"ALBのDNS名" - ページにアクセスでき、「Hello World!」が表示されていたら成功です!
HostnameにはEC2のホスト名が表示されるようになっているので、何度かリロードしてホスト名が切り替わることも確認してみましょう!(ALBのラウンドロビン負荷分散の動作)
- 管理コンソールに戻り、対象インスタンスにチェックをいれて「接続」を押し、EC2に接続できることを確認します。
- コマンドラインの画面が表示されれば接続完了です!
- EC2への接続が確認できたら、以下コマンドを実行してインターネットへの接続ができることを確認します。
// Googleドメイン名への疎通確認(外部向け通信) $ ping google.com - 上記すべての動作確認が正常であれば完了です!構築と動作確認まで問題なくできましたね!
リソース削除
構築したリソースは利用料金が発生する可能性がありますので、以下コマンドを実行して環境を削除しましょう。
// 環境削除
$ terraform destroy
最後に
今回は Terraform を使って、基本的なWeb構成を構築しました。
- ALB 経由で Hello World が表示されること
- EC2 に SSM で安全に接続できること
- プライベートサブネットから外部通信が可能であること
これら動作をTerraformで再現できることを確認できました。
また、Auto ScalingやHTTPS化などを追加すれば、より実践的な環境にも発展させることができます!
コードによる再現性やヒューマンエラーの防止目的で多くのシステムで活用されるTerraformですので、引き続き知見を深めていきたいですね!
おまけ
本題から逸れるため説明は省きましたが、本構成での NAT ゲートウェイ は、昨年11月にリリースされた リージョナルNAT Gateway で構築しています。 従来のNAT Gatewayとの比較やメリットは、AWS公式ドキュメントが参考になるのでぜひご覧ください。
この記事は私が書きました
S.Hanyu
記事一覧サッカー観戦(プレミア、ラ・リーガ)とコーヒーが好きです。 Terraformが好きでよく触っています。