はじめに
今回は遂にTerraformを触ってみました!
業務でCloudFormationを使用していたので、よく比較されるTerraformは前からやってみたいと思っていました
Terraformを使うとAWSだけでなく、他SaaS等もIaC管理出来て、実PJでもかなり需要がありそうと感じていました
私の学習の備忘録のような形で実施したことをまとめたので、同じようにこれからTerraformを初めて触る方や、TerraformでAWSを構築する方の参考になれば幸いです
ハンズオンの流れ
1. Terraformインストール
以下からTerraformをインストールする
https://developer.hashicorp.com/terraform
ダウンロードしたZipファイルを解凍して、任意のフォルダに保存
例: C:\Windows\terraform\ に「すべて展開」で保存等

「Windows+R」>「cmd」でターミナルを開き、以下を実行
C:\Windows\terraform\terraform -v
2. tflintインストール
次にtflintをインストールする
以下のサイトにアクセス
https://github.com/terraform-linters/tflint/releases
「Assets」>「tflint_windows_amd64.zip」を押下してダウンロード

ダウンロードしたZipファイルを解凍して、先ほどと同じフォルダに保存
例: C:\Windows\terraform\ に「すべて展開」で保存等
先ほどと同様にターミナルで以下を実行
C:\Windows\terraform\tflint -v
3. 環境変数設定
次に環境変数を設定する
管理者権限でPowerShellを開き、以下を実行
パスの部分はご自身の保存したフォルダに応じて変えてください
$env:Path += ";C:\Windows\terraform"
[Environment]::SetEnvironmentVariable('PATH', $Env:Path, 'Machine')
以下を実行して、設定できたことを確認
terraform -v
tflint -v
4. VS Code拡張機能設定
次にVS Codeに拡張機能を設定する
VS Codeの拡張機能(Extensions)より「terraform」で検索し、
「HashiCorp Terraform」をインストール

5. AWSと連携するためのSSOプロファイル設定
以下を参考にAWSとTerraformを連携する為に、SSOプロファイルを設定した
AWSへログイン
IAM Identy Centerを開き、「有効にする」を押下
初めてIAM Identy Centerを使用する場合のみ実行
組織インスタンスが作成完了(アカウントインスタンスでは許可セット/アカウントへのアクセスできないため組織インスタンスを作成した)

ユーザを作成する
作成した組織インスタンス>ユーザーを開き、「ユーザーを追加」を押下

登録したメールアドレスにメールが来ているので、「Accept Invitation」を押下
PWを設定した後MFAを設定
その後Your AWS access portal URLよりSSOユーザーでサインできるかを確認

組織インスタンス>許可セットを開き、「許可セットを作成」を押下

マネージドポリシーで、必要なポリシーを選択
今回はVPC+EC2+ALB+S3を作成しようと思うので、以下を追加
- AmazonEC2FullAccess
- AmazonS3FullAccess
- ElasticLoadBalancingFullAccess
インラインポリシーに以下を追加
AWSアカウントIDはご自身のAWSアカウントIDに置き換える
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "CreateAndManageEc2SsmRoleOnly",
"Effect": "Allow",
"Action": [
"iam:CreateRole",
"iam:GetRole",
"iam:UpdateAssumeRolePolicy",
"iam:DeleteRole",
"iam:TagRole",
"iam:UntagRole",
"iam:ListRolePolicies",
"iam:ListAttachedRolePolicies",
"iam:ListInstanceProfilesForRole"
],
"Resource": "arn:aws:iam::<AWSアカウントID>:role/ec2-ssm-*"
},
{
"Sid": "AttachOnlyAmazonSSMManagedInstanceCore",
"Effect": "Allow",
"Action": [
"iam:AttachRolePolicy",
"iam:DetachRolePolicy",
"iam:ListAttachedRolePolicies"
],
"Resource": "arn:aws:iam::<AWSアカウントID>:role/ec2-ssm-*",
"Condition": {
"ArnEquals": {
"iam:PolicyARN": "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}
}
},
{
"Sid": "CreateAndManageInstanceProfileOnly",
"Effect": "Allow",
"Action": [
"iam:CreateInstanceProfile",
"iam:GetInstanceProfile",
"iam:AddRoleToInstanceProfile",
"iam:RemoveRoleFromInstanceProfile",
"iam:DeleteInstanceProfile"
],
"Resource": "arn:aws:iam::<AWSアカウントID>:instance-profile/ec2-ssm-*"
},
{
"Sid": "PassOnlyEc2SsmRoleToEC2",
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "arn:aws:iam::<AWSアカウントID>:role/ec2-ssm-*",
"Condition": {
"StringEquals": {
"iam:PassedToService": "ec2.amazonaws.com"
}
}
}
]
}
「次へ」を押下
許可セットに任意の名前を命名
またセッション時間は作業時間を考慮し、今回は8時間とした
「次へ」を押下

次にアカウントとユーザーの紐づけ、および先ほど作成した許可セットの紐づけを行う
AWSアカウントを開き、表示されているツリー内の該当AWSアカウントを選択し、「ユーザーまたはグループを割り当て」を押下

AWSアカウント>表示されているツリー内の該当AWSアカウントを押下し、ユーザーと許可セットが紐づいたことを確認

SSOユーザーでAWS access portalに再度ログインすると、以下のようにアカウントが表示されていることを確認

PowerShellで以下を実行
以下を参考にCLIと対話式で回答していくことで、AWS Configを作成可能
aws configure sso
SSO session name (Recommended): tf-user
SSO start URL [None]: https://XXXXXXX.awsapps.com/start/ # SSOユーザーのAWS access portalのURL(メールから確認可能)
SSO region [None]: ap-northeast-1
SSO registration scopes [sso:account:access]: # そのままEnter
Attempting to automatically open the SSO authorization page in your default browser.
If the browser does not open or you wish to use a different device to authorize this request, open the following URL:
https://d-XXXXXXXX.awsapps.com/start/#/device
Then enter the code:
XXXX-ZZZZ # 認証コード
再度PowerShellに戻り、以下を回答
The only AWS account available to you is: XXXXXXXX
Using the account ID XXXXXXX
The only role available to you is: tf-permission-set-ec2-s3-elb
Using the role name "tf-permission-set-ec2-s3-elb"
CLI default client Region [ap-northeast-1]: ap-northeast-1
CLI default output format [None]: json
CLI profile name [tf-permission-set-ec2-s3-elb-740625119642]: dev-tf-profile
Powershellで以下を実行し、~/.aws/configを確認
type ~/.aws/config
以下コマンドでログインを実行
aws sso login --profile dev-tf-profile
先ほどと同様に認証画面へ遷移するので、同様の手順で進める
6. tfファイル作成
まずはVPCを作ってみる
以下main.tfを作成
provider "aws" {
region = "ap-northeast-1"
profile = "dev-tf-profile"
}
resource "aws_vpc" "tf-test-vpc" {
cidr_block = "192.168.16.0/20"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "tf-test-vpc"
}
}
resource "aws_subnet" "tf-private-subnet-1a" {
vpc_id = aws_vpc.tf-test-vpc.id
cidr_block = "192.168.16.0/24"
availability_zone = "ap-northeast-1a"
tags = {
Name = "tf-private-subnet-1a"
}
}
resource "aws_subnet" "tf-public-subnet-1a" {
vpc_id = aws_vpc.tf-test-vpc.id
cidr_block = "192.168.17.0/24"
availability_zone = "ap-northeast-1a"
map_public_ip_on_launch = true
tags = {
Name = "tf-public-subnet-1a"
}
}
7. terraform実行準備(VS Code)
今回はVS Codeのターミナルで実行するため、環境変数を設定
VS Code>Terminalを開き、以下を実行
パスは自身の環境に応じて変更
$env:Path += ";C:\Windows\terraform"
[Environment]::SetEnvironmentVariable('PATH', $Env:Path)
VS Codeのターミナルで以下を実行し、正常にVerが表示されることを確認
terraform -v
tflint -v
まずは再度SSOユーザーでログイン
aws sso login --profile dev-tf-profile
AWSのプロファイルとリージョンは環境変数として設定
$env:AWS_PROFILE="dev-tf-profile"
$env:AWS_REGION="ap-northeast-1"
次にmain.tfを作成した階層へ移動
以下を実行して、main.tfが表示されることを確認
ls
8. Terraform実行
以下を実行して、整形
terraform fmt -recursive
以下を実行して、初期化(初回/Provider更新時に実行要)
initはTerraformがこのコードを実行できる環境を作るイメージ
terraform init
以下を実行して、文法チェック
terraform validate
以下を実行して、設計、ベストプラクティスのチェック
tflint
すると、以下が表示された
Warning: terraform "required_version" attribute is required (terraform_required_version)
on main.tf line 1:
Reference: https://github.com/terraform-linters/tflint-ruleset-terraform/blob/v0.13.0/docs/rules/terraform_required_version.md
Warning: Missing version constraint for provider "aws" in `required_providers` (terraform_required_providers)
on main.tf line 26:
26: resource "aws_subnet" "tf-public-subnet-1a" {
Reference: https://github.com/terraform-linters/tflint-ruleset-terraform/blob/v0.13.0/docs/rules/terraform_required_providers.md
調査した結果、TerraformのVerとProviderのVerを固定化していないという内容だった
この必要なVerを定義しておかないと、実行環境(PC等)によってVerがバラバラになり、意図しないエラーが発生する恐れがあるとのこと
versions.tfを作成し、以下を定義
私の環境に合わせて、Verを定義しているのでご自身の環境に合わせて適宜Verは変更してください
terraform {
required_version = ">= 1.14.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0"
}
}
}
変更したので再度以下を実行
terraform fmt -recursive
terraform init -upgrade
tflint
今度はtflintを実行しても何も表示されなくなった
以下を実行して、差分確認(実際に作成されるリソースの情報を確認)
terraform plan -out tfplan
特にエラー等なければ、最後以下を実行して、リソースを作成
terraform apply tfplan
tfplanを使用してApplyすることでtfplanで確認した内容が環境に反映されるため、再現性がある
AWSに作成したVPCを見に行く
出来たのもつかの間、さっそく削除してみます
以下を実行して、削除
terraform destroy
9. より実践的なファイル構成に変更
ここからはChat GPTにより実践的なファイル構成、コードにリファクタリングしてもらい、色々と試してみました
これから学習していくので、まだおかしなところ多々あると思いますが、ご了承ください
構成は以下
project/
├─ versions.tf
├─ provider.tf
├─ main.tf
├─ variables.tf
├─ outputs.tf
各コードの詳細は以下
terraform {
required_version = ">= 1.6.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0"
}
}
}
provider "aws" {
region = var.aws_region
profile = var.aws_profile
}
locals {
common_tags = {
Project = var.project_name
Managed = "terraform"
}
}
resource "aws_vpc" "this" {
cidr_block = var.vpc_cidr
enable_dns_support = true
enable_dns_hostnames = true
tags = merge(
local.common_tags,
{ Name = "${var.project_name}-vpc" }
)
}
resource "aws_subnet" "private_1a" {
vpc_id = aws_vpc.this.id
cidr_block = var.private_subnet_cidr_1a
availability_zone = var.az_1a
tags = merge(
local.common_tags,
{ Name = "${var.project_name}-private-subnet-1a" }
)
}
resource "aws_subnet" "public_1a" {
vpc_id = aws_vpc.this.id
cidr_block = var.public_subnet_cidr_1a
availability_zone = var.az_1a
map_public_ip_on_launch = true
tags = merge(
local.common_tags,
{ Name = "${var.project_name}-public-subnet-1a" }
)
}
variable "aws_region" {
description = "AWS region"
type = string
default = "ap-northeast-1"
}
variable "aws_profile" {
description = "AWS CLI profile name (SSO profile)"
type = string
default = "dev-tf-profile"
}
variable "project_name" {
description = "Name prefix for resources"
type = string
default = "tf-test"
}
variable "vpc_cidr" {
description = "VPC CIDR block"
type = string
default = "192.168.16.0/20"
}
variable "az_1a" {
description = "Availability Zone for subnets"
type = string
default = "ap-northeast-1a"
}
variable "private_subnet_cidr_1a" {
description = "Private subnet CIDR in az_1a"
type = string
default = "192.168.16.0/24"
}
variable "public_subnet_cidr_1a" {
description = "Public subnet CIDR in az_1a"
type = string
default = "192.168.17.0/24"
}
output "vpc_id" {
value = aws_vpc.this.id
description = "Created VPC ID"
}
output "private_subnet_id_1a" {
value = aws_subnet.private_1a.id
description = "Created private subnet ID (1a)"
}
output "public_subnet_id_1a" {
value = aws_subnet.public_1a.id
description = "Created public subnet ID (1a)"
}
各ファイルの役割となぜ分けるのかは以下(Chat GPTの解説をもとに自分なりにまとめました)
versions.tf
実行するTerraformとProviderのバージョンを固定化
変更頻度が低いため、ファイルを分ける
provider.tf
接続設定として、TerraformがどのAWSアカウント/リージョンに接続するかを定義
開発環境や本番環境等、環境ごとにここだけ変えることが多いので、ファイルを分ける
variables.tf
各コードのパラメータの設定値を定義(変更可能な設定値が可視化)
main.tfのハードコーディングを避ける
またコードの再利用性を高めるために、ファイルを分ける
環境分離(dev/prod)が可能となる
main.tf
AWSリソースを定義
outputs.tf
作成したリソースのIDやARN等の情報を表示し、他モジュールや環境から参照可能とする
将来的にモジュール化、環境分離をするためにも上記の構成が必要らしい
上記に変更し、再度以下を実行してリソースを作成(上記ファイルを作成した階層まで移動していることが前提)
terraform fmt -recursive
terraform init
tflint
terraform validate
terraform plan -out tfplan
terraform apply tfplan
作成成功画面
先ほどと異なり、Outputとして作成されたVPCのID等が表示されている

最後に再度作成したリソースを削除
terraform destroy
まとめ
念願のTerraformを触ってみた第一印象は、思ったより簡単にAWSのリソースができる!でした
勝手なイメージで最初のハードルが高いのかなと思っていましたが、自分はCloudFormationを使ってきて、IaCについて業務経験が2年ほどあるので、Terraformの考え方もすっと理解することができました
とはいえ、実運用を想定するとまだまだ学習していかないといけない要素がたくさんあることも見えてきたので、次回はそちらに取り組みたいです
参考にさせていただいたサイト







































