- 公開日
- 最終更新日
【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 "awsvpc" "mainvpc" { cidrblock = "10.0.0.0/16" enablednssupport = true enablednshostnames = true tags = { Name = "terraform-dev-vpc" } } // インターネットゲートウェイ作成 resource "awsinternetgateway" "igw" { vpcid = awsvpc.mainvpc.id tags = { Name = "terraform-dev-igw" } } // リージョナルNATゲートウェイ作成 resource "awsnatgateway" "regionalngw" { connectivitytype = "public" availabilitymode = "regional" vpcid = awsvpc.mainvpc.id tags = { Name = "terraform-dev-ngw" } } // ローカルブロックでサブネットCIDRを定義 locals { publicsubnets = { az1 = { az = "ap-northeast-1a" cidr = "10.0.1.0/24" } az2 = { az = "ap-northeast-1c" cidr = "10.0.2.0/24" } } privatesubnets = { az1 = { az = "ap-northeast-1a" cidr = "10.0.10.0/24" } az2 = { az = "ap-northeast-1c" cidr = "10.0.20.0/24" } } } // パブリックサブネット作成 resource "awssubnet" "public" { foreach = local.publicsubnets vpcid = awsvpc.mainvpc.id cidrblock = each.value.cidr availabilityzone = each.value.az tags = { Name = "terraform-dev-public-subnet-${each.value.az}" } } // プライベートサブネット作成 resource "awssubnet" "private" { foreach = local.privatesubnets vpcid = awsvpc.mainvpc.id cidrblock = each.value.cidr availabilityzone = each.value.az tags = { Name = "terraform-dev-private-subnet-${each.value.az}" } } // ルートテーブル作成 resource "awsroutetable" "public" { vpcid = awsvpc.mainvpc.id route { cidrblock = "0.0.0.0/0" gatewayid = awsinternetgateway.igw.id } tags = { Name = "terraform-dev-public-rt" } } resource "awsroutetable" "private" { vpcid = awsvpc.mainvpc.id route { cidrblock = "0.0.0.0/0" natgatewayid = awsnatgateway.regionalngw.id } tags = { Name = "terraform-dev-private-rt" } } // サブネットとルートテーブルの関連付け resource "awsroutetableassociation" "publicassoc" { foreach = awssubnet.public subnetid = each.value.id routetableid = awsroutetable.public.id } resource "awsroutetableassociation" "privateassoc" { foreach = awssubnet.private subnetid = each.value.id routetableid = awsroutetable.private.id } - ec2.tfファイル内に以下を記述して保存します。
ここを押すと展開します
// プライベートサブネットリスト化 locals { privatesubnetids = values(awssubnet.private)[*].id } // EC2用セキュリティグループ resource "awssecuritygroup" "websg" { name = "terraform-dev-web-sg" vpcid = awsvpc.mainvpc.id ingress { fromport = 80 toport = 80 protocol = "tcp" securitygroups = [awssecuritygroup.albsg.id] } egress { fromport = 0 toport = 0 protocol = "-1" cidrblocks = ["0.0.0.0/0"] } tags = { Name = "terraform-dev-web-sg" } } // Amazon Linux2023の最新AMI取得 data "awsssmparameter" "al2023" { name = "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x8664" } // webインスタンス作成(2台) resource "awsinstance" "web" { count = 2 ami = data.awsssmparameter.al2023.value instancetype = "t2.micro" subnetid = local.privatesubnetids[ count.index % length(local.privatesubnetids) ] vpcsecuritygroupids = [awssecuritygroup.websg.id] iaminstanceprofile = awsiaminstanceprofile.ssmprofile.name // ユーザーデータ指定 userdata = <<-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 "awsiamrole" "ssmrole" { name = "terraform-dev-ssmrole" assumerolepolicy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Principal = { Service = "ec2.amazonaws.com" } Action = "sts:AssumeRole" } ] }) } resource "awsiamrolepolicyattachment" "ssmattach" { role = awsiamrole.ssmrole.name policyarn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" } resource "awsiaminstanceprofile" "ssmprofile" { name = "terraform-dev-ssmprofile" role = awsiamrole.ssmrole.name } - alb.tfファイル内に以下を記述して保存します。
ここを押すと展開します
// ALB用セキュリティグループ resource "awssecuritygroup" "albsg" { name = "terraform-dev-alb-sg" vpcid = awsvpc.mainvpc.id ingress { fromport = 80 toport = 80 protocol = "tcp" cidrblocks = ["0.0.0.0/0"] } egress { fromport = 0 toport = 0 protocol = "-1" cidrblocks = ["0.0.0.0/0"] } tags = { Name = "terraform-dev-alb-sg" } } // ALB作成 resource "awslb" "alb" { name = "terraform-dev-alb" internal = false loadbalancertype = "application" securitygroups = [ awssecuritygroup.albsg.id ] subnets = values(awssubnet.public)[*].id tags = { Name = "terraform-dev-alb" } } // ターゲットグループ作成 resource "awslbtargetgroup" "tg" { name = "terraform-dev-tg" port = 80 protocol = "HTTP" targettype = "instance" vpcid = awsvpc.mainvpc.id healthcheck { path = "/" interval = 30 timeout = 5 healthythreshold = 3 unhealthythreshold = 3 } tags = { Name = "terraform-dev-tg" } } // HTTPリスナー作成 resource "awslblistener" "http" { loadbalancerarn = awslb.alb.arn port = 80 protocol = "HTTP" defaultaction { type = "forward" targetgrouparn = awslbtargetgroup.tg.arn } } // EC2インスタンスをターゲットグループに登録 resource "awslbtargetgroupattachment" "web" { count = 2 targetgrouparn = awslbtargetgroup.tg.arn targetid = awsinstance.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が好きでよく触っています。