個人的な印象:
terraform state list | grep template_file ... # delete terraform state rm data.template_file.example1 # update .terraform.lock.hcl terraform init -reconfigure
記事:
記事:
記事:
注意点:
ssh -F ssh-config -fNg -L 5432:<RDS ID>.****.ap-northeast-1.rds.amazonaws.com:5432 bastion-host
Error: Error connecting to PostgreSQL server 127.0.0.1 (scheme: awspostgres): tls: failed to verify certificate: x509: cannot validate certificate for 127.0.0.1 because it doesn't contain any IP SANs
sudo vim /etc/hosts -- 127.0.0.1 <RDS ID>.****.ap-northeast-1.rds.amazonaws.com --
記事:
記事:
variable "with_optional_attribute" {
type = object({
a = string # a required attribute
b = optional(string) # an optional attribute
c = optional(number, 127) # an optional attribute with default value
})
}記事:
記事:
上から順に読み込まれ、同名は上書きされる
Input Variables - Configuration Language | Terraform | HashiCorp Developer
記事:
variable "heroku_app_organization" {
description = ""
type = object({
name = string
})
default = {
name = null
}
}
resource "heroku_app" "main" {
...
dynamic "organization" {
for_each = var.heroku_app_organization.name == null ? [] : [var.heroku_app_organization]
content {
name = organization.value.name
}
}
}
sensitiveの変更を出力したい時:
記事:
...
elasticsearch_configuration {
domain_arn = try(each.value.es_cluster_endpoint, null) == null ? each.value.es_domain_arn : null
cluster_endpoint = try(each.value.es_cluster_endpoint, null)
...data "http" "example" {
url = "https://example.com/api1"
method = "POST"
request_headers = {
Content-Type = "application/json"
}
request_body = file("/path/to/example.json")
}
output "http_os_example_result" {
value = "status_code: ${data.http.example.status_code}, response_body: ${data.http.example.response_body}"
}記事:
resource "elasticsearch_opensearch_ism_policy" "example" {
policy_id = "example"
body = templatefile(
"${path.module}/files/opensearch/ism-policy-example.json",
{
min_index_age = "30d",
min_size = "50gb",
}
)
}removed {
from = aws_instance.example
lifecycle {
destroy = false
}
}# ソース変更 terraform state mv <old resource> <new resource> # または terraofrm state rm <old resource> terraofrm import <new resource> <resource name>
記事:
for_each = toset(formatlist("%s", range(1, 10)))locals {
var1 = {
bucket1 = {
s3_bucket_arn = "arn:aws:s3:::example1"
}
bucket2 = {
s3_bucket_arn = "arn:aws:s3:::example2"
}
}
}
data "aws_iam_policy_document" "s3" {
statement {
effect = "Allow"
actions = [
"s3:GetObject",
]
resources = concat([for value in local.var1 : value.s3_bucket_arn], [for value in local.var1 : "${value.s3_bucket_arn}/*"])
}
}rm versions.tf echo 'latest:^0.12' > .terraform-version rm -rf .terraform teraform init terraform 0.12upgrade teraform plan # コードのエラー修正
echo 'latest:^0.13' > .terraform-version rm -rf .terraform teraform init terraform 0.13upgrade teraform plan # コードのエラー修正 # applyしないとv1.0でエラーが出る teraform apply
echo 'latest:^1.0' > .terraform-version rm -rf .terraform teraform init teraform plan # コードのエラー修正
記事:
output example_password {
value = aws_db_instance.example.password
sensitive = true
}
# json terraform output -json terraform output example_password # raw value terraform output -raw example_password
output example_password {
value = nonsensitive(aws_db_instance.example.password)
}
記事
例:
解決:
terraform untaint aws_db_instance.main
byte = "${100*1024*1024}" # 100MBterraform plan -no-color > plan.txt
terraform console > concat(["a", "b"], ["c"]) [ "a", "b", "c", ] > exit
echo 'concat(["a", "b"], ["c"])' | terraform console
find . -name .terraform -type d | xargs du -sh
# 対象ディレクトリ一覧 find . -name .terraform -type d # 削除実行 find . -name .terraform -type d | xargs -i rm -rf {}
リソースが増えてくると、planやapplyがどんどん遅くなる。
export TF_CLI_ARGS_plan="--parallelism=20" export TF_CLI_ARGS_apply="--parallelism=20"
v0.11とv0.12では挙動がまったく異なる場合がある。また、関数も入れ子だと動かない場合がある。
list[
list[
map {
},
map {
}
]
]aws_instance.web[count.index].ebs_block_device[*].volume_id[*]
[count.index]
# idが欲しい場合
id = aws_instance.web.*.id[count.index]
# 一度localへ入れる場合
locals {
web_ebs1 = "${flatten(aws_instance.web.*.ebs_block_device)}"
}
...
${lookup(local.web_ebs1[count.index], "volume_id")}Cycleエラーの解消などに利用できる。
EC2をN台作る等、同じ種類のリソースを複数定義する時に使える。
string01: string01 string02: string02 list01: - "aaa" - "bbb" dict01: key1: val1 key2: val2
output "external_yamldecode" {
value = yamldecode(file("./vars.yml"))
}terraform init terraform plan terraform apply ... Outputs: external_yamldecode = { "dict01" = { "key1" = "val1" "key2" = "val2" } "list01" = [ "aaa", "bbb", ] "string01" = "string01" "string02" = "string02" }
#!/usr/bin/env python # -*- coding: utf-8 -*- # # read yaml file to json # # Requirements: # python 2.7 # pip install pyyaml from __future__ import print_function import sys import json import yaml from pprint import pprint def read_stdin(): return {x.strip() for x in sys.stdin} def main(): """ main """ try: stdin_lines = read_stdin() for line in stdin_lines: if line: jsondata = json.loads(line) f = open(jsondata["in_file"], "r") data = yaml.load(f, Loader=yaml.SafeLoader) f.close() sys.stdout.write(json.dumps(data)) except Exception, e: import traceback traceback.print_exc() sys.exit(1) if __name__ == '__main__': main() # vim: ts=4 sw=4 expandtab
echo '{"in_file":"./vars.yml"}' | ./read-from-yaml.py {"string02": "string02", "string01": "string01"}
data "external" "yaml" {
program = ["python", "${path.module}/read-from-yaml.py"]
query = {
in_file = "./vars.yml"
}
}
output "external_yaml" {
value = "${data.external.yaml.result}"
}
output "external_yamldecode" {
value = yamldecode(file("./vars.yml"))
}terraform init terraform plan terraform apply ... Outputs: external_yaml = { string01 = string01 string02 = string02 }
output "name" {value="..."}data.terraform_remote_state.example.outputs.name
provider "heroku" {
version = "= 1.9.0"
...
}terraform init -upgrade # or rm -rf .terraform terraform init
terraformはバージョンが0.0.1でも違うとコードの互換性が無くなる事が多い。
そのためバージョンを固定したい場合がある。
terraform {
required_version = ">= 0.12.0"
}terraform {
required_version = ">= 0.11.0, < 0.12.0"
}git clone https://github.com/tfutils/tfenv.git ~/.tfenv echo '[[ -d ~/.tfenv/bin ]] && export PATH="${PATH}:~/.tfenv/bin"' > ~/.bash.d/tfenv.sh chmod +x ~/.bash.d/tfenv.sh source ~/.bashrc # v0.11.xの最新をインストール tfenv install latest:^0.11 # カレントディレクトリをv0.11.xにする。 .terraform-version が書き換わる。~/で実行すればデフォルト値になる。 cd ~/ tfenv use 0.11.14
# 特定ディレクトリだけ0.12に変更 tfenv install latest:^0.12 # カレントディレクトリの .terraform-version が0.12.xへ変わる # 特定ディレクトリのバージョンを0.12.xへ固定 echo latest:^0.12 > .terraform-version
terraformはサイズが大きく、バージョンアップも頻繁なので古いバージョンを消したい。
tfenv list | grep -o -E '0.12.[0-9]+' | sort -n | head -n -1 | xargs -i bash -c 'tfenv uninstall {}'
terraform init This configuration or its associated state refers to the unqualified provider "aws".
terraform state replace-provider 'registry.terraform.io/-/aws' 'registry.terraform.io/hashicorp/aws'
echo latest:^0.13 > .terraform-version rm versions.tf terraform 0.13upgrade
terraform {
required_version = ">= 0.13"
required_providers {
aws = {
source = "hashicorp/aws"
}
}
}v0.11.x以下とコードの互換性が無くなかった。
terraform 0.12upgrade
同一のリソースを名前やアカウント、リージョンだけ作成したい時に、コードを共通化できる。
不便な点:
aws_elb "example" {
...
depends_on = [
"module.example"
]moduleを使わない方法:
記事:
provider "aws" {
alias = "src"
}provider "aws" {
alias = "usw1"
region = "us-west-1"
}
module "example" {
source = "../modules/tunnel"
providers = {
aws.src = "aws.usw1"
}
}variable "var1" {
default = "var1"
}
resource "aws_instance" "server" {
...
}
output "private_ip" {
value = aws_instance.server.private_ip
}# 変数の参照
"${module.server.var1}"
# 動的リソースの参照
"${module.server.private_ip}"security_groups = ["${compact(list("sg-xxA", "sg-xxb", ""))}]"
# data.aws_caller_identity.current.arn = arn:aws:iam::123456789012:user/user01
locals {
role_arn = "${replace(data.aws_caller_identity.current.arn, "/user\\/.+/", "")}role/example_global"
}戦略:
tables: key01: attr01: 10 key02: attr01: 20
variable "tables" {
default = {
"0" = "key01"
"1" = "key02"
}
}
output "tables.count" { value = "${length(var.tables)}" } # 2
output "tables.key0" { value = "${lookup(var.tables, 0)}" } # key01
variable "tables_attr01" {
default = {
"key01" = "10"
"key02" = "20"
}
}
output "tables_attr01.key01.val.way1" { value = "${lookup(var.tables_attr01, "key01")}" } # 10
output "tables_attr01.key01.val.way2" { value = "${lookup(var.tables_attr01, lookup(var.tables, 0))}" } # 10terraform apply ... Outputs: tables.count = 2 tables.key0 = key01 tables_attr01.key01.val.way1 = 10 tables_attr01.key01.val.way2 = 10
CONDITION ? TRUEVAL : FALSEVAL
# moduleを同一アカウントで、複数回使う場合、2回目以降はfalseにしておく。
variable create_iam_role { default = true }
resource "aws_iam_role" "example_global" {
count = "${var.create_iam_role ? 1 : 0}"
name = "example_global"
...resource "aws_instance" "vpn" { count = "${var.something ? 1 : 0}" }
# example: ["arn:aws:iam::1234567890:root", "..."] variable account_id_arns { default = [] } # example: ["arn:aws:s3:::example1, "..."] variable s3_r_bucket_arns { default = [] } variable aws_iam_role_name { default = "assume-role-s3" } resource "aws_iam_role" "assume-role-s3" { name = "${var.aws_iam_role_name}" assume_role_policy = "${data.aws_iam_policy_document.assume-role.json}" } resource "aws_iam_role_policy" "s3-r" { name = "s3-r" role = "${aws_iam_role.assume-role-s3.id}" policy = "${data.aws_iam_policy_document.s3-r.json}" count = "${ length(var.allowed_r_s3_bucket_arns) > 0 ? 1 : 0 }" } data "aws_iam_policy_document" "assume-role" { # assume role statement { actions = [ "sts:AssumeRole", ] effect = "Allow" principals = { type = "AWS" identifiers = "${var.account_id_arns}" } } } data "aws_iam_policy_document" "s3-r" { statement { actions = [ "s3:ListBucket", ] resources = ["${var.s3_r_bucket_arns}"] } statement { actions = [ "s3:GetObject", ] resources = ["${split( ",", "${join("/*,", var.s3_r_bucket_arns)}/*" )}"] } }
terraform fmtoutput "web-0001.public_dns" { value = "${aws_instance.web.1.public_dns}" }output "web.public_dns" { value = "${aws_instance.web.*.public_dns}" }subnet_id = element(data.aws_subnet_ids.main_private.ids[*], count.index)
aws_instance.web.id[count.index]
stateはbackend指定でS3に置けるが、通常default一つしか無い。
dev, stg, prodのように分けてstateを管理できる。
awsのregion毎に分けても良い。
terraform workspace new dev
terraform workspace select devvariable "cidr_whitelist" {
type = "list"
default = [
{ "value" = "${var.cidr_office}" type = "IPV4" },
]locals {
cidr_whitelist = [
{ "value" = "${var.cidr_office}" type = "IPV4" },
]
}locals {
owner = "taro.yamada"
site_id = "example-dev"
}
locals {
common_tags = {
owner = local.owner
site_id = local.site_id
}
}
aws_instance web {
...
tags = merge(local.common_tags, {
Name = "${local.prefix}-web-01"
role = "web"
})
}配列の要素の末尾に文字列を結合したい場合がある。
data "aws_iam_policy_document" "s3-r" { statement { actions = [ "s3:ListBucket", ] resources = ["${var.s3_r_bucket_arns}"] } statement { actions = [ "s3:GetObject", ] resources = ["${split( ",", "${join("/*,", var.s3_r_bucket_arns)}/*" )}"] } }
data "aws_instances" "web" {
instance_tags = {
"Name" = "web-*"
}
instance_state_names = ["running", "stopped"]
}
# 参照する場合
"${split( ",", "${join("/32,", data.aws_instances.web.public_ips)}/32")}",Terraformで起動、チェック実行、破棄までを自動化
リソースの作成/削除等で、timeoutする場合がある。
resource "aws_db_instance" "timeout_example" { name = "mydb" # ... timeouts { create = "60m" delete = "2h" } }
中国のalicloud用プロバイダー
resource "aws_iam_policy" "policy" { name = "test_policy" path = "/" description = "My test policy" policy = <<EOT { "Version": "2012-10-17", "Statement": [ { "Action": [ "ec2:Describe*" ], "Effect": "Allow", "Resource": "*" } ] } EOT }
# https://aws.amazon.com/marketplace/fulfillment?productId=b7ee8a69-ee97-4a49-9e68-afaee216db2e&ref=cns_srchrow&versionTitle=1708 # "${lookup(var.aws_ami_ids, "ap-northeast-1_centos7_2017-10-12")}" variable "aws_ami_ids" { default = { "us-east-1_centos7_2017-10-12" = "ami-db48ada1" "us-west-2_centos7_2017-10-12" = "ami-e535c59d" "ap-southeast-1_centos7_2017-10-12" = "ami-1fbad07c" "ap-northeast-1_centos7_2017-10-12" = "ami-e4599a82" "eu-central-1_centos7_2017-10-12" = "ami-2540f74a" "eu-west-1_centos7_2017-10-12" = "ami-5f76b626" } } variable "aws_region" { default = "ap-northeast-1" } locals { aws_ami_id_centos7 = "${lookup(var.aws_ami_ids, "${var.aws_region}_centos7_2017-10-12")}" } output "aws_ami_ids" { value = "${var.aws_ami_ids}" } output "aws_ami_id_centos7" { value = "${local.aws_ami_id_centos7}" }
terraform apply ... aws_ami_id_centos7 = ami-e4599a82 aws_ami_ids = { ap-northeast-1_centos7_2017-10-12 = ami-e4599a82 ap-southeast-1_centos7_2017-10-12 = ami-1fbad07c eu-central-1_centos7_2017-10-12 = ami-2540f74a eu-west-1_centos7_2017-10-12 = ami-5f76b626 us-east-1_centos7_2017-10-12 = ami-db48ada1 us-west-2_centos7_2017-10-12 = ami-e535c59d
terraform state list terraform state rm <terraform address> terraform import <terraform address> <resource id>
cat var.tf -- variable api_key {} -- api_key=*** terraform plan -var "api_key=$api_key"
api_key = "****"
security_groups = ["sg-xxxx1", "${var.list_sg}"]EBSを追加したり、SGを追加しようとするとEC2を作り直してしまう場合がある。
lifecycle {
ignore_changes = ["root_block_device"]
}TF_LOG=DEBUG terraform planpublic dns, IP等、後で参照したい情報を出力するのに便利。
refreshまたはapplyした後でしか表示されない。(tfstateファイルの中身を表示しているのでは)
terraform output # json terraform output -json
terraform output -json example_domain_validation_options | jq -r ".[] | [.resource_record_name, .resource_record_type, .resource_record_value] | @tsv" _1234567890abcdef1234567890abcdef.example.com CNAME _1234567890abcdef1234567890abcde.abcdefghij.acm-validations.aws.
Output Variablesでは1つの値しか出力できないようだ。
output "web.private_ip" { value = "${aws_instance.web.*.private_ip}" }
Outputs:
web.private_ip = [
i-1111,
i-2222
]output "web.private_ip" { value = "${join(",",aws_instance.web.*.private_ip)}" }terraform {
backend "s3" {
profile = "myprofile"
# shared_credentials_file = "~/.aws/config" # v0.9.3 で試しても失敗
region = "ap-northeast-1"
bucket = "mybucket"
key = "web/terraform.tfstate" # path/file で1バケットに複数のtfstateを置ける
use_lockfile = false # v1.10以降。CIや複数人使用時にロックしたい場合
}
}aws --profile myprofile configure # ~/.aws/credentials に該当キーが出来ているのが重要。 terraform init terraform plan
terraform plan -target=aws_instance.web[0]
cat example.tf| perl -ane 'if(/resource\s+"([^\"]+)"\s+"([^\"]+)"/){print "-target $1.$2 \\\n";}'
# example.tf
variable "security_groups" {
default = ["sg-xx", "sg-x,sg-xxx"]
}
resource "aws_instance" "web" {
...
security_groups = "${var.security_groups}"
}# example.tf
variable "security_groups" {
default = "sg-xx,sg-x,sg-xxx"
}
resource "aws_instance" "web" {
...
security_groups = "${split(",", var.security_groups)}"
}※terraform v0.7.0からimport機能が実装されたため、Terraformingは不要かもしれない。
terraform.tfstate や tf形式のソースを生成する [#f45434d2]
rvm use 2.1.5 ruby --version ruby 2.1.5p273 (2014-11-13 revision 48405) [x86_64-linux] rvmsudo gem install terraforming aws-sdk ln -s ~/.aws/config ~/.aws/credentials # なぜか --profileオプションが動作せず export AWS_ACCESS_KEY_ID=**** export AWS_SECRET_ACCESS_KEY=**** export AWS_REGION=ap-northeast-1 terraforming ec2 > terraforming.ec2.tf terraforming ec2 --tfstate >terraforming.terraform.tfstate
TERRAFORM_VER=0.8.5 sudo mkdir -p /opt/terraform.${TERRAFORM_VER} sudo wget -O /opt/terraform.${TERRAFORM_VER}/terraform_${TERRAFORM_VER}_linux_amd64.zip https://releases.hashicorp.com/terraform/${TERRAFORM_VER}/terraform_${TERRAFORM_VER}_linux_amd64.zip sudo unzip -d /opt/terraform.${TERRAFORM_VER}/ /opt/terraform.${TERRAFORM_VER}/terraform_${TERRAFORM_VER}_linux_amd64.zip # v0.7.x以上の場合 sudo alternatives --install /usr/local/bin/terraform terraform /opt/terraform.${TERRAFORM_VER}/terraform 70 # v0.6.x以下の場合 echo alternatives --install /usr/local/bin/terraform terraform /opt/terraform.${TERRAFORM_VER}/terraform 60 \\ > /tmp/install-terraform.sh find /opt/terraform.${TERRAFORM_VER}/ -name 'terraform-*' -type f -printf ' --slave /usr/local/bin/%f %f %p \\\n' >> /tmp/install-terraform.sh sudo bash /tmp/install-terraform.sh
alternatives --display terraform sudo alternatives --set terraform /opt/terraform.${TERRAFORM_VER}/terraform # 削除 sudo alternatives --set terraform /opt/terraform.${TERRAFORM_VER}/terraform
resource "aws_iam_policy_attachment" "s3-rw" { name = "s3-jp-rw" users = ["s3-jp-rw","s3-us-rw"] policy_arn = "arn:aws:iam::aws:policy/AmazonS3FullAccess" }
wget -O terraform_0.3.7_linux_amd64.zip https://dl.bintray.com/mitchellh/terraform/terraform_0.3.7_linux_amd64.zip sudo unzip terraform_0.3.7_linux_amd64.zip -d /usr/local/bin/ terraform version Terraform v0.3.7