こんにちは。技術部プラットフォームグループのそめやポチです。春になるとゴマフアザラシ誕生のニュースが増えるので幸せを感じます。 この記事では、Terraformと他のツールとの間で情報を共有するために、設定値をyamlファイルに切り出して管理する方法を紹介します。
概要
Terraformは、インフラストラクチャの構成をコードとして管理するツールです。Terraformにおいては、開発者はインフラリソースをコードによって宣言的に記述できます。記述されたリソースは他のTerraformコードから参照可能です。しかし、Terraformが管理する値をTerraformの外部、例えばCI/CDワークフローなどの他のツールから直接的に参照することはできません。
そこで、Terraformと他のツールとの間で設定値を共有するために、構造化データファイルを使用することにしました。Terraformにはjsondecode
やyamldecode
といった関数があります。これらの関数によって、jsonやyamlなどの形式で書かれたデータをTerraformファイル内で解釈できます。また、jsonやyamlは広く使われている形式であるため、他のツールとの組み合わせが容易です。したがって、共有が必要なデータを構造化データファイルに書き出すことによって、Terraformと他のツールとの間でデータを共有することができます。
この記事ではyamlファイルを使用した例を紹介します。
コード例
Terraformがyamlファイルの内容を読み取るには、yamldecode
関数とfile
関数を使用します。以下の例では、variables.yaml
ファイルに記述された値を読み取り、locals
ブロック内で変数として使用しています。
# variables.yaml
ip_addresses:
- 1.1.1.1
- 2.2.2.2
# main.tf
locals {
ip_addresses = yamldecode(file("variables.yaml")).ip_addresses
}
output "ip_addresses" {
value = local.ip_addresses
}
このoutput
の結果は次のとおりになります。
Changes to Outputs:
+ ip_addresses = [
+ "1.1.1.1",
+ "2.2.2.2",
]
実践例: Google CloudのOIDC認証をGitHub Actionsワークフローで利用するために、プロジェクト情報をyamlファイルに書く
この記事ではyamlファイルによる設定値の共有の実践例として、Google CloudのOIDC認証をGitHub Actionsワークフローで利用するために、必要なデータをyamlファイルに書いておく方法を紹介します。
参考: Google Cloud Platform での OpenID Connect の構成 - GitHub Docs
ワークフロー上でOIDC認証をする場面はいくつかあります。たとえばワークフロー上でterraform
コマンドを実行するためには、認証を通じて管理対象であるGoogle Cloudリソースへのアクセス権限を取得しておく必要があります。サービスアカウントの秘匿情報によっても認証できますが、OIDC認証を利用する方法の方がセキュアです。
課題
Google Cloudでは「プロジェクト」という単位でリソースが管理されます。Google CloudのOIDC認証を利用するためには、プロジェクトIDなどの情報が必要です。また、TerraformでGoogle Cloudのプロジェクトを管理する際にもプロジェクトの情報が必要になります。
リポジトリ内で管理されるGoogle Cloudリソースがすべて同一のプロジェクトのものであれば、それぞれのコードにプロジェクト情報をハードコードすれば済みます。しかし、単一のリポジトリで複数のGoogle Cloudプロジェクトを管理する場合、ワークフローが動的にプロジェクトを切り替える必要があります。 それぞれのTerraformディレクトリが管理するプロジェクトと、ディレクトリ構造とが綺麗に対応していれば、ディレクトリ名からプロジェクト情報を確定させることもできます。しかし、今回はディレクトリ構造が複雑であり、Terraformのディレクトリ名がプロジェクトに対応していないものとします。
例えば、次の構造のリポジトリを考えます。このリポジトリでは、infra-repository
直下にserviceN/environmentN
ディレクトリが複数あり、それぞれのディレクトリにTerraformコードが配置されています。各Terraformディレクトリが管理するGoogle Cloudプロジェクトには規則性がありません。
infra-repository/
├── service1/
│ ├── environment1/
│ │ └── terraform/
│ │ └── code-for-project1.tf
│ ├── environment2/
│ │ └── terraform/
│ │ └── code-for-project2.tf
│ └── environment3/
│ └── terraform/
│ └── code-for-project1.tf
├── service2/
│ ├── environment1/
│ │ ├── terraform/
│ │ │ └── code-for-project3.tf
│ └── environment2/
│ ├── terraform/
│ │ └── code-for-project3.tf
...
実装コード例
上記の課題を解決するため、Terraformディレクトリごとにプロジェクト情報をyamlファイルに書き出すことで、GitHub Actionsのワークフローからも利用できる状態にします。
それぞれのTerraformディレクトリ直下のvariables.yaml
にプロジェクト情報を記述します。
# variables.yaml
project_id: "my-project1-id"
tfファイルでは、yamldecode
関数を使ってvariables.yaml
を読み込み、プロジェクト情報を取得します。
# /path/to/a/terraform-directory/main.tf
locals { project = yamldecode(file("variables.yaml")).project_id }
provider "google" {
project = local.project
region = "my-region"
}
OIDC認証の過程で使うため、それぞれのプロジェクト情報をまとめたyamlファイルを用意します。
# config/gcloud_projects_data.yaml
projects:
- name: my-project1-display-name
project_id: my-project1-id
project_number: my-project1-number
- name: my-project2-display-name
project_id: my-project2-id
project_number: my-project2-number
- name: my-project3-display-name
project_id: my-project3-id
project_number: my-project3-number
GitHub Actionsのワークフローでは、例えばyq
などのツール使ってyamlファイルを読み込み、OIDC認証に必要なプロジェクトIDとプロジェクトナンバーを取得します。
# .github/workflows/terraform.yaml
name: Terraform workflow
on:
workflow_dispatch:
inputs:
directory:
description: 'Terraform directory'
required: true
jobs:
terraform:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install yq
run: wget https://github.com/mikefarah/yq/releases/download/v4.6.1/yq_linux_amd64 -O yq && chmod +x yq
- name: Extract Project Data by Directory
id: 'extract-project'
run: |
DIRECTORY="${{ inputs.directory }}"
# ディレクトリごとのプロジェクト情報を取得
GCLOUD_PROJECT_ID=$(yq e '.project_id' $DIRECTORY/variables.yaml)
GCLOUD_PROJECT_NUMBER=$(yq e ".projects[] | select(.project_id == \"$GCLOUD_PROJECT_ID\").project_number" config/gcloud_projects_data.yaml)
echo "project-id=${GCLOUD_PROJECT_ID}" >> $GITHUB_OUTPUT
echo "project-number=${GCLOUD_PROJECT_NUMBER}" >> $GITHUB_OUTPUT
- name: Authenticate to Google Cloud
id: 'auth'
uses: google-github-actions/auth@v1
with:
workload_identity_provider: projects/${{ steps.extract-project.outputs.project-number }}/locations/global/workloadIdentityPools/gh-oidc-pool/providers/github-actions # プロジェクトごとに同名のOIDCプロバイダを作成しておく
service_account: github-actions@${{ steps.extract-project.outputs.project-id}}.iam.gserviceaccount.com # プロジェクトごとに同名のサービスアカウントを作成しておく
project_id: ${{ steps.extract-project.outputs.project-id }}
- name: Do something with Terraform after authentication
working-directory: ${{ inputs.directory }}
run: |
# 認証が完了した状態なのでTerraformコマンドが実行できる
...
以上のようにすることで、どのディレクトリにおいても、適切なGoogle Cloudプロジェクトに対してOIDC認証をすることができます。
おわりに
以上、yamlファイルで設定値を管理することで、Terraformと他のツールとの間でデータを共有する方法を紹介しました。 私は、ディレクトリ構成が複雑なリポジトリでTerraformのCI/CDを実現しようとしてこの方法に辿り着きました。構造化データを汎用的な形式で管理することによって、ツールを問わず利用できるようにするという手法は、他の事例にも適用できると感じました。 「yaml最高!!1」と主張するつもりはないですが、適切な形式でデータを一元管理すると、手間が減って世界の幸せの総量が増えると思いますのでお試しください。