Terraform の GCP 環境向けの基本的な使い方
[履歴] [最終更新] (2021/04/13 00:19:38)
最近の投稿
注目の記事

概要

GCP 環境をプロビジョニングするための Terraform の基本的な設定を記載します。

本ページで利用する terraform のバージョンは以下のとおりです。

[vagrant@localhost myrepo]$ terraform --version
Terraform v0.14.8

簡単な例 (VPC の作成)

以下のような main.tf ファイルを作成します。GCP のサービスアカウントの key ファイルのパスを指定します。サービスアカウントには必要なロールを割り当てておく必要があります。

terraform {
  required_providers {
    google = {
      source = "hashicorp/google"
      version = "3.60.0"
    }
  }
}

provider "google" {
  credentials = file("./my-service-account-key.json")
  project = "my-project-id"
}

resource "google_compute_network" "vpc_network" {
  name = "terraform-network"
}

Terraform では対象とする環境に応じて provider を指定します。GCP の場合は hashicorp/google という provider を利用します。

以下のコマンドで provider のバイナリのダウンロード等の初期化処理を行います。

terraform init

変更される内容を dry-run で確認します。

terraform plan

変更を適用します。

terraform apply

terraform が認識している状態を確認します。

terraform show

出力例

# google_compute_network.vpc_network:
resource "google_compute_network" "vpc_network" {
    auto_create_subnetworks         = true
    delete_default_routes_on_create = false
    id                              = "projects/my-project-id/global/networks/terraform-network"
    mtu                             = 0
    name                            = "terraform-network"
    project                         = "my-project-id"
    routing_mode                    = "REGIONAL"
    self_link                       = "https://www.googleapis.com/compute/v1/projects/my-project-id/global/networks/terraform-network"
}

local でビルドした google provider を利用する設定

terraform の設定が期待通りに動かない場合に、local でビルドした google provider を利用できると検証時に便利です。

local ビルド

git clone https://github.com/hashicorp/terraform-provider-google.git

Go 言語でビルドします。

cd ./terraform-provider-google
go build

ビルド成果物

du -h ~/terraform-provider-google/terraform-provider-google
75M     /home/vagrant/terraform-provider-google/terraform-provider-google

~/.terraformrc

以下のようなディレクトリとシンボリックリンクを作成します。

mkdir -p ~/.terraform.d/plugin/registry.terraform.io/hashicorp/google/99.99.99/linux_amd64
mkdir -p ~/.terraform.d/plugin-cache

ln -s ~/terraform-provider-google/terraform-provider-google ~/.terraform.d/plugin/registry.terraform.io/hashicorp/google/99.99.99/linux_amd64/terraform-provider-google_v99.99.99

~/.terraformrc を作成します。

provider_installation {
  filesystem_mirror {
    path = "/home/vagrant/.terraform.d/plugin"
  }
  direct {
    exclude = []
  }
}

plugin_cache_dir = "/home/vagrant/.terraform.d/plugin-cache"

terraform init

main.tf の version を更新します。

terraform {
  required_providers {
    google = {
      source = "hashicorp/google"
#      version = "3.60.0"
      version = "99.99.99"
    }
  }
}

ロックファイルを削除して init しなおします。

rm .terraform.lock.hcl
terraform init

以下のように .terraform/* にインストールされました。

find .terraform/providers/ -name terraform-provider-google* -follow

.terraform/providers/registry.terraform.io/hashicorp/google/3.60.0/linux_amd64/terraform-provider-google_v3.60.0_x5
.terraform/providers/registry.terraform.io/hashicorp/google/99.99.99/linux_amd64/terraform-provider-google_v99.99.99

応用的な設定例 (Cloud Console および API へのアクセス制限の設定)

注意: BeyondCorp Enterprise の機能を使うため、以下の手順を試すためには GCP における「組織」の登録が必要となります。

サービスアカウントを作成

サービスアカウントは、ある一つのプロジェクトに属します。「組織」にサービスアカウントを作成することはできないため、適当なプロジェクトでサービスアカウントを作成します。

mylab-308012 というプロジェクトで my-service-account-2 を作成すると、以下のようなメールアドレスになります。

my-service-account-2@mylab-308012.iam.gserviceaccount.com

JSON 形式の鍵ファイルをダウンロードしておきます。

「組織」の階層構造を参照する権限の付与

「組織」の「IAM」において、「roles/browser(参照者)」ロールをサービスアカウントに付与します。

組織の階層構造を参照できるようになります。

Uploaded Image

gcloud auth activate-service-account --key-file ./my-service-account-2.json
gcloud organizations list

DISPLAY_NAME            ID  DIRECTORY_CUSTOMER_ID
qoosky.io     111111111111              XXXXXXXXX

「組織」のリソースを編集する権限の付与

作成したサービスアカウントを用いて Terraform でリソースを作成する必要があるため、更に「roles/editor(編集者)」ロールも付与します。

editor ロールがあると、組織のリソースを閲覧できるようになります。例えば以下のコマンドがエラーなく実行できます。

gcloud access-context-manager policies list --organization 111111111111

NAME          ORGANIZATION  TITLE           ETAG
486212067009  111111111111  default policy  045d9af982e4e4fa

デフォルトのポリシーが既に作成されている「組織」の場合は上記のように表示されます。「組織」は一つのポリシーしか持てないため、デフォルトのポリシーが存在する場合は terraform でポリシーを作成しようとするとエラーになります。

「アクセスレベル」の作成

「デフォルトのポリシー」が存在しない場合は、以下のように「組織」のポリシーも terraform で管理します。既にデフォルトのポリシーが存在する場合で terraform 管理しない場合は、以下の main.tf の ${google_access_context_manager_access_policy.my-access-policy.name} をデフォルトポリシーのものでハードコーディングします。

main.tf

terraform {
  required_providers {
    google = {
      source = "hashicorp/google"
      version = "3.60.0"
    }
  }
}

provider "google" {
  credentials = file("./my-service-account-2.json")
}

resource "google_access_context_manager_access_policy" "my-access-policy" {
  parent = "organizations/111111111111"
  title  = "my-access-policy"
}

resource "google_access_context_manager_access_level" "access-level" {
  parent = "accessPolicies/${google_access_context_manager_access_policy.my-access-policy.name}"
  name   = "accessPolicies/${google_access_context_manager_access_policy.my-access-policy.name}/accessLevels/my_ip_restriction"
  title  = "my_ip_restriction"
  basic {
    conditions {
      ip_subnetworks = [
        "123.123.123.123/32"
      ]
    }
  }
}

google_access_context_manager_access_level リソースのドキュメントはこちらですが、terraform の google プロバイダは、内部的には GCP REST API を実行しているため、ドキュメントでは把握しづらい情報は、REST API のドキュメントを参照して設定します。

グループの作成

グループを作成します。これにユーザを所属させることで、アクセス制限を行います。サービスアカウントでグループを作成するためには、必要な権限を付与する必要があります。CloudIdentity の場合は「グループ管理者」を付与します。

Cloud Identity 管理コンソールで「管理者ロール」をクリックします。

Uploaded Image

「グループ管理者」をクリックします。

Uploaded Image

「サービスアカウントへの割り当て」をクリックします。

Uploaded Image

サービスアカウントのメールアドレスを入力して追加します。

Uploaded Image

また、CloudIdentity の顧客ID を確認しておきます。

gcloud organizations list

DISPLAY_NAME            ID  DIRECTORY_CUSTOMER_ID
qoosky.io     111111111111              CXXXXXXXX

main.tf に以下のリソース定義を追記します。

resource "google_cloud_identity_group" "my-group" {
  display_name = "my-ip-restriction-group"
  parent = "customers/CXXXXXXXX"
  group_key {
    id = "my-ip-restriction-group@qoosky.io"
  }
  labels = {
    "cloudidentity.googleapis.com/groups.discussion_forum" = ""
  }
}

アクセスバインディングの作成

作成した「アクセスレベル」と「グループ」の紐付けを設定します。

resource "google_access_context_manager_gcp_user_access_binding" "my-access-binding" {
  organization_id = "111111111111"
  group_key       =  trimprefix(google_cloud_identity_group.my-group.id, "groups/")
  access_levels   = [
    google_access_context_manager_access_level.access-level.name,
  ]
}

補足

最終的に以下のような main.tf となります。

terraform {
  required_providers {
    google = {
      source = "hashicorp/google"
      version = "3.60.0"
    }
  }
}

provider "google" {
  credentials = file("./my-service-account-2.json")
}

resource "google_access_context_manager_access_policy" "my-access-policy" {
  parent = "organizations/111111111111"
  title  = "my-access-policy"
}

resource "google_access_context_manager_access_level" "access-level" {
  parent = "accessPolicies/${google_access_context_manager_access_policy.my-access-policy.name}"
  name   = "accessPolicies/${google_access_context_manager_access_policy.my-access-policy.name}/accessLevels/my_ip_restriction"
  title  = "my_ip_restriction"
  basic {
    conditions {
      ip_subnetworks = [
        "123.123.123.123/32"
      ]
    }
  }
}

resource "google_cloud_identity_group" "my-group" {
  display_name = "my-ip-restriction-group"
  parent = "customers/CXXXXXXXX"
  group_key {
    id = "my-ip-restriction-group@qoosky.io"
  }
  labels = {
    "cloudidentity.googleapis.com/groups.discussion_forum" = ""
  }
}

resource "google_access_context_manager_gcp_user_access_binding" "my-access-binding" {
  organization_id = "111111111111"
  group_key       =  trimprefix(google_cloud_identity_group.my-group.id, "groups/")
  access_levels   = [
    google_access_context_manager_access_level.access-level.name,
  ]
}

また、アクセス制限できるのは、同じドメイン qoosky.io におけるユーザアカウントのみです。

Uploaded Image

アクセス制限が有効になると cloud console が使えなくなります。

Uploaded Image

API も同様です。

Uploaded Image

うまくいかない場合の動作検証

期待通りに動作しない場合は、Cloud Logging のログを「組織」について確認します。

Uploaded Image

上記例では、callerIp が IPv6 となっており、IPv4 で設定した許可IP としてアクセスできておらず、Access Denied となっています。自宅や職場のネットワーク設定で IPv6 を利用している場合は注意します。

IPv4 の 0.0.0.0/0 に相当する ::/0 を許可 IP として設定して Access Denied とならなくなる場合は、これが原因です。

関連する gcloud コマンド

グループの情報を調査

gcloud identity groups describe my-ip-restriction-group@qoosky.io

グループ一覧の確認

gcloud identity groups search --organization 111111111111 --labels 'cloudidentity.googleapis.com/groups.discussion_forum'

gcloud は内部的に REST API を実行しています。上記と同様の処理を curl で行うためには以下のようにします。parent を URL エンコーディングする必要があることに注意します。

curl -sS -H "Authorization: Bearer $(gcloud auth print-access-token)" "https://cloudidentity.googleapis.com/v1/groups?parent=customers%2FCXXXXXXXX"

グループを作成できるアカウントであることを gcloud で確認

gcloud identity groups create my-group@qoosky.io --organization 111111111111 --labels 'cloudidentity.googleapis.com/groups.discussion_forum' --with-initial-owner empty

アクセスバインディングを gcloud で作成、削除 (group-key の指定方法に注意)

gcloud access-context-manager cloud-bindings create \
    --group-key "04k668n33mb28sl" \
    --level accessPolicies/486212067009/accessLevels/my_ip_restriction \
    --organization 111111111111

gcloud access-context-manager cloud-bindings delete --binding organizations/111111111111/gcpUserAccessBindings/aAQS-YRTcHRZ1IHJsBBe9kx0Tv1WHPqDpiA7yVtVZI55qrJpu
関連ページ
    概要 こちらのページで基本的な使い方を把握した Terraform を用いると、GCP 環境内にリソースを作成できます。GCP で組織を用いる際に必要となる Cloud Identity は GCP の外に存在する Google のサービスです。Cloud Identity が出力するログを GCP 内の Cloud Logging に取り込む方法について、Terraform モジュールのソース