🐷

GitHub Actions + AWSでterraform apply

2024/08/08に公開

OIDCでGitHub ActionsとAWSを連携し、terraform applyしてVPC作成をやってみました。

環境

Terraform v1.9.3

【準備】GitHub Actionsでaws s3 lsができることを確認

https://docs.github.com/ja/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services
を参考に、GitHub Actionsでaws s3 lsができることを確認します。

ドキュメントをほぼなぞっているだけです。

AWS

IDプロバイダー

IDプロバイダーを追加します。

  • プロバイダのタイプ : OpenID Connect
  • プロバイダURL : https://token.actions.githubusercontent.com
  • 対象者 : sts.amazonaws.com

IAMロール

IAMロールを作成します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::[AWSアカウントID]:oidc-provider/token.actions.githubusercontent.com"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringLike": {
                    "token.actions.githubusercontent.com:sub": "repo:[GitHubのリポジトリの所有者]/[GitHubのリポジトリ名]:*"
                },
                "StringEquals": {
                    "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
                }
            }
        }
    ]
}

StringLikeでアクセスしたいGitHubリポジトリを設定しています。
このGitHubリポジトリの設定が抜けてしまうと、全てのGitHubリポジトリからアクセスできてしまうので、気をつけねばなりません(怖いよぉ)。

ちなみに上記の設定ではブランチを指定していないので、specific github repo and branch recommendedという警告が出ました。が、スルーします。

ブランチ指定したい場合は、

-               "StringLike": {
-                   "token.actions.githubusercontent.com:sub": "repo:[GitHubのリポジトリの所有者]/[GitHubのリポジトリ名]:*"
-               },
                "StringEquals": {
+                   "token.actions.githubusercontent.com:sub": "repo:[GitHubのリポジトリの所有者]/[GitHubのリポジトリ名]::ref:refs/heads/[ブランチ名]",
                    "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
                },

と書けばよいです。

次にGitHub Actionsでaws s3 lsコマンドを実行してみようと思うので、IAMポリシーはReadOnlyAccessを付与します。

ロール名を好きな名前に設定して、作成をポチります。

GitHub

ワークフローファイルを作成します。

.github/workflows/oidc-check.yml
name: Check Access AWS
on:
  pull_request
env:
  AWS_REGION : "ap-northeast-1"
permissions:
  id-token: write
  contents: read
jobs:
  LsS3:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Configure aws credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.IAM_ROLE_NAME }}
          role-session-name: MyGithubActionsSession
          aws-region: ${{ env.AWS_REGION }}
      - name: s3 ls
        run: |
          aws s3 ls

AWS_ACCOUNT_IDとIAM_ROLE_NAMEをGitHub上でsecretに登録しておきます。
IAM_ROLE_NAMEはsecretには当たらないですが、IAMロールのGitHubリポジトリの設定をみすり、かつIAMロールのARNもお漏らししてしまったときに、被害を最小限にするためにsecretに登録します。

これでPRを作成すると、結果は写しませんが、無事s3 lsでS3バケット一覧を取得できました。

GitHub ActionsでAWSにterraform applyする

ここまできたら詰まりそうなところはほぼ完了です。
TerraformでVPC作成をしてみようと思います。

AWS

tfstate用S3バケット作成

tfstateを保存するためのS3バケットを作成しておきます。

IAMロールの編集

先ほど作成した

  • IAMロールにVPCを作成できる
  • tfstate用S3バケットをコネコネする

ためのインラインポリシー追加します。

{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Sid": "AmazonVPCCreateRole",
			"Effect": "Allow",
			"Action": [
				"ec2:CreateVpc",
				"ec2:DescribeVpcs",
				"ec2:CreateTags",
				"ec2:DescribeVpcAttribute"
			],
			"Resource": "*"
		},
		{
		    "Sid": "AmazonS3ListRole",
			"Effect": "Allow",
			"Action": "s3:ListBucket",
			"Resource": "arn:aws:s3:::[作成したtfstate保存用s3バケット名]"
		},
		{
		    "Sid": "AmazonS3OperateRole",
			"Effect": "Allow",
			"Action": [
				"s3:GetObject",
				"s3:PutObject",
				"s3:DeleteObject"
			],
			"Resource": [
				"arn:aws:s3:::[作成したtfstate保存用s3バケット名]/*"
			]
		}
	]
}

ポリシー名をつけて保存をポチります。

GitHub

terraform

構成は下記の通りです。

./
└── terraform
    ├── env
    │   └── dev
    │       ├── local.tf
    │       ├── main.tf
    │       └── provider.tf
    └── modules
        └── vpc
            ├── varriables.tf
            └── vpc.tf

VPCを作成するためのファイルを新規追加していきます。

terraform/env/dev/local.tf
locals {
  env         = "dev"
  region      = "ap-northeast-1"
  name_prefix = "githubactions-oidc-${local.env}"
}
terraform/env/dev/main.tf
module "vpc" {
  source      = "../../modules/vpc"
  name_prefix = local.name_prefix
}
terraform/env/dev/provider.tf
provider "aws" {
  region = local.region

  default_tags {
    tags = {
      Environment = local.env
    }
  }
}

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "5.61.0"
    }
  }
  required_version = "~>1.9.0"
  backend "s3" {
    bucket = "[tfstate用S3バケット名]"
    key    = "dev.tfstate"
    region = "ap-northeast-1"
  }
}
terraform/modules/vpc/varriables.tf
variable "name_prefix" {
  type = string
}
terraform/modules/vpc/vpc.tf
resource "aws_vpc" "main" {
  cidr_block = "10.1.0.0/16"

  tags = {
    Name = "${var.name_prefix}-vpc"
  }
}

ワークフローファイル

terraform applyするようにワークフローファイルを編集します。

.github/workflows/oidc-check.yml
name: Check Access AWS
on:
  pull_request
env:
  AWS_REGION : "ap-northeast-1"
permissions:
  id-token: write
  contents: read
jobs:
  TerraformApply:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: ./terraform/env/dev
    steps:
      - uses: actions/checkout@v4

      - name: Configure aws credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.IAM_ROLE_NAME }}
          role-session-name: MyGithubActionsSession
          aws-region: ${{ env.AWS_REGION }}

      - name: Setup terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: "1.9.3"

      - name: Terraform init
        run: terraform init

      - name: Terraform plan
        run: terraform plan

      - name: Terraform apply
        run: terraform apply -auto-approve

これでPRを作成すると無事にGitHub ActionsでAWSにVPCを作成することができました。

Terraform will perform the following actions:

  # module.vpc.aws_vpc.main will be created
  + resource "aws_vpc" "main" {
      + arn                                  = (known after apply)
      + cidr_block                           = "10.1.0.0/16"
      + default_network_acl_id               = (known after apply)
      + default_route_table_id               = (known after apply)
      + default_security_group_id            = (known after apply)
      + dhcp_options_id                      = (known after apply)
      + enable_dns_hostnames                 = (known after apply)
      + enable_dns_support                   = true
      + enable_network_address_usage_metrics = (known after apply)
      + id                                   = (known after apply)
      + instance_tenancy                     = "default"
      + ipv6_association_id                  = (known after apply)
      + ipv6_cidr_block                      = (known after apply)
      + ipv6_cidr_block_network_border_group = (known after apply)
      + main_route_table_id                  = (known after apply)
      + owner_id                             = (known after apply)
      + tags                                 = {
          + "Name" = "***-dev-vpc"
        }
      + tags_all                             = {
          + "Environment" = "dev"
          + "Name"        = "***-dev-vpc"
        }
    }

Plan: 1 to add, 0 to change, 0 to destroy.

改良ポイント

実際に開発を行うときは、追加でやっておきたい点を挙げます。

  • 手動で作成したOIDCプロバイダーとIAMロールもTerraform管理にする
  • PR作成でapplyしたいことはないと思うので、
    • PR作成時はterraform planまで
    • mainブランチなど特定ブランチへのマージ時にterraform applyをするようにする
  • terraform apply前に承認プロセスを入れる
  • GitHub Actionsのワークフローを手動でも実行できるようにする
  • github/workflows/oidc-check.ymlworking-directory: ./terraform/env/devとベタ書きしているのを環境指定で実行できるようにする

参考

https://docs.github.com/ja/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services

https://zenn.dev/kou_pg_0131/articles/gh-actions-oidc-aws

https://www.amazon.co.jp/GitHub-CI-CD実践ガイド――持続可能なソフトウェア開発を支えるGitHub-Actionsの設計と運用-エンジニア選書/dp/4297141736

株式会社ゆめみ

Discussion