こんにちは。よっしーです(^^)
今日は、Terraformを利用したEC2環境の構築についてご紹介します。
前提
本記事では、下記記事の内容が動作することを前提にしています。
背景
AWSでEC2環境を構築する機会がありましたので、その内容を共有したいと思います。
AWSのIAMユーザには、terraformというユーザを作成して、PowerUserAccessのポリシーを許可しています。
作業ディレクトリの準備
作業ディレクトリの作成
作業ディレクトリの作成をします。ここでは、18_learn_terraform-aws-ec2としていますが、ディレクトリ名は任意に変えていただいて問題ありません。
mkdir 18_learn_terraform-aws-ec2
使用するコマンドのバージョン指定
使用するコマンドのバージョンを指定します。
asdf local terraform 1.5.3
asdf local awscli 2.13.2
このとき、自分の環境では、下記のエラーになりました。
% asdf local awscli 2.13.2
version 2.13.2 is not installed for awscli
なので、下記のコマンドで指定のバージョンをインストールします。
asdf install awscli 2.13.2
インストールが完了したら、再度、下記のコマンドを実行します。
asdf local awscli 2.13.2
下記のコマンドでバージョンを確認します。
% terraform --version
Terraform v1.5.3
on darwin_arm64
% aws --version
aws-cli/2.13.2 Python/3.11.4 Darwin/22.5.0 exe/x86_64 prompt/off
asdfコマンドについては下記の記事を御覧ください。
修正内容
下記のファイルを作成します。下記の各セクションに各ファイルの修正内容を記載しています。
new file: main.tf
new file: vpc.tf
new file: ec2.tf
new file: output.tf
new file: variables.tf
main.tf
下記の内容で新規作成します。
terraform {
cloud {
organization = "(自身の組織名を入力)"
workspaces {
name = "learn-aws-ec2"
}
}
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.16"
}
}
required_version = ">= 1.2.0"
}
provider "aws" {
region = "ap-northeast-1"
}
ec2.tf
下記の内容で新規作成します。
# AMI
data "aws_ssm_parameter" "learn_ec2_ami" {
name = "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"
}
# EC2作成
resource "aws_instance" "learn_ec2_instance" {
ami = data.aws_ssm_parameter.learn_ec2_ami.value
instance_type = "t2.micro"
availability_zone = var.az_a
subnet_id = aws_subnet.learn_ec2_public_subnet_1a.id
associate_public_ip_address = "true"
tags = {
Name = "${var.tag_name}"
}
}
vpc.tf
下記の内容で新規作成します。
# VPC
resource "aws_vpc" "learn_ec2_vpc" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true # DNSホスト名を有効化
tags = {
Name = "${var.tag_name}"
}
}
# Subnet
resource "aws_subnet" "learn_ec2_public_subnet_1a" {
vpc_id = aws_vpc.learn_ec2_vpc.id
cidr_block = "10.0.1.0/24"
availability_zone = var.az_a
tags = {
Name = "${var.tag_name}"
}
}
# Internet Gateway
resource "aws_internet_gateway" "learn_ec2_igw" {
vpc_id = aws_vpc.learn_ec2_vpc.id
tags = {
Name = "${var.tag_name}"
}
}
# Route table
resource "aws_route_table" "learn_ec2_rt" {
vpc_id = aws_vpc.learn_ec2_vpc.id
tags = {
Name = "${var.tag_name}"
}
}
resource "aws_route" "learn_ec2_rt_public" {
route_table_id = aws_route_table.learn_ec2_rt.id
gateway_id = aws_internet_gateway.learn_ec2_igw.id
destination_cidr_block = "0.0.0.0/0"
}
# SubnetとRoute tableの関連付け
resource "aws_route_table_association" "learn_ec2_rt_associate" {
subnet_id = aws_subnet.learn_ec2_public_subnet_1a.id
route_table_id = aws_route_table.learn_ec2_rt.id
}
output.tf
下記の内容で新規作成します。
# 作成したEC2のパブリックIPアドレスを出力
output "ec2_global_ips" {
value = aws_instance.learn_ec2_instance.*.public_ip
}
variables.tf
下記の内容で新規作成します。
variable "az_a" {
default = "ap-northeast-1a"
}
variable "tag_name" {
default = "learn-aws-ec2"
}
EC2環境構築
下記のコマンドを実行します。
terraform login
Yesで確認し、自動的に開くブラウザウィンドウでワークフローに従います。プロンプトが表示されたら、生成された API キーを Terminal に貼り付ける必要があります。
下記のコマンドを実行していきます。
terraform init
terraform fmt
terraform validate
terraform plan
上記のコマンドでエラーがなければ、下記のコマンドを実行します。これで、AWSにインスタンスが作成されます。
terraform apply
下記のような出力なっていれば成功です。
Apply complete! Resources: 7 added, 0 changed, 0 destroyed.
Outputs:
ec2_global_ips = [
"xxx.xxx.xxx.xxx",
]
作成したEC2インスタンスに接続したいのですが、接続の設定が未設定のため、接続することができません。なので、次回の記事では、作成したEC2インスタンスにSSH接続できるようにしますので、次回の記事をお待ち下さい。
ここでは、作成したインスタンスを次のコマンドで破棄しておきます。
terraform destroy
下記のような出力になっていれば成功です。
Apply complete! Resources: 0 added, 0 changed, 7 destroyed.
解説
main.tf
このコードは、Terraformの設定ファイル(通常、main.tf
という名前のファイル)の一部です。Terraformは、クラウドインフラストラクチャをコードとして管理するためのツールであり、このファイルにはAWSプロバイダーとそのバージョン、およびTerraformCloudの組織とワークスペースに関する設定が含まれています。
解説:
terraform
ブロック: これは、Terraform設定ファイルの先頭に置かれるブロックで、Terraformのバージョンや必要なプロバイダーの設定を指定するために使用されます。required_version
: この設定は、このTerraform設定が実行されるのに必要なTerraformの最小バージョンを指定します。この例では、Terraformのバージョンは1.2.0以上である必要があります。required_providers
: このブロックは、Terraformが使用するプロバイダーを指定するために使用されます。ここでは、AWSプロバイダーを指定しています。
cloud
ブロック: これは、Terraform Cloudの組織とワークスペースに関連する設定を行うためのブロックです。organization
: この設定は、Terraformクラウドの組織を指定します。workspaces
: このブロックは、ワークスペースの設定を行います。Terraformでは、複数のワークスペースを使用して異なる環境を管理することができます。ここでは、”learn-aws-ec2″という名前のワークスペースが指定されています。
provider
ブロック: これは、Terraformが使用するプロバイダーの具体的な設定を行うためのブロックです。ここでは、AWSプロバイダーの設定が指定されています。region
: この設定は、AWSリソースを作成するリージョンを指定します。この例では、”ap-northeast-1″(アジアパシフィック(東京)リージョン)が指定されています。
この設定ファイルをTerraformコマンドで実行すると、指定されたAWSリージョンで”learn-aws-ec2″という名前のワークスペースが作成され、AWSプロバイダーを使用してインフラストラクチャの構築や管理が行われるでしょう。ただし、このコードの他のファイルやリソース定義がない場合、何も作成されない可能性もあります。
ec2.tf
このTerraformコードは、AWS上にAmazon Linux 2のEC2インスタンスを作成するための設定です。以下では、各ブロックの機能と意味を解説します:
data "aws_ssm_parameter" "learn_ec2_ami"
ブロック: これは、Amazon EC2のAmazon Machine Image (AMI) を取得するためにAWS Systems Manager (SSM) パラメータストアを使用しています。AWS SSMパラメータストアは、構成情報やシークレットなどのパラメータを管理するためのAWSサービスです。このブロックでは、指定された名前のAMIパラメータを取得します。name
: 取得するパラメータの名前を指定しています。この例では、Amazon Linux 2の最新の公式AMIを指定しています。
resource "aws_instance" "learn_ec2_instance"
ブロック: これは、Amazon EC2インスタンスを作成するためのリソースブロックです。ここでは、前のブロックで取得したAMIとその他のパラメータを使用してEC2インスタンスを作成しています。ami
: EC2インスタンスに使用するAMIを指定しています。data "aws_ssm_parameter" "learn_ec2_ami.value
で取得したAMIを使用します。instance_type
: インスタンスのサイズを指定します。この例では、”t2.micro” という小さいサイズのインスタンスを指定しています。availability_zone
: EC2インスタンスを作成するアベイラビリティーゾーンを指定します。この例では、変数var.az_a
に指定されたアベイラビリティーゾーンを使用しています。subnet_id
: EC2インスタンスを配置するサブネットのIDを指定します。この例では、リソースaws_subnet.learn_ec2_public_subnet_1a
から取得したサブネットIDを使用しています。associate_public_ip_address
: インスタンスにパブリックIPアドレスを自動的に割り当てるかどうかを指定します。この例では、”true” に設定しています。tags
: インスタンスにタグを付けるためのマップを指定します。この例では、変数var.tag_name
の値を使って”Name”タグを付けています。
このコードを実行すると、指定したAMIを使用してAmazon Linux 2のEC2インスタンスが作成され、指定したサブネットおよびアベイラビリティーゾーンに配置されます。また、インスタンスには指定したタグが付けられ、パブリックIPアドレスが自動的に割り当てられます。
vpc.tf
このコードは、AWS上にVPCを作成し、その中にパブリックサブネットを作成してインターネット接続を行うTerraformの設定です。以下では、各ブロックの機能と意味を解説します:
resource "aws_vpc" "learn_ec2_vpc"
ブロック: これは、VPC(Virtual Private Cloud)を作成するためのリソースブロックです。VPCはAWSリソースをデプロイするための仮想ネットワークを提供します。cidr_block
: VPCで使用するネットワークのCIDRブロックを指定します。この例では、”10.0.0.0/16″ のアドレス範囲が指定されています。enable_dns_hostnames
: この設定は、VPC内のEC2インスタンスにDNSホスト名を有効にするかどうかを指定します。この例では、trueに設定しています。tags
: VPCにタグを付けるためのマップを指定します。この例では、変数var.tag_name
の値を使って”Name”タグを付けています。
resource "aws_subnet" "learn_ec2_public_subnet_1a"
ブロック: これは、パブリックサブネットを作成するためのリソースブロックです。パブリックサブネットは、インターネットに直接アクセス可能なサブネットです。vpc_id
: サブネットを作成するVPCのIDを指定します。この例では、前のVPCリソースで作成したVPCのIDを使用しています。cidr_block
: サブネットで使用するネットワークのCIDRブロックを指定します。この例では、”10.0.1.0/24″ のアドレス範囲が指定されています。availability_zone
: サブネットを配置するアベイラビリティーゾーンを指定します。この例では、変数var.az_a
に指定されたアベイラビリティーゾーンを使用しています。tags
: サブネットにタグを付けるためのマップを指定します。この例では、変数var.tag_name
の値を使って”Name”タグを付けています。
resource "aws_internet_gateway" "learn_ec2_igw"
ブロック: これは、インターネットゲートウェイを作成するためのリソースブロックです。インターネットゲートウェイは、VPCとインターネットの通信を可能にします。vpc_id
: インターネットゲートウェイをアタッチするVPCのIDを指定します。この例では、前のVPCリソースで作成したVPCのIDを使用しています。tags
: インターネットゲートウェイにタグを付けるためのマップを指定します。この例では、変数var.tag_name
の値を使って”Name”タグを付けています。
resource "aws_route_table" "learn_ec2_rt"
ブロック: これは、ルートテーブルを作成するためのリソースブロックです。ルートテーブルは、VPC内のトラフィックの送信先を指定します。vpc_id
: ルートテーブルを作成するVPCのIDを指定します。この例では、前のVPCリソースで作成したVPCのIDを使用しています。tags
: ルートテーブルにタグを付けるためのマップを指定します。この例では、変数var.tag_name
の値を使って”Name”タグを付けています。
resource "aws_route" "learn_ec2_rt_public"
ブロック: これは、パブリックルートを作成するためのリソースブロックです。パブリックルートは、インターネットゲートウェイを経由してインターネットへのトラフィックを送信するために使用されます。route_table_id
: ルートを追加するルートテーブルのIDを指定します。この例では、前のルートテーブルリソースで作成したルートテーブルのIDを使用しています。gateway_id
: ルートの送信先を指定します。この例では、前のインターネットゲートウェイリソースで作成したインターネットゲートウェイのIDを使用しています。destination_cidr_block
: 送信先のネットワークを指定します。この例では、”0.0.0.0/0″ すなわち、すべてのトラフィックを指定しています。
resource "aws_route_table_association" "learn_ec2_rt_associate"
ブロック: これは、サブネットとルートテーブルを関連付けるためのリソースブロックです。これにより、作成したパブリックサブネットが先に作成したパブリックルートテーブルを使用できるようになります。subnet_id
: 関連付けるサブネットのIDを指定します。この例では、前のサブネットリソースで作成したサブネットのIDを使用しています。route_table_id
: 関連付けるルートテーブルのIDを指定します。この例では、前のルートテーブルリソースで作成したパブリックルートテーブルのIDを使用しています。
このTerraformコードを実行すると、以下のようなリソースがAWS上に作成されます:
- VPC(Virtual Private Cloud)リソースが作成されます。CIDRブロック “10.0.0.0/16” のVPCが作成されます。また、VPC内のEC2インスタンスにDNSホスト名が有効になります。
- パブリックサブネットが作成されます。CIDRブロック “10.0.1.0/24” のサブネットがVPC内に作成されます。このサブネットは、インターネットに直接アクセス可能なパブリックサブネットです。
- インターネットゲートウェイが作成されます。このインターネットゲートウェイは、VPCにアタッチされ、インターネット通信を可能にします。
- パブリックルートテーブルが作成されます。このルートテーブルは、パブリックサブネット内のトラフィックをインターネットゲートウェイに送信するためのルートが追加されます。
- パブリックルートがパブリックルートテーブルに追加されます。
destination_cidr_block
が “0.0.0.0/0” なので、このルートはすべてのトラフィックをインターネットゲートウェイに送信します。 - パブリックサブネットとパブリックルートテーブルが関連付けられます。これにより、パブリックサブネット内のEC2インスタンスがインターネットにアクセスできるようになります。
これらのリソースの作成には、Terraformが提供するAWSプロバイダーと各リソースのリソースタイプが使用されています。
output.tf
このコードでは、output
ブロックを使用して “ec2_global_ips” という名前の出力を定義しています。この出力は、aws_instance.learn_ec2_instance.*.public_ip
を値として持ちます。
aws_instance.learn_ec2_instance
は、先に定義された EC2 インスタンスを参照しています。aws_instance
は EC2 インスタンスを作成するための Terraform リソースタイプであり、learn_ec2_instance
はこのリソースの名前です。また、.*
は複数のリソースの要素を表し、すべての EC2 インスタンスを指定しています。.public_ip
は、EC2 インスタンスのパブリックIPアドレスを表しています。public_ip
は EC2 インスタンスの属性であり、インスタンスに割り当てられたパブリックIPアドレスを取得します。
これにより、Terraform実行後に作成されたすべての EC2 インスタンスのパブリックIPアドレスが ec2_global_ips
という名前で表示されるようになります。出力された値は、Terraformの実行結果に表示されるほか、terraform output
コマンドを使用して後から確認することもできます。
variables.tf
これはTerraformの変数定義です。Terraformの変数を使うことで、コード内での値の再利用や変更が簡単になります。以下は、定義された変数の説明です:
variable "az_a"
ブロック: これは、アベイラビリティーゾーン(Availability Zone)を指定するための変数定義です。変数名は “az_a” で、デフォルト値は “ap-northeast-1a” です。アベイラビリティーゾーンは、AWSリージョン内に複数の物理的なデータセンターがある領域を指します。この変数には、AWSの他のリージョンのアベイラビリティーゾーンを指定することもできます。variable "tag_name"
ブロック: これは、リソースに付けるタグの名前を指定するための変数定義です。変数名は “tag_name” で、デフォルト値は “learn-aws-ec2” です。タグはAWSリソースに付けられる名前と値のペアであり、リソースを識別したりグループ化したりするのに役立ちます。この変数には、異なる名前のタグを指定することもできます。
これらの変数定義は、Terraformの設定ファイル内で使用されています。例えば、先に示したコードの中にある以下の部分で変数が使用されています:
availability_zone = var.az_a
ここで var.az_a
は、variable "az_a"
ブロックで定義された変数 “az_a” の値(デフォルト値 “ap-northeast-1a”)を参照しています。同様に、var.tag_name
は variable "tag_name"
ブロックで定義された変数 “tag_name” の値(デフォルト値 “learn-aws-ec2″)を参照しています。これにより、変数の値を変更するだけで、Terraformコードの挙動を簡単にカスタマイズできるようになります。
おわりに
今日は、Terraformを利用したEC2環境の構築についてご紹介します。
次回は、SSH接続できる設定を追加したいと思います。
本記事でご紹介したソースは、下記のリポジトリにあります。
何か質問や相談があれば、遠慮なくコメントしてください。また、エンジニア案件についても、いつでも相談にのっていますので、お気軽にお問い合わせください。
それでは、また明日お会いしましょう(^^)
コメント