Terraform

参考ページ https://qiita.com/donkomura/items/6289bb31fecfce2cda79

環境構築(VSCode)

mauve.terraform拡張機能を入れる。

settings.jsonの設定例:

{
  "terraform.indexing": {
    "enabled": false,
    "liveIndexing": false,
    "delay": 500,
    "exclude": [".terraform/**/*", "**/.terraform/**/*"]
  },
  "terraform.languageServer": {
    "enabled": true,
    "args": []
  }
}

Ref: https://qiita.com/pypypyo14/items/5520f3defa55119f3a1a

概要

https://www.terraform.io/intro/index.html

リソースの定義

構文:

resource "<リソースタイプ>" "<リソース名>" {
  # ここにリソースの構成を書く
}

リソースの定義には次の 2 つを指定する

  • リソースタイプ - 予め決められたリソースタイプ
  • リソース名 - 好きな名前を指定する。このリソース名を利用して他のリソースからリソースの値を参照できる

Ubuntu18.04 にインストール

Terraform本体のインストール。

VERSION="0.12.20"
wget "https://releases.hashicorp.com/terraform/${VERSION}/terraform_${VERSION}_linux_amd64.zip"
unzip "terraform_${VERSION}_linux_amd64.zip"
sudo mv terraform /usr/local/bin
rm "terraform_${VERSION}_linux_amd64.zip"

Ref: https://kazuhira-r.hatenablog.com/entry/2019/04/09/231234

tflintのインストール。

curl -L "$(curl -Ls https://api.github.com/repos/terraform-linters/tflint/releases/latest | grep -o -E "https://.+?_linux_amd64.zip")" -o tflint.zip && unzip tflint.zip && rm tflint.zip

Ref: https://github.com/terraform-linters/tflint

ベストプラティクス

ozbillwang / terraform-b​​est-practices:AWS ユーザー向けの Terraform のベストプラクティス

Terraform の推奨プラクティス-HashiCorp による Terraform

命名規則

https://www.terraform-best-practices.com/naming

Output Values

Output Values - Configuration Language - Terraform by HashiCorp

Docker で Terraform コマンドを実行する

docker pull hashicorp/terraform:light

Cloud Build で Terraform を実行する

事前準備

Cloud Build を有効化する。

gcloud services enable cloudbuild.googleapis.com compute.googleapis.com

GCP で使う

https://www.terraform.io/docs/providers/google/index.html

terraform 用のサービスアカウントを作る

gcloud init

# アカウントの確認
gcloud config list

terraform 用のサービスアカウントを作る。

PROJECT_ID="<プロジェクトID>"

# サービスアカウントを作成する
gcloud beta iam service-accounts create terraform \
    --project $PROJECT_ID \
    --display-name "terraform"
# 編集者の権限を付与する
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
  --member serviceAccount:terraform@${PROJECT_ID}.iam.gserviceaccount.com \
  --role roles/editor
# プロジェクトIAM管理者権限を付与する
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
  --member serviceAccount:terraform@${PROJECT_ID}.iam.gserviceaccount.com \
  --role roles/resourcemanager.projectIamAdmin
# サービスアカウントキーを生成する
gcloud iam service-accounts keys create key_terraform.json \
  --iam-account terraform@${PROJECT_ID}.iam.gserviceaccount.com

生成した鍵ファイルのパスをGOOGLE_CLOUD_KEYFILE_JSONに設定する。

export GOOGLE_CLOUD_KEYFILE_JSON=<PATH>

Ref: https://www.terraform.io/docs/providers/google/guides/getting_started.html#adding-credentials

Terraform の状態を GCS で管理する

terraform_bucket_lifecycle.jsonを作成する。

{
  "lifecycle": {
    "rule": [
      {
        "action": {
          "type": "Delete"
        },
        "condition": {
          "numNewerVersions": 5
        }
      }
    ]
  }
}
PROJECT_ID="<プロジェクトID>"

# Terraform用のバケットを作成
TERRAFORM_BUCKET=gs://${PROJECT_ID}-managed-by-terraform
gsutil mb -l asia-northeast1 -p $PROJECT_ID $TERRAFORM_BUCKET
gsutil versioning set on $TERRAFORM_BUCKET
gsutil lifecycle set "terraform_bucket_lifecycle.json" $TERRAFORM_BUCKET

StackDriver

filter の指定方法

https://cloud.google.com/monitoring/api/v3/filters?hl=ja

Terraform 起動用スクリプト

#!/usr/bin/env bash
set -euxo pipefail

# サービスアカウントキーの場所(GCS)
TERRAFORM_KEY_OBJECT="gs://<my-bucket>/<my-objet>"
# Terraform状態管理用のバケット(GCS)
TERRAFORM_BUCKET="<terraform-bukcet>"

SCRIPT_DIR=$(cd $(dirname $0); pwd)
source "$SCRIPT_DIR/_config"

# サービスアカウントキーをローカルにコピー
gsutil cp gs://$TERRAFORM_KEY_OBJECT $SCRIPT_DIR/key_terraform.json

# Terraform用のバケットを作成
if ! gsutil ls -b gs://$TERRAFORM_BUCKET ; then
  echo "Create new bucket..."
  gsutil mb -l asia-northeast1 gs://$TERRAFORM_BUCKET
  gsutil versioning set on gs://$TERRAFORM_BUCKET
  gsutil lifecycle set "$SCRIPT_DIR/terraform_bucket_lifecycle.json" gs://$TERRAFORM_BUCKET
fi

# デプロイ
(cd $SCRIPT_DIR/terraform \
  && export GOOGLE_CLOUD_KEYFILE_JSON=$SCRIPT_DIR/key_terraform.json \
  && export GOOGLE_APPLICATION_CREDENTIALS=$SCRIPT_DIR/key_terraform.json \
  && terraform init -backend-config="bucket=$TERRAFORM_BUCKET" \
  && terraform plan -var-file="$SCRIPT_DIR/$VAR_FILE" \
  && terraform apply -auto-approve -parallelism=30 -var-file="$SCRIPT_DIR/$VAR_FILE")

実行

# 初回実行時のみ必要
terraform init
terraform apply

Compute Engine

Ref: https://www.terraform.io/docs/providers/google/r/compute_instance.html

resource "google_compute_instance" "vm_instance" {
  name         = "terraform-instance"
  machine_type = "f1-micro"

  boot_disk {
    initialize_params {
      image = "debian-cloud/debian-9"
    }
  }

  network_interface {
    # A default network is created for all GCP projects
    network       = "default"
    access_config {
    }
  }
}

Tips

条件によってリソースの生成する/しないを切り替える

countを使う。

locals {
  use_my_network = true
}
resource "google_compute_network" "my_network" {
  # 条件を満たすときのみ生成する
  count = local.use_my_network ? 1 : 0

  name                    = "my-network"
}
resource "google_compute_subnetwork" "subnet_my_network" {
  count = local.use_my_network ? 1 : 0

  name          = "subnet-my-network"
  ip_cidr_range = "10.0.0.0/14"
  region        = "asia-northeast1"
  # countを使っているので`[0]`が必要
  network       = google_compute_network.my_network[0].self_link
}

lock を解除

terraform force-unlock <ID>

CI でデプロイ

<PROJECTの番号>@cloudbuild.gserviceaccount.comに、編集者の権限をつけておく。

steps:
  - name: "hashicorp/terraform:0.12.20"
    entrypoint: "sh"
    args:
      - "-c"
      - |
        echo PROJECT_ID="$PROJECT_ID"
        cd terraform
        terraform init
        terraform validate
        terraform workspace select dev && terraform apply -auto-approve
        terraform workspace select stage && terraform apply -auto-approve
timeout: 3600s

workspace の名前変更

defaultワークスペースをdevワークスペースに変更する場合

gsutil mv gs://<バケット>/terraform/state/{default,dev}.tfstate

Ref:
https://github.com/hashicorp/terraform/issues/16072

環境ごとに構成を切り替える

terraform workspace newでワークスペースを作成する。

terraform workspace new dev
terraform workspace new stage
terraform workspace new prod

# workspacenの確認
terraform workspace list

リモート状態を使用している場合は、環境ごとに次のように.tfstateファイルが分けて管理される。

- <BUCKET_NAME>/terraform/state/default.tfstate
- <BUCKET_NAME>/terraform/state/dev.tfstate
- <BUCKET_NAME>/terraform/state/stage.tfstate
- <BUCKET_NAME>/terraform/state/prod.tfstate

環境ごとの変数の設定は次のように設定する。

locals {
   env = {
      default = {
         instance_type  = "t2.micro"
         ami            = "ami-0ff8a91507f77f867"
         instance_count = 1
      }
      dev = {
         instance_type  = "m5.2xlarge"
         ami            = "ami-0130c3a072f3832ff"
      }
      stage = {
         instance_type  = "m5.2xlarge"
         ami            = "ami-00f0abdef923519b0"
         instance_count = 3
      }
      prod = {
         instance_type  = "c5.4xlarge"
         ami            = "ami-0422d936d535c63b1"
         instance_count = 6
      }
   }
   environmentvars = "${contains(keys(local.env), terraform.workspace) ? terraform.workspace : "default"}
   workspace       = "${merge(local.env["default"], local.env[local.environmentvars])}"
}

設定値の参照は次のようになる。

instance_type = "${local.workspace["instance_type"]}"

Ref:
Terraform ワークスペースでの環境変数の処理

ダラー(dallor)をエスケープする

$${}のように$を 2 つ書く。

resource "google_monitoring_alert_policy" "my_policy" {
  display_name = "My policy"
  combiner     = "OR"
  conditions {
    display_name = "My condition"

    condition_threshold {
      filter          = "metric.type=\"composer.googleapis.com/workflow/task/run_count\" AND resource.type=\"cloud_composer_workflow\" AND metric.label.state = \"failed\""
      duration        = "0s"
      comparison      = "COMPARISON_GT" # しきい値を超えたらアラート
      threshold_value = 0
      aggregations {
        alignment_period     = "60s"
        cross_series_reducer = "REDUCE_NONE"
        per_series_aligner   = "ALIGN_MAX"
      }
      trigger {
        count = 1
      }
    }
  }
  documentation {
    mime_type = "text/markdown"
    content   = <<EOT
      ## My Alert
      $${resource.label.workflow_name}
EOT
  }
}

Ref:
Interpolation Syntax

似たようなリソースを複数定義する

countを使う。

Ref: https://www.terraform.io/docs/configuration/resources.html#count-multiple-resource-instances-by-count

変数を設定する

変数定義にはvariableを使う。

variable "project_id" {
  type = string
}
provider "google" {
  project = var.project_id
}

Ref: https://qiita.com/ringo/items/3af1735cd833fb80da75

StackDriver で Slack 通知する

google cloud platform - Obtain Slack auth_token for Terraform google_monitoring_notification_channel resource - Stack Overflow

FAQ

Error: Error inspecting states in the "gcs" backend: querying Cloud Storage failed: storage: bucket doesn't exist

.terraformディレクトリを削除してから再トライ。