Nix+Terraform で個人用のインフラ開発環境を作る
やること
次のようなTerraform開発環境を作成します。
# shellを立ち上げる
task staging:up # 本番の場合はtask production:up
# 特定のfeatureを起動
task staging:feature1 -- [terraform_command]
また、適度なレベルのCIも設定します。
やらないこと
Workload Identity Pool, ProviderなどのOIDC通信はやりません。個人なので。
どうやるか
- Terraform CloudでLocalを選択
- 開発環境をNix Developで構築
- CLIをTaskfileで操作
なぜNix?
Dependency Hellで苦しんだ経験があるのでサンドボックス環境に閉じ込めたい。gcloudもTerraformも管理したくない!!
なぜTaskfile?
Typing面倒!!
準備
- HashiCorp Cloudのアカウント作成
- Google CloudなどのPaaSのProjectを作成
- Nixのインストール
- Taskfileのインストール
DevShellを作る
{
description = "Development environment for Terraform project";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
};
outputs = { self, nixpkgs }:
let
system = "aarch64-darwin";
pkgs = nixpkgs.legacyPackages.${system};
google_cloud_project = builtins.getEnv "GOOGLE_CLOUD_PROJECT";
asciiArt = ''
.__ __
______| |__ __ __ ____ ______ ____ ____ | | __
/ ___/| | \ | | \ / \ / ___/ / _ \ _/ ___\ | |/ /
\___ \ | Y \| | /| | | \\____ \ ( <_> )\ \___ | <
/______>|___| /|____/ |__| |__//______>\____/ \_____>|__|__\.
'';
in {
devShells.${system}.default = pkgs.mkShell {
packages = [
pkgs.go-task
pkgs.google-cloud-sdk
pkgs.terraform
pkgs.tflint
];
shellHook = ''
echo "${asciiArt}"
echo "✅ Following packages are available:"
echo "- Google Cloud SDK: $(command -v gcloud)"
echo "- Task: $(command -v task)"
echo "- Terraform: $(command -v terraform)"
echo "- TFLint: $(command -v tflint)"
echo "🚀 Setup Google SDK:"
gcloud auth login
gcloud auth application-default login
gcloud config set project "${google_cloud_project}"
echo "✅ Project set to ${google_cloud_project}."
echo "🎉 Setup steps finished. Consider running 'terraform init' next."
'';
};
};
}
起動の流れ
- Taskfileがワークフローを発火
- nix developのビルドが走る
-
flake.nixのshellHookが起動する
解説
GoogleCloudのProjectの情報はDevShellを起動する段階で決めたいものです。今回は環境変数をWrapperのTaskfile経由で渡します。
次の部分がNixが環境変数から取得する部分です
google_cloud_project = builtins.getEnv "GOOGLE_CLOUD_PROJECT";
ここにTaskfile経由で流し込みます。TaskfileではGlobalな設定とtaskごとの設定を書けます。複数入力があるので後者を利用します。
version: '3'
tasks:
up:
env:
GOOGLE_CLOUD_PROJECT: "staging環境"
cmds:
- nix develop --impure
silent: true
shellHook はShellに入るタイミングで起動するスクリプトです。ちゃんとやるなら、entrypoint.shを作った方が良いと思います。ASCII Artはかっこいいので書いています。実用性?しらんな。
ちなみにですが、Google Cloudを使う場合は次の設定を入れておくのが無難です。自分の使用しているプロジェクトは「常に」確信を持っておきたいですよね。(100敗)
echo "🚀 Setup Google SDK:"
gcloud auth login
gcloud auth application-default login
gcloud config set project "${google_cloud_project}"
Taskfileでstaging, production用のコマンドを作る
起動時のスクリプトを考えてみます。愚直にやるとこうなりますかね。
export GOOGLE_CLOUD_PROJECT=ステージング環境 && nix develop --impure
さすがに冗長すぎるので設定をします。Taskfileはymlを分割可能です。これ次のように設定すれば、staging:upはtasks/staging.ymlで定義されたuptaskが起動します。
version: '3'
env:
NIXPKGS_ALLOW_UNFREE: 1
includes:
staging: tasks/staging.yml
production: tasks/production.yml
Terraformのコマンド入力を楽にする
Terraformのディレクトリ移動面倒ですよね。仕組みでディレクトリ移動が発生しないようにします。例えば、次の設定ではtask staging:feature1 -- [command]と入力するとディレクトリ移動しなくて良くなります。
tasks/staging.yml
version: '3'
tasks:
feature1:
dir: "environments/staging/feature1"
cmds:
- terraform {{ .CLI_ARGS}}
silent: true
GitHub Actions
LocalからApplyするにしてもLintやfmtぐらいは掛けたいものです。とりあえずLintが通らないのは嫌なので追加します。長くなりすぎるのもどうかと思うので省いていますが、terraform validateやterraform format -recursiveあたりはいれましょう。
tflint
name: Lint Terraform
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup TFLint
uses: terraform-linters/setup-tflint@v4
with:
tflint_version: latest
- name: Run TFLint
run: tflint -f compact
ShellCheck
どうせいつか生えるのでShellも検証します。これはTerraformに限らずプロジェクトを作るときは常にやっておいた方が良いです。
name: Lint ShellScript
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
shellcheck:
name: Run ShellCheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run ShellCheck
uses: ludeeus/action-shellcheck@master
Dependabot
dependabotも入れておきましょう。
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
- package-ecosystem: "terraform"
directory: "environments/staging/feature1"
schedule:
interval: "monthly"
- package-ecosystem: "terraform"
directory: "environments/production/feature1"
schedule:
interval: "monthly"
おわりに
環境構築の際の助けになれば幸いです!!

Discussion