はじめに
terraformをつかってAWS(EC2,RDS)を作成します。
全体の流れ
- terraformインストール
 - AWSでIAMユーザー作成
 - AWS CLI導入
4. AWS CLIにIAMユーザーの登録 - terraformとは
 - terraform導入
5. プロバイダーの設定
6. VPC作成
7. サブネット作成
8. インターネットゲートウェイ作成
9. ルートテーブル作成
10. EC2作成
11. Security Groupの作成
11. RDS作成 
参考
terraformインストール
$ brew update
$ brew install terraform
AWSでIAMユーザー作成
参考
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にはテンプレート内の他リソースの属性を参照する方法があります。
具体的には、<リソースの種類>.<リソース名>.<属性名>で他リソースの属性を参照することができます。
その他の知識は下のリンクで
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に上げてはいけないとので追加します。
#  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ができていることを確認
参考
Terraform 変数について
Terraform の変数はvariableブロックで定義
var.<変数名>という書式で参照
外部ファイルに値を定義した場合、terraform.tfvarsなら自動的に読み込まれて変数に代入されます。
また.tfvarsはgithubに挙げないとこが大事です。
参考
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"`と書きましたが、文字で初めて英数字両方を書かないといけないらしいです。
参考
Error creating DB Instance: InvalidParameterValue: Invalid DB engine
これはRDSインスタンスの`engine`の名前の書き方でエラーが出ました。今回は`engine = "postgres"`と書きました。
参考
内容一式
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 = ※※※※※※※※※
おしまい。
何かありましたらコメント欄で