はじめに
terraformをつかってAWS(EC2,RDS)を作成します。
全体の流れ
- terraformインストール
- AWSでIAMユーザー作成
- AWS CLI導入
- AWS CLIにIAMユーザーの登録
- terraformとは
- terraform導入
- プロバイダーの設定
- VPC作成
- サブネット作成
- インターネットゲートウェイ作成
- ルートテーブル作成
- EC2作成
- Security Groupの作成
- RDS作成
参考
* 10分で理解するTerraform
terraformインストール
$ brew update
$ brew install terraform
AWSでIAMユーザー作成
参考
* Identity and Access Management へようこそ
* Terraformで構築するAWS
AdministratorAccessポリシー(管理者権限)を選択してください。
権限が足りないと
Error creating VPC: UnauthorizedOperation: You are not authorized to perform this operation.
とエラーが出ます。
AWS CLI インストール
参考
* AWS CLI のインストールと設定
順番にやればいいですが、私の場合はpythonのパスや環境でハマりました
エラー解決↓
* MacOSとHomebrewとpyenvで快適python環境を。
* Python・Python3のインストール先、パス等確認
* Python2.7からPython3.6をデフォルトにする話
* Python3インストール(Mac編)
上記のリンクを参考にすればイケルと思います。
curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py"
sudo python get-pip.py
sudo pip install awscli
エラーなくインストルされバージョンが出れば成功です
pip --version
aws --version
AWS CLIにIAMユーザーの登録
aws configure
または、
aws configure --profile ユーザー名
順番に記入します
-
AWS CLI の設定
-
AWS-CLIの初期設定のメモ
それぞれ先ほど作成した、IAMユーザーをコピペします。
リージョンと出力形式はリンク先から選んでください。
AWS Access Key ID [None]:
AWS Secret Access Key [None]:
Default region name [None]:
Default output format [None]: json
# Asia Pacific (Tokyo) ap-northeast-1
terraformとは
インフラのコード化であります。GUIを通さずにインフラに変更を加えられコードの共有と再利用ができます。
参考
* インフラの構成管理を自動化するTerraform入門
* Terraformを使ってAWSのVPCを作成してEC2を起動した
ファイル形式は主に.tf
と.tfvars
を使います。
ファイル分割はあとにして全てmain.tf
に書きます。
主なコマンド
# 初期化 Terraformで新しく設定を記述した場合、初期化を行う必要があります。
terraform init
# 確認(所謂dry-run)
terraform plan
# 適用 コードの状態をAWS上へ適用
terraform apply
# すべて消去するコマンド
terraform destroy
# リソースの閲覧
terraform show
準備
mkdir terraform
cd terraform
touch main.tf
touch terraform.tfvars
プロバイダーの設定
最初にproviderという指定をする必要がある。
複数の環境に対応しているため、「どのプロバイダーを使うのか?」を宣言する
providerとは
- その名と通りプロバイダでAWSの他にherokuやGCPもできるらしい
- profileで
aws config
でprofileを指定した場合はその名前、していない場合は"default"
-
region = "ap-northeast-1"
で指定したリージョン内にVPCなどを作っていく
provider "aws" {
profile = ユーザー名
region = "ap-northeast-1"
}
VPCを作成
使うリソースaws_vpc
# VPC
resource "aws_vpc" "aws-tf-vpc" {
cidr_block = "10.1.0.0/16"
instance_tenancy = "default"
tags = {
Name = "aws-tf-vpc"
}
}
実行
terraform init
terraform plan
terraform apply
# リソースの閲覧
terraform show
実際に確かめてみる
aws-tf-vpc
と書いてあるVPC
があれば成功です
resourceとは
resourceはVPCやEC2のような起動したいリソースを定義
リソースの種類は、プロバイダーがAWSの場合はaws_*という名前でTerraformで予め定義されています。VPCであればaws_vpc、EC2であればaws_instanceです。
resourceの構文
リソースはresourceブロックで設定します。resource "<リースの種類>" "<リソース名>" {}という構文です。
resourceの定義と命名
resource "aws_vpc" "this" {
ここでは 「"aws_vpc"というリソースを"this"という名前」 で作成しています。
resource "aws_vpc" まではAWSのVPCを作成するという意味で、 "this" はTerraformで定義する他のリソースから参照する際に使用します。
他リソースの属性の参照
Terraformにはテンプレート内の他リソースの属性を参照する方法があります。
具体的には、<リソースの種類>.<リソース名>.<属性名>で他リソースの属性を参照することができます。
その他の知識は下のリンクで
* AWSでTerraformに入門
vscodeをterraform v0.12対応させる
下記のようなメッセージが出た人向け
For Terraform 0.12 support try enabling the experimental language server with the 'Terraform: Enable/Disable Language Server' command
コマンドパレットを開く(ctl/cmd+shift+p)
terraform: install/update language server -> 現在最新の v0.0.9 を選択
terraform: Enable/Disable Language Server を実行
一度vscodeを閉じたらHCL2記法の.tfファイルでもエラーが出なくなりました。
.gitignoreに上げて行けないファイルの追加
下記のファイルはgithubに上げてはいけないとので追加します。
* Terraform.gitignore
# Local .terraform directories
**/.terraform/*
# .tfstate files
*.tfstate
*.tfstate.*
# .tfvars files
*.tfvars
サブネット作成
使うリソースaws_subnet
# サブネット2つ作成(publicとprivate)
resource "aws_subnet" "aws-tf-public-subnet-1a" {
vpc_id = aws_vpc.aws-tf-vpc.id
cidr_block = "10.1.1.0/24"
availability_zone = "ap-northeast-1a"
tags = {
Name = "aws-tf-public-subnet-1a"
}
}
resource "aws_subnet" "aws-tf-private-subnet-1a" {
vpc_id = aws_vpc.aws-tf-vpc.id
cidr_block = "10.1.20.0/24"
availability_zone = "ap-northeast-1a"
tags = {
Name = "aws-tf-private-subnet-1a"
}
}
インターネットゲートウェイの作成
使うリソースaws_internet_gateway
resource "aws_internet_gateway" "aws-tf-igw" {
vpc_id = aws_vpc.aws-tf-vpc.id
tags = {
Name = "aws-tf-igw"
}
}
ルートテーブルの作成
使うリソースaws_route_table
resource "aws_route_table" "aws-tf-public-route" {
vpc_id = aws_vpc.aws-tf-vpc.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.aws-tf-igw.id
}
tags = {
Name = "aws-tf-public-route"
}
}
サブネットの関連付けでルートテーブルをパブリックサブネットに紐付け
使うリソースaws_route_table_association
resource "aws_route_table_association" "aws-tf-public-subnet-association" {
subnet_id = aws_subnet.aws-tf-public-subnet-1a.id
route_table_id = aws_route_table.aws-tf-public-route.id
}
EC2作成(public側に作る)
使うリソースaws_instance
、aws_key_pair
resource "aws_instance" "aws-tf-web" {
ami = "ami-011facbea5ec0363b"
instance_type = "t2.micro"
disable_api_termination = false
key_name = aws_key_pair.auth.key_name
vpc_security_group_ids = [aws_security_group.aws-tf-web.id]
subnet_id = aws_subnet.aws-tf-public-subnet-1a.id
tags = {
Name = "aws-tf-web"
}
}
# amiとはAmazon Linux 2 AMIです。今回は実際のamiの値を直接入れてますが、いつも最新版にできます。
variable "public_key_path" {}
resource "aws_key_pair" "auth" {
key_name = "terraform-aws"
public_key = file(var.public_key_path)
}
# ローカルに鍵がある場所を指定
public_key_path = "~/.ssh/terraform-aws.pub"
amiを常に最新版にする
インスタンスのkey_nameについて
GUIで作るときはインスタンス作成時にsshキーを新規作成や既存のキーを使いAWSにアクセスしますが、
terraformで作成する場合は.tfvars
ファイルから参照して公開鍵をインスタンスのkey_name
に貼り付けます。
私はterraform-aws
という名前で鍵を作成しました。
$ cd .ssh
$ ssh-keygen -t rsa
terraform-aws 今回の場合の名前
ここでエンターキーを2連打
$ ls
ここでterraform-aws terraform-aws.pubができていることを確認
参考
* Resource: aws_key_pair
* Terraform でキーペア登録し起動した EC2 に SSH接続
Terraform 変数について
Terraform の変数はvariable
ブロックで定義
var.<変数名>
という書式で参照
外部ファイルに値を定義した場合、terraform.tfvars
なら自動的に読み込まれて変数に代入されます。
また.tfvars
はgithubに挙げないとこが大事です。
参考
* 【Terraform 再入門】EC2 + RDS によるミニマム構成な AWS 環境をコマンドライン一発で構築してみよう
Security Groupの作成
使うリソースaws_security_group
、aws_security_group_rule
resource "aws_security_group" "aws-tf-web" {
name = "aws-tf-web"
description = "aws-tf-web_sg"
vpc_id = aws_vpc.aws-tf-vpc.id
tags = {
Name = "aws-tf-web"
}
}
# 80番ポート許可のインバウンドルール
resource "aws_security_group_rule" "inbound_http" {
type = "ingress"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = [
"0.0.0.0/0",
]
# ここでweb_serverセキュリティグループに紐付け
security_group_id = aws_security_group.aws-tf-web.id
}
# 22番ポート許可のインバウンドルール
resource "aws_security_group_rule" "inbound_ssh" {
type = "ingress"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [
"0.0.0.0/0",
]
# ここでweb_serverセキュリティグループに紐付け
security_group_id = aws_security_group.aws-tf-web.id
}
# アウトバウンドルール
resource "aws_security_group_rule" "outbound_all" {
type = "egress"
from_port = 0
to_port = 0
protocol = -1
cidr_blocks = [
"0.0.0.0/0",
]
# ここでweb_serverセキュリティグループに紐付け
security_group_id = aws_security_group.aws-tf-web.id
}
Security Group詳細
ElasticIP作成
使うリソースaws_eip
resource "aws_eip" "aws-tf-eip" {
instance = aws_instance.aws-tf-web.id
vpc = true
}
output "example-public-ip" {
value = "${aws_eip.aws-tf-eip.public_ip}"
}
outputについて
ElasticIPなどはGUIから確認できますが、output
ブロックで記述するとterraform apply
実行時に下記のように出力されます。
Apply complete!
Outputs:
example-public-ip = ElasticIP
RDSの作成
RDSは2つのサブネットが必要なのであと一つ違うアベイラビリティゾーンから作成します。
使うリソースaws_subnet
、aws_db_subnet_group
# RDS用のサブネットを作成
resource "aws_subnet" "aws-tf-private-subnet-1c" {
vpc_id = aws_vpc.aws-tf-vpc.id
cidr_block = "10.1.3.0/24"
availability_zone = "ap-northeast-1c"
tags = {
Name = "aws-tf-private-subnet-1c"
}
}
# 使用する2つを指定します。
resource "aws_db_subnet_group" "rdb-tf-db" {
name = "rdb-tf-db-subnet"
description = "It is a DB subnet group on tf_vpc."
subnet_ids = [aws_subnet.aws-tf-private-subnet-1a.id,aws_subnet.aws-tf-private-subnet-1c.id]
tags = {
Name = "rdb-tf-db"
}
}
DB用のセキュリティーを作成
WebサーバーからのみDBサーバーにアクセスできるようにするためにセキュリティを作ります。
使うリソースaws_security_group
、aws_security_group_rule
# Security Group
resource "aws_security_group" "aws-tf-db" {
name = "aws-tf-db"
description = "aws-tf-db-sg"
vpc_id = aws_vpc.aws-tf-vpc.id
tags = {
Name = "aws-tf-db"
}
}
resource "aws_security_group_rule" "db" {
type = "ingress"
from_port = 5432
to_port = 5432
protocol = "tcp"
source_security_group_id = aws_security_group.aws-tf-web.id
security_group_id = aws_security_group.aws-tf-db.id
}
# source_security_group_idとはアクセスを許可するセキュリティグループIDつまりWeb側のセキュリティグループを指します。
RDSインスタンスの作成
使うリソースaws_db_instance
variable "aws-td-db-username" {}
variable "aws-td-db-password" {}
resource "aws_db_instance" "aws-td-db" {
identifier = "aws-td-db"
allocated_storage = 20
name = "db11"
engine = "postgres"
engine_version = "11.5"
instance_class = "db.t2.micro"
storage_type = "gp2"
username = var.aws-td-db-username
password = var.aws-td-db-password
vpc_security_group_ids = [aws_security_group.aws-tf-db.id]
db_subnet_group_name = aws_db_subnet_group.rdb-tf-db.name
}
public_key_path = "~/.ssh/terraform-aws.pub"
aws-td-db-username = ※※※※※※※※※※
aws-td-db-password = ※※※※※※※※※
それぞれ指定してください。
実行
terraform plan
terraform apply
# 確認
terrafom show
RDSインスタンス作成時にハマったエラーについて
DBName must begin with a letter and contain only alphanumeric characters
これはRDSインスタンスのnameについてのエラーです。私の場合は`name = "db11"`と書きましたが、文字で初めて英数字両方を書かないといけないらしいです。
参考
* AWS aws_db_instance DBName issue
Error creating DB Instance: InvalidParameterValue: Invalid DB engine
これはRDSインスタンスの`engine`の名前の書き方でエラーが出ました。今回は`engine = "postgres"`と書きました。
参考
* "Invalid DB engine" when creating AWS/RDS Postgresql instance
* Engineのセクションを見てください
内容一式
provider "aws" {
profile = プロフィール名
region = "ap-northeast-1"
}
# VPC作成
resource "aws_vpc" "aws-tf-vpc" {
cidr_block = "10.1.0.0/16"
instance_tenancy = "default"
enable_dns_support = "true"
enable_dns_hostnames = "true"
tags = {
Name = "aws-tf-vpc"
}
}
# サブネット2つ作成(publicとprivate)
resource "aws_subnet" "aws-tf-public-subnet-1a" {
vpc_id = aws_vpc.aws-tf-vpc.id
cidr_block = "10.1.1.0/24"
availability_zone = "ap-northeast-1a"
tags = {
Name = "aws-tf-public-subnet-1a"
}
}
resource "aws_subnet" "aws-tf-private-subnet-1a" {
vpc_id = aws_vpc.aws-tf-vpc.id
cidr_block = "10.1.20.0/24"
availability_zone = "ap-northeast-1a"
tags = {
Name = "aws-tf-private-subnet-1a"
}
}
# インターネットゲートウェイの作成
resource "aws_internet_gateway" "aws-tf-igw" {
vpc_id = aws_vpc.aws-tf-vpc.id
tags = {
Name = "aws-tf-igw"
}
}
# ルートテーブルの作成
resource "aws_route_table" "aws-tf-public-route" {
vpc_id = aws_vpc.aws-tf-vpc.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.aws-tf-igw.id
}
tags = {
Name = "aws-tf-public-route"
}
}
# サブネットの関連付けでルートテーブルをパブリックサブネットに紐付け
resource "aws_route_table_association" "aws-tf-public-subnet-association" {
subnet_id = aws_subnet.aws-tf-public-subnet-1a.id
route_table_id = aws_route_table.aws-tf-public-route.id
}
# EC2作成(public側)
resource "aws_instance" "aws-tf-web" {
ami = "ami-011facbea5ec0363b"
instance_type = "t2.micro"
disable_api_termination = false
key_name = aws_key_pair.auth.key_name
vpc_security_group_ids = [aws_security_group.aws-tf-web.id]
subnet_id = aws_subnet.aws-tf-public-subnet-1a.id
tags = {
Name = "aws-tf-web"
}
}
variable "public_key_path" {}
resource "aws_key_pair" "auth" {
key_name = "terraform-aws"
public_key = file(var.public_key_path)
}
# Security Group
resource "aws_security_group" "aws-tf-web" {
name = "aws-tf-web"
description = "aws-tf-web_sg"
vpc_id = aws_vpc.aws-tf-vpc.id
tags = {
Name = "aws-tf-web"
}
}
# 80番ポート許可のインバウンドルール
resource "aws_security_group_rule" "inbound_http" {
type = "ingress"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = [
"0.0.0.0/0",
]
# ここでweb_serverセキュリティグループに紐付け
security_group_id = aws_security_group.aws-tf-web.id
}
# 22番ポート許可のインバウンドルール
resource "aws_security_group_rule" "inbound_ssh" {
type = "ingress"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [
"0.0.0.0/0",
]
# ここでweb_serverセキュリティグループに紐付け
security_group_id = aws_security_group.aws-tf-web.id
}
# アウトバウンドルール
resource "aws_security_group_rule" "outbound_all" {
type = "egress"
from_port = 0
to_port = 0
protocol = -1
cidr_blocks = [
"0.0.0.0/0",
]
# ここでweb_serverセキュリティグループに紐付け
security_group_id = aws_security_group.aws-tf-web.id
}
# ElasticIP
resource "aws_eip" "aws-tf-eip" {
instance = aws_instance.aws-tf-web.id
vpc = true
}
output "example-public-ip" {
value = aws_eip.aws-tf-eip.public_ip
}
####RDSの作成
# RDS用のサブネットを作成
resource "aws_subnet" "aws-tf-private-subnet-1c" {
vpc_id = aws_vpc.aws-tf-vpc.id
cidr_block = "10.1.3.0/24"
availability_zone = "ap-northeast-1c"
tags = {
Name = "aws-tf-private-subnet-1c"
}
}
# DB用のセキュリティーを作成
# Security Group
resource "aws_security_group" "aws-tf-db" {
name = "aws-tf-db"
description = "aws-tf-db-sg"
vpc_id = aws_vpc.aws-tf-vpc.id
tags = {
Name = "aws-tf-db"
}
}
resource "aws_security_group_rule" "db" {
type = "ingress"
from_port = 5432
to_port = 5432
protocol = "tcp"
source_security_group_id = aws_security_group.aws-tf-web.id
security_group_id = aws_security_group.aws-tf-db.id
}
resource "aws_db_subnet_group" "rdb-tf-db" {
name = "rdb-tf-db-subnet"
description = "It is a DB subnet group on tf_vpc."
subnet_ids = [aws_subnet.aws-tf-private-subnet-1a.id,aws_subnet.aws-tf-private-subnet-1c.id]
tags = {
Name = "rdb-tf-db"
}
}
variable "aws-td-db-username" {}
variable "aws-td-db-password" {}
resource "aws_db_instance" "aws-td-db" {
identifier = "aws-td-db"
allocated_storage = 20
name = "db11"
engine = "postgres"
engine_version = "11.5"
instance_class = "db.t2.micro"
storage_type = "gp2"
username = var.aws-td-db-username
password = var.aws-td-db-password
vpc_security_group_ids = [aws_security_group.aws-tf-db.id]
db_subnet_group_name = aws_db_subnet_group.rdb-tf-db.name
}
public_key_path = "~/.ssh/terraform-aws.pub"
aws-td-db-username = ※※※※※※※※※※
aws-td-db-password = ※※※※※※※※※
おしまい。
何かありましたらコメント欄で