AWS †
AWS Provider Version 3でエラー †
- aws providerのバージョンをv2.xに固定する。
- ソースをAWS Provider Version 3対応に修正する
- ソースをv3対応後
is set of object with XX elements †
既存リソースの参照 †
- コード中に直接リソースIDを書くと、汎用性がなくなるので避けたい
- dataリソースで、作成済みのリソースを参照できる。
- 作成済みのリソースでは、dataリソースから参照しやすいtagを付ける
data.aws_instance(s): 既存EC2の参照 †
data "aws_instances" "web" {
instance_tags = {
"Name" = "web-*"
}
instance_state_names = ["running", "stopped"]
}
# 参照する場合
# count = length(data.aws_instances.web.ids)
# data.aws_instances.web.public_ips[count.index]
AMI †
s3 bucketとobjectの生成 †
- terraform v0.12.12
- ループで複数オブジェクトを作成できるが、数が増えると遅そう。遅い場合aws-cliの 「aws s3 sync」を検討した方が良い。
resource "aws_s3_bucket" "example" {
bucket = "example-bucket"
acl = "private"
}
locals {
s3_objects = [
{
key : "index.html"
source : "contents/index.html"
},
{
key: "top.html"
source: "contents/top.html"
}
]
}
resource "aws_s3_bucket_object" "object" {
count = length(local.s3_objects)
bucket = aws_s3_bucket.example.id
key = local.s3_objects[count.index].key
source = local.s3_objects[count.index].source
}
root_block_device,ebs_block_deviceで分けて処理をしたい †
- Issueで同様の要望が上がっているが、解決はしていない。
- aws_instance内のvolume_tags だと、root_block_device, ebs_block_device 両方に同じタグが付く
- ebs_block_device だけに任意のタグを付けたい場合。
- 新規構築であれば、aws_instance 内に ebs_block_device を書かない。後から変更できなくなる。「aws_ebs_volume」「aws_volume_attachment」と分けて書いた方が良さそう。ただ、その場合「delete_on_termination」が効かないので、手動でEC2をterminateした場合、追加したEBSはそのまま残る。
- terraform v0.12.9: 「aws_instance.web[0].ebs_block_device[0].volume_id」がエラーになる。
resource "null_resource" "backup_web_ebs1" {
count = length(aws_instance.web)
provisioner "local-exec" {
command = <<EOD
aws ec2 create-tags \
--tags Key=backup:DailyBackup7Days,Value=${count.index == 0 ? true : false} \
--resources ${element(aws_instance.web[count.index].ebs_block_device[*].volume_id, 0)} \
--profile ${var.aws_profile} \
--region ${var.aws_region}
EOD
}
}
- terraform v0.11.14: 一度変数(locals)を使う必要があった。
locals {
web_ebs1 = "${flatten(aws_instance.web.*.ebs_block_device)}"
}
resource "null_resource" "backup_web_ebs1" {
count = "${length(aws_instance.web.*.id)}"
provisioner "local-exec" {
command = <<EOD
aws ec2 create-tags \
--tags Key=backup:DailyBackup7Days,Value=${count.index == 0 ? true : false} \
--resources ${lookup(local.web_ebs1[count.index], "volume_id")} \
--profile ${var.aws_profile} \
--region ${var.aws_region}
EOD
}
}
aws_instance 内の ebs_block_device の volume_id の参照 †
output "ebs_test01" {
value = "${aws_instance.web[0].ebs_block_device}"
}
# 結果
ebs_test01 = [
{
"delete_on_termination" = true
"device_name" = "/dev/sdf"
"encrypted" = false
"iops" = 100
"kms_key_id" = ""
"snapshot_id" = ""
"volume_id" = "vol-0123456789abcdef"
"volume_size" = 5
"volume_type" = "gp2"
},
]
output "ebs_test02" {
value = "${aws_instance.web[0].ebs_block_device[0].volume_id}"
}
# 結果: エラー
# Block type "ebs_block_device" is represented by a set of objects, and set
# elements do not have addressable keys. To find elements matching specific
# criteria, use a "for" expression with an "if" clause.
output "ebs_test03" {
value = "${element(aws_instance.web[0].ebs_block_device[*].volume_id, 0)}"
}
# 結果
ebs_test03 = vol-0123456789abcdef
- terraform v0.11.14: 構文解析がおかしい。element()がlist[map{}]構造だとエラーになる。
output "ebs_test01" {
value = "${aws_instance.web.*.ebs_block_device}"
}
# result
ebs_test01 = [
[
map[delete_on_termination:1 device_name:/dev/sdf encrypted:1 iops:100 snapshot_id: volume_id:vol-0123456789abcdef volume_size:5 volume_type:gp2]
],
[
map[delete_on_termination:1 device_name:/dev/sdf encrypted:1 iops:100 snapshot_id: volume_id:vol-0f50829f1dfbc02ca volume_size:5 volume_type:gp2]
]
]
output "ebs_test02" {
value = "${lookup(aws_instance.web.0.ebs_block_device[0], "volume_id")}"
}
# result
ebs_test02 = vol-0123456789abcdef
output "ebs_test04" {
value = "${slice(flatten(aws_instance.web.*.ebs_block_device[0]), 0, 1)}"
}
# result
ebs_test04 = [
{
delete_on_termination = 1,
device_name = /dev/sdf,
encrypted = 1,
iops = 100,
snapshot_id = ,
volume_id = vol-0123456789abcdef,
volume_size = 5,
volume_type = gp2
}
]
ACM †
EC2を作る時は、リソース毎に分ける †
AWS上のIDが分かれているリソース(EC2, EBS, SecurityGroup)を作る場合、ID毎にリソースを分けて作成する事で保守性が上がる。
- aws_instance
- ebs_block_device をこのリソース内で定義できるが、apply後、EBSを追加しようとinstanceごと破棄 -> 再生成しようとするので保守性が悪い。
SNSメッセージをSlackへ通知 †
- LambdaのDEBUGログ等は、CloudWatch logs > ロググループ > /aws/lambda/sns-to-slack のストリームに出力される。
- lambda_function.py の中にテスト用jsonが埋め込まれている。
- sns_event_template の ['Records'][0]['Sns']['Message'] 部分。「DEBUG EVENT:」はこの部分が出力される。
- slackへ送信される部分は DEBUG PAYLOAD: としてCloudWatch logsへ記録される
Lambda †
CloudFront †
WAF †
policy jsonの代わりに aws_iam_policy_document を使う †
terraformのドキュメントの例には「policy = <json>」のように書いてある部分もあるが、毎回差分が出る不具合があるので、aws_iam_policy_documentを使うと良さそう。
- s3の場合、policyを書けるresourceが2つある。どちらか1箇所に書く。基本的に変更単位が最小になる方を使えば良い。
- s3.tf: exampleバケットに対して、Webhosting用のPublic Read、IP制限をする場合
resource "aws_s3_bucket_policy" "example" {
bucket = "${aws_s3_bucket.example.id}"
policy = "${data.aws_iam_policy_document.s3_example_public_read.json}"
}
data "aws_iam_policy_document" "s3_example_public_read" {
statement {
sid = "IPAllow"
effect = "Allow"
principals {
type = "*"
identifiers = ["*"]
}
actions = [
"s3:GetObject",
]
resources = [
"arn:aws:s3:::example/*",
]
condition {
test = "IpAddress"
variable = "aws:SourceIp"
values = [
"192.0.2.0/24", # TEST-NET-1
"198.51.100.0/24",# TEST-NET-2
"203.0.113.0/24", # TEST-NET-3
]
}
}
}
既存EC2にIAM roleを付けたい †
セキュリティグループのルールはaws_security_group_ruleを使う †
- ルールは aws_security_group_rule 推奨。
- aws_security_groupにもruleは書けるが、plan時に順番が変わって、毎回diffとして表示されてしまう。diffも見難い。
- aws_security_groupとaws_security_group_ruleで、ルールを混在させない。混在すると、aplly後のplanで差分が出て、片方が消える。
No valid credential sources found for AWS Provider. †
tfファイル内で複数AWSアカウント、複数リージョンを扱う †
VPC Peering †
- 異なるAWSアカウント間で、VPC peeringを設定する
- アクセプタ側でVPC > ピア接続 > 選択 > アクション > 許可する
- 手動で許可する場合、terraform applyが必ず失敗する(auto_accept = false でも)。許可した後、もう一度実行が必要
- iam.tf
output "aws_iam_role.vpc_peering.arn" { value = "${aws_iam_role.vpc_peering.arn}" }
resource "aws_iam_role" "vpc_peering" {
name = "main-vpcpeering"
assume_role_policy = "${data.aws_iam_policy_document.sts-assumerole.json}"
}
# AWS console上だとIAM > ロール > ロール名 > 信頼関係タブの部分
data "aws_iam_policy_document" "sts-assumerole" {
statement {
actions = [
"sts:AssumeRole",
]
effect = "Allow"
principals = {
type = "AWS"
identifiers = [
"arn:aws:iam::${var.peer_aws_account_id}:root",
]
}
}
}
# AWS console上だとIAM > ロール > ロール名 > アクセス許可タブ > インラインポリシーの部分
resource "aws_iam_role_policy" "vpc_peering" {
name = "main-vpcpeering"
role = "${aws_iam_role.vpc_peering.id}"
policy = "${data.template_file.vpc_peering.rendered}"
}
data "template_file" "vpc_peering" {
template = "${file("./policy/vpc-peering-connection.tpl")}"
vars {
aws_region = "${var.aws_region}"
main_aws_account_id = "${data.aws_caller_identity.main.account_id}"
main_aws_vpc_id = "${var.main_aws_vpc_id}"
peer_aws_account_id = "${var.peer_aws_account_id}"
peer_aws_vpc_id = "${var.peer_vpc_id}"
}
}
- ./policy/vpc-peering-connection.tpl
{
"Version":"2012-10-17",
"Statement":[
{
"Effect":"Allow",
"Action":"ec2:AcceptVpcPeeringConnection",
"Resource":"arn:aws:ec2:${aws_region}:${main_aws_account_id}:vpc-peering-connection/*",
"Condition":{
"ArnEquals":{
"ec2:RequesterVpc":"arn:aws:ec2:${aws_region}:${peer_aws_account_id}:vpc/${peer_aws_vpc_id}"
}
}
},
{
"Effect":"Allow",
"Action":"ec2:AcceptVpcPeeringConnection",
"Resource":"arn:aws:ec2:${aws_region}:${main_aws_account_id}:vpc/${main_aws_vpc_id}"
}
]
}
AWS account_id/arn/user_idの取得 †
// "${data.aws_caller_identity.main.account_id}" として参照できる
data "aws_caller_identity" "main" {}
EC2起動時にuser_dataを渡す †
セキュリティグループを後から変更しようとするとEC2を作り直してしまう †
- VPCの場合、security_groups ではなく vpc_security_group_ids を使う
aws_nat_gateway †
- subnet毎にnat-gatewayを作成
- terraform v0.10.0
variable aws_subnets {
type = "list"
default = [
"subnet-aaaa", # AZ: ap-northeast-1a
"subnet-cccc", # AZ: ap-northeast-1c
]
}
resource "aws_nat_gateway" "gw" {
allocation_id = "${aws_eip.gw.*.id[count.index]}"
subnet_id = "${var.aws_subnets[count.index]}"
count = "${length(var.aws_subnets)}"
}
resource "aws_eip" "gw" {
vpc = true
count = "${length(var.aws_subnets)}"
}
複数リソースの指定 †
- EC2の数がsubnet数を超えても、AZが正しく振られるようにする
variable "ec2_subnets" { default = ["subnet-xxxx","subnet-xxxx","subnet-xxxx"] }
resource "aws_instance" "web" {
...
count = 4
subnet_id = "${var.ec2_subnets[count.index % length(var.ec2_subnets)]}"
}
- 変数と配列を指定 v0.10.2
variable "cidr_common" { default = "192.168.2.1/32" }
variable "cidr_example" { default = ["192.168.1.0/24", "10.5.1.0/24"] }
resource "aws_security_group" "common" {
...
cidr_blocks = [
"${var.cidr_common}",
"${var.cidr_example}"
]
- EC2インスタンスにEIPを付ける v0.10.2
variable "instance_count_web" { default = 1 }
# aws_elbは複数リソースがそのまま指定できる
resource "aws_elb" "frontend" {
instances = ["${aws_instance.web.*.id}"]
}
# aws_eipは一つのリソースしか指定できないので変換して渡す
resource "aws_eip" "web" {
count = "${var.instance_count_web}"
instance = "${element(aws_instance.web.*.id, count.index)}"
vpc = true
}
- Resource '〜' not found for variable '〜.1.id': 「.0.id」はアクセスできるが、「.1.id」がエラーになった。terraformの不具合か分からないが、以下の様に明示的に指定できる
"${element(aws_instance.web.*.id, 1)}"
RDS †
AWS: aws_db_instance - Terraform by HashiCorp
EC2-Classic上に作る場合の注意 †
classic network上にRDSを作る場合にsubnetのエラーが出る。v0.12.30 で確認
Error: Error creating DB Instance: InvalidVPCNetworkStateFault: Cannot create the DB Instance because db subnet group has not been specified which is required for a private DBInstance creation.
解決:
- 「publicly_accessible = true」が必要
VPCの作成 †