みなさんこんにちは。マネージドサービス課の塩野です。
この記事では、GitHub ActionsとTerraformを使ってNew Relic監視設定を自動展開する方法を解説します。Pull Requestでterraform planの結果を確認し、マージ後に自動的にterraform applyが実行される仕組みを構築します。
実装後は、アラート設定やダッシュボードの変更をコードで管理でき、レビュープロセスを経て安全に本番環境へ展開できるようになります。GitHub ActionsとAWS CodePipelineの比較については前回の記事をご確認ください。
目次
前提条件
- GitHubアカウント
- AWSアカウント(S3バケット用)
- New Relicアカウント
- Terraformの基本知識
事前準備
必要なもの
実装を始める前に、以下を準備します。
GitHubリポジトリ
Terraformコードを管理するリポジトリを作成します。既存のリポジトリを使う場合は、専用のディレクトリを作成すると管理しやすいでしょう。
New Relic APIキー
New Relicの管理画面から、User APIキーを取得します。APIキーは「User」タイプを選択し、適切な権限を付与してください。
AWSリソース
tfstate保存用のS3バケットを作成します。バケット名は一意である必要があるため、組織名やプロジェクト名を含めた命名をおすすめします。バージョニングを有効化しておくと、万が一の際にロールバックできます。
ディレクトリ構成
環境ごとにtfstateを分離するため、以下のディレクトリ構成を推奨します。
. ├── .github/ │ └── workflows/ │ ├── terraform-plan.yml │ └── terraform-apply.yml ├── terraform/ │ ├── environments/ │ │ ├── dev/ │ │ │ ├── backend.tf │ │ │ └── main.tf │ │ └── prod/ │ │ ├── backend.tf │ │ └── main.tf │ └── modules/ │ ├── main.tf │ ├── variables.tf │ └── alerts.tf └── README.md
environments/配下に環境ごとのディレクトリを作成し、それぞれ独立したbackend設定を持たせます。共通のTerraformコードはmodules/ディレクトリで管理し、各環境から参照する構成です。これにより、dev環境とprod環境でtfstateが分離され、環境間の干渉を防げます。
Terraformコードの準備
Provider設定
共通のTerraformコードをmodules/ディレクトリに配置します。
terraform/modules/main.tf
terraform { required_providers { newrelic = { source = "newrelic/newrelic" version = "~> 3.0" } } } provider "newrelic" { account_id = var.newrelic_account_id api_key = var.newrelic_api_key region = "US" }
Variables設定
terraform/modules/variables.tf
variable "newrelic_account_id" { description = "New Relic Account ID" type = string sensitive = true } variable "newrelic_api_key" { description = "New Relic User API Key" type = string sensitive = true }
これらの変数は、GitHub ActionsでTF_VAR_プレフィックス付きの環境変数として設定されます。
Backend設定
環境ごとに異なるS3キーを使用してtfstateを分離します。
terraform/environments/dev/backend.tf
terraform { backend "s3" { bucket = "your-terraform-state-bucket" key = "newrelic/dev/terraform.tfstate" region = "ap-northeast-1" encrypt = true } }
terraform/environments/prod/backend.tf
terraform { backend "s3" { bucket = "your-terraform-state-bucket" key = "newrelic/prod/terraform.tfstate" region = "ap-northeast-1" encrypt = true } }
環境ごとに異なるkeyを指定することで、tfstateが分離されます。encryptオプションでS3の暗号化を有効にします。本記事ではDynamoDBによるstate lockを省略していますが、本番環境や複数人で実行する環境の場合はDynamoDBによるLockingを実装を検討してください。また、DynamoDBを使わない場合はワークフローファイルに concurrency を追加して同時実行しないようにすることを検討してください。
アラート設定の例
terraform/modules/alerts.tf
resource "newrelic_alert_policy" "example" { name = "Example Alert Policy" } resource "newrelic_nrql_alert_condition" "high_cpu_usage" { policy_id = newrelic_alert_policy.example.id name = "High CPU Usage" enabled = true nrql { query = "SELECT average(host.cpuPercent) FROM Metric" } critical { operator = "above" threshold = 80 threshold_duration = 300 threshold_occurrences = "all" } }
この例では、CPU使用率が80%を超えた状態が5分間続いた場合にアラートが発火します。実際の運用では、サーバーの特性に合わせて閾値を調整してください。
環境ごとのエントリーポイント
各環境のディレクトリに、modulesを参照するエントリーポイントを作成します。
terraform/environments/dev/main.tf
module "newrelic" { source = "../../modules" newrelic_account_id = var.newrelic_account_id newrelic_api_key = var.newrelic_api_key }
terraform/environments/prod/main.tf
module "newrelic" { source = "../../modules" newrelic_account_id = var.newrelic_account_id newrelic_api_key = var.newrelic_api_key }
各環境で同じmoduleを参照しますが、backend設定とSecretが異なるため、tfstateは完全に分離されます。
GitHub Secretsの設定
New Relic APIキーの登録
GitHubリポジトリの「Settings」→「Secrets and variables」→「Actions」から、以下のSecretを登録します。
NEWRELIC_API_KEY: New RelicのUser APIキーNEWRELIC_ACCOUNT_ID: New RelicのアカウントID
AWS認証情報の登録
S3バケットへのアクセスには、OIDC(OpenID Connect)を使った認証を推奨します。長期的な認証情報を保存する必要がなく、セキュリティリスクを低減できます。
OIDC設定の手順
1. OIDCプロバイダーの作成
AWSコンソールから「IAM」→「IDプロバイダー」→「プロバイダーを追加」を選択します。
- プロバイダータイプ: 「OpenID Connect」
- プロバイダーURL:
https://token.actions.githubusercontent.com - オーディエンス:
sts.amazonaws.com - 「プロバイダーを追加」をクリック
2. IAMロールの作成
「IAM」→「ロール」→「ロールを作成」を選択します。
- 信頼されたエンティティタイプ: 「ウェブアイデンティティ」
- アイデンティティプロバイダー: 作成したOIDCプロバイダーを選択
- オーディエンス:
sts.amazonaws.com
信頼ポリシーは以下のようになります。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Federated": "arn:aws:iam::ACCOUNT_ID:oidc-provider/token.actions.githubusercontent.com" }, "Action": "sts:AssumeRole", "Condition": { "StringEquals": { "token.actions.githubusercontent.com:aud": "sts.amazonaws.com", "token.actions.githubusercontent.com:sub": "repo:YOUR_GITHUB_USERNAME/YOUR_REPO_NAME:*" } } } ] }
3. IAMロールに権限を付与
以下のカスタムポリシーを作成してアタッチします。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject" ], "Resource": "arn:aws:s3:::your-terraform-state-bucket/*" }, { "Effect": "Allow", "Action": [ "s3:ListBucket" ], "Resource": "arn:aws:s3:::your-terraform-state-bucket" } ] }
設定後、以下のSecretを登録します。
AWS_ROLE_ARN: AssumeするIAMロールのARN
注意: IAMユーザーのアクセスキーを使う方法もありますが、セキュリティ上の理由からOIDCの利用を強く推奨します。
環境別のSecrets設定
GitHub Environmentsを使えば、環境ごとにSecretを分離できます。「Settings」→「Environments」から、dev、prodなどの環境を作成し、それぞれにSecretを設定します。本番環境には承認者を設定しておくと、誤った展開を防げます。
GitHub Actions Workflowの作成
terraform planワークフロー
Pull Request作成時にterraform planを実行し、結果をアーティファクトに保存するワークフローです。ベースブランチに応じて環境を自動判定します。
name: Terraform Plan on: pull_request: paths: - 'terraform/**' jobs: plan: runs-on: ubuntu-latest environment: ${{ (github.base_ref == 'main' && 'production') || (github.base_ref == 'develop' && 'development') || 'development' }} permissions: contents: read pull-requests: write id-token: write steps: - uses: actions/checkout@v4 - name: Determine Environment id: env run: | if [ "${{ github.base_ref }}" = "main" ]; then echo "env_name=prod" >> $GITHUB_OUTPUT elif [ "${{ github.base_ref }}" = "develop" ]; then echo "env_name=dev" >> $GITHUB_OUTPUT else echo "Error: Unsupported base branch ${{ github.base_ref }}" exit 1 fi - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: ${{ secrets.AWS_ROLE_ARN }} aws-region: ap-northeast-1 - name: Setup Terraform uses: hashicorp/setup-terraform@v3 - name: Terraform Init working-directory: ./terraform/environments/${{ steps.env.outputs.env_name }} run: terraform init - name: Terraform Plan working-directory: ./terraform/environments/${{ steps.env.outputs.env_name }} env: TF_VAR_newrelic_api_key: ${{ secrets.NEWRELIC_API_KEY }} TF_VAR_newrelic_account_id: ${{ secrets.NEWRELIC_ACCOUNT_ID }} run: | terraform plan -no-color | tee plan_output.txt - name: Mask sensitive information working-directory: ./terraform/environments/${{ steps.env.outputs.env_name }} run: | # New Relic APIキーをマスキング (NRAK-で始まる40文字の英数字) sed -i 's/NRAK-[A-Z0-9]\{40\}/***MASKED_API_KEY***/g' plan_output.txt # New Relic Account ID をマスキング (account_id = の後の数字のみ) sed -i 's/\(account_id[[:space:]]*=[[:space:]]*\)[0-9]\{7,\}/\1***MASKED_ACCOUNT_ID***/g' plan_output.txt # AWS ARNをマスキング sed -i 's/arn:aws:[a-z0-9:/_-]*\(["[:space:]]\)/***MASKED_ARN***\1/g' plan_output.txt - name: Upload Plan Output uses: actions/upload-artifact@v4 with: name: terraform-plan-${{ steps.env.outputs.env_name }}-${{ github.event.pull_request.number }} path: terraform/environments/${{ steps.env.outputs.env_name }}/plan_output.txt
このワークフローは、terraform/ディレクトリ配下のファイルが変更された場合にのみ実行されます。github.base_refを使用してマージ先のブランチを判定し、mainまたはdevelop以外のブランチへのPRの場合はエラーで停止します。mainブランチへのPRの場合はterraform/environments/prod/、developブランチへのPRの場合はterraform/environments/dev/ディレクトリで実行されます。これにより、環境ごとに独立したtfstateが使用され、production環境またはdevelopment環境のSecretが適用されます。plan結果は機密情報をマスキングした上でワークフローのアーティファクトとして保存され、「Actions」タブからダウンロードして確認できます。アーティファクト名にはPR番号が含まれるため、同じブランチへの複数PRでも衝突しません。
セキュリティ上の注意: terraform planの出力には、リソース名やタグ値など機密情報が含まれる可能性があります。マスキング処理では、New Relic APIキー、Account ID(account_id =の後の数字のみ)、AWS ARNを対象としています。閾値やポート番号などの数値は保持されます。組織のセキュリティポリシーに応じて追加の対策を検討してください。
terraform applyワークフロー
Pull Requestがマージされた際にterraform applyを実行するワークフローです。ブランチに応じて環境を自動判定します。
name: Terraform Apply on: push: branches: - main - develop paths: - 'terraform/**' jobs: apply: runs-on: ubuntu-latest environment: ${{ (github.ref_name == 'main' && 'production') || (github.ref_name == 'develop' && 'development') || 'development' }} permissions: contents: read id-token: write steps: - uses: actions/checkout@v4 - name: Determine Environment id: env run: | if [ "${{ github.ref_name }}" = "main" ]; then echo "env_name=prod" >> $GITHUB_OUTPUT elif [ "${{ github.ref_name }}" = "develop" ]; then echo "env_name=dev" >> $GITHUB_OUTPUT else echo "Error: Unsupported branch ${{ github.ref_name }}" exit 1 fi - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: ${{ secrets.AWS_ROLE_ARN }} aws-region: ap-northeast-1 - name: Setup Terraform uses: hashicorp/setup-terraform@v3 - name: Terraform Init working-directory: ./terraform/environments/${{ steps.env.outputs.env_name }} run: terraform init - name: Terraform Apply working-directory: ./terraform/environments/${{ steps.env.outputs.env_name }} env: TF_VAR_newrelic_api_key: ${{ secrets.NEWRELIC_API_KEY }} TF_VAR_newrelic_account_id: ${{ secrets.NEWRELIC_ACCOUNT_ID }} run: | terraform apply -auto-approve -no-color | tee apply_output.txt - name: Mask sensitive information working-directory: ./terraform/environments/${{ steps.env.outputs.env_name }} run: | # New Relic APIキーをマスキング (NRAK-で始まる40文字の英数字) sed -i 's/NRAK-[A-Z0-9]\{40\}/***MASKED_API_KEY***/g' apply_output.txt # New Relic Account ID をマスキング (account_id = の後の数字のみ) sed -i 's/\(account_id[[:space:]]*=[[:space:]]*\)[0-9]\{7,\}/\1***MASKED_ACCOUNT_ID***/g' apply_output.txt # AWS ARNをマスキング sed -i 's/arn:aws:[a-z0-9:/_-]*\(["[:space:]]\)/***MASKED_ARN***\1/g' apply_output.txt - name: Upload Apply Output uses: actions/upload-artifact@v4 with: name: terraform-apply-${{ steps.env.outputs.env_name }}-${{ github.run_number }} path: terraform/environments/${{ steps.env.outputs.env_name }}/apply_output.txt
このワークフローは、mainまたはdevelopブランチへのマージ時に自動的にapplyが実行されます。github.ref_nameを使用してブランチ名を判定し、mainまたはdevelop以外のブランチへのプッシュの場合はエラーで停止します。mainブランチの場合はterraform/environments/prod/、developブランチの場合はterraform/environments/dev/ディレクトリで実行されます。これにより、環境ごとに独立したtfstateが使用され、production環境またはdevelopment環境のSecretが適用されます。-auto-approveオプションを付けているため、承認後は確認なしで実行される点に注意してください。アーティファクト名には実行番号が含まれるため、複数回の実行でも衝突しません。
環境別展開の仕組み
上記のワークフローでは、ブランチ戦略に基づいて環境を自動判定する仕組みを採用しています。
ブランチと環境の対応
developブランチ →development環境mainブランチ →production環境
動作の流れ
developブランチへのPR作成時:development環境でplan実行developブランチへのマージ時:development環境でapply実行mainブランチへのPR作成時:production環境でplan実行mainブランチへのマージ時:production環境でapply実行(承認者設定がある場合は承認後)
これにより、以下のメリットがあります。
- 開発環境と本番環境でAPIキーやアカウントIDを分離できる
- 本番環境には承認者を設定し、誤った展開を防止できる
- 環境ごとに異なるIAMロールを使用できる
- ブランチ名で環境が自動判定されるため、ワークフローの管理が簡単
GitHub Environmentsの設定で、production環境に承認者を追加しておくと、mainブランチへのマージ後、指定された承認者が承認するまでterraform applyは実行されません。
動作確認
Pull Request作成
アラート設定を変更するPull Requestを作成します。
- ブランチを作成
terraform/modules/alerts.tfを編集- コミット・プッシュ
- Pull Request作成(developブランチへ)
数分後、GitHub Actionsが実行されます。plan結果はアーティファクトとして保存されるため、次のステップで確認しましょう。
plan結果の確認
GitHub Actionsの「Actions」タブから該当のワークフロー実行を選択し、「Artifacts」セクションからterraform-planをダウンロードして結果を確認します。追加・変更・削除されるリソースが表示されるため、意図した変更になっているか確認してください。
マージ後のapply実行確認
Pull Requestをマージすると、自動的にterraform applyが実行されます。GitHub Actionsの「Actions」タブから実行状況を確認できます。
New Relicでの設定反映確認
terraform applyが成功したら、New Relicの管理画面で設定が反映されているか確認します。アラートポリシーや条件が正しく作成・更新されていることを確認しましょう。
まとめ
GitHub ActionsとTerraformを使ったNew Relic監視設定の自動展開を実装しました。Pull Requestでplan結果を確認し、マージ後に自動的にapplyが実行される仕組みにより、安全かつ効率的な運用が可能になります。
実装のポイントは、GitHub Secretsでの認証情報管理、S3バックエンドでのtfstate管理、アーティファクトへのplan結果保存です。これによりチーム全体で変更内容を確認しながら、自動化された展開が実現できます。次のステップとして、複数環境への展開、Slack通知の追加、より複雑なアラート設定の管理などに取り組んでみてください。段階的に自動化範囲を広げることで、運用負荷を軽減し、より本質的な監視設計に時間を使えるようになるでしょう。
こちらの記事がどなたかのお役に立てれば幸いです。
宣伝
弊社では、お客様環境のオブザーバビリティを加速するためのNew Relicアカウント開設を含めた伴走型のNew Relic導入支援サービスをご提供しております。 もしご興味をお持ちの方は、こちらのお問合せフォームよりお問合せ頂けましたら幸いでございます。
関連記事
blog.serverworks.co.jp blog.serverworks.co.jp
◆ 塩野 正人
◆ マネージドサービス部 所属
◆ X(Twitter):@shioccii
◆ 過去記事はこちら
前職ではオンプレミスで仮想化基盤の構築や運用に従事。現在は運用部隊でNew Relicを使ってサービス改善に奮闘中。New Relic User Group運営。