Terraform の GCP 環境向けの基本的な使い方
[履歴] [最終更新] (2021/10/26 22:47:11)
最近の投稿
注目の記事

概要

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" = ""
  }
}

補足

アカウントタイプ ドメイン Organization Administrator (GCP IAM による cloudidentity.googleapis.com 利用の許可) Groups Admin (Cloud Identity qoosky.io) mygroup@qoosky.io 作成
ユーザアカウント qoosky.io X X X
ユーザアカウント qoosky.io O X O
ユーザアカウント qoosky.io X O O
ユーザアカウント qoosky.io O O O
ユーザアカウント abc.com O/X X (権限付与はできません) X
サービスアカウント xxx.iam.gserviceaccount.com X X X
サービスアカウント xxx.iam.gserviceaccount.com O X X
サービスアカウント xxx.iam.gserviceaccount.com X O X
サービスアカウント xxx.iam.gserviceaccount.com O O O

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

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

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 モジュールのソース
    概要 グローバルIP を持たず、インターネットとの接続が行えない GCP VM に対して SSH 接続するための設定を記載します。 インターネットアクセスを制限するための設定 以下のような制限が存在しているとします。 これらの制限がなされていたとしても SSH 接続できることの確認になります。 Firewall
    概要 GCP の基本的な使い方について、コマンド例などを記載します。 Web ブラウザ経由で gcloud コマンドを利用 (Cloud Shell) GCP において、Cloud Console (Web UI) によるリソース管理操作と同等の処理は、gcloud コマンドによって行うこともできます。Cloud Shell
    概要 API を管理するためのプラットフォームである Apigee X について、基本的な使い方を記載します。Apigee は 2016 年に Google によって買収されており、Apigee Edge という名称で Google Cloud のサービスに加わりました。2021 年に Apigee X という名称の GCP-native なサービスが開始されました。