GitHub ActionsとCloudFormationでVPCをデプロイ
近年、インフラのコード化(IaC)が標準となる中で、GitHub Actionsを利用したAWSリソースの自動デプロイは一般的な構成になってきています。
今回は、Self-hosted Runnerを利用し、GitHub ActionsからCloudFormationを通じてVPCリソースをデプロイする一連の流れを解説します。
アーキテクチャ

Self-hosted Runnerを利用するメリット
通常のGitHub-hosted runnerではなく、自前の環境(EC2など)で実行するSelf-hosted Runnerを利用することで、特定のネットワーク要件(VPC内リソースへのアクセス)や、実行環境のカスタマイズが容易になります。
また、プライベートリポジトリであっても、Self-hosted Runnerであれば無料で利用が可能です。
GitHub Actionsの課金の詳細についてはGitHubのドキュメントをご参照ください。
※GitHub Acttionsの課金
Self-hosted Runnerのセットアップは本稿の趣旨とは逸れますので、記載はしておりませんが、参考として以下の記事をご参照ください。
※GitHub Actionsのセルフホステッドランナーをセットアップ
IDプロバイダーの設定(GitHub連携)
AWSマネジメントコンソールにログインして、IAMプロバイダを設定します。
| プロバイダのタイプ | OpenID Connect |
| プロバイダのURL | https://token.actions.githubusercontent.com |
| 対象者 | sts.amazonaws.com |
IAMロールの作成(GitHub連携)
IAMロールを作成します。
| IAMロール名 | GitHubActionsRole |
| 許可ポリシー | AmazonVPCFullAccess AWSCloudFormationFullAccess ※今回はFullAccessにしましたが、本番環境ではスコープを絞った権限を付与することを推奨します。 |
信頼ポリシーは以下を設定します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::{YOUR_AWS_ACCOUNT_ID}:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:{YOUR_GITHUB_USER_NAME}/{YOUR_GITHUB_REPOSITORY}:*"
}
}
}
]
}VPCデプロイ用のCloudFormationテンプレート
まずは、ベースとなるVPCリソースを定義したYAMLファイルを作成します。
環境名(Environment)などをパラメータ化し、再利用性を高めています。
ファイルパス:cloudformation/vpc-stack.yaml
Description: Provision VPC
Parameters:
Environment:
Type: String
Subsystem:
Type: String
Default: common
Usage:
Type: String
Default: vpc
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
Tags:
- Key: Name
Value: !Sub ${Environment}-${Subsystem}-${Usage}
- Key: Environment
Value: !Ref Environment
Outputs:
VpcId:
Description: The ID of the VPC
Value: !Ref VPC
VpcCidr:
Description: The CIDR block of the VPC
Value: !GetAtt VPC.CidrBlock
作成したYAMLファイルはGitHubのリポジトリのデフォルトブランチにマージしておきましょう。
GitHub Actionsのワークフロー定義
次に、GitHub ActionsのWorkflowを作成します。
今回はセキュリティを考慮し、OIDC(OpenID Connect)を利用してAWSの認証情報を取得する構成です。
cfn-lintによる構文チェックとバリデーション取得、VPCデプロイを行います。
また、cfn-lintはSelf-hosted-Runnerにすでにインストールされていることが前提になります。
ワークフローのポイント
- runs-on: self-hosted: 実行環境にSelf-hosted Runnerを指定します。
- OIDC認証: IAMロールのARNを環境変数にセットし、一時的な認証情報を取得します。
- cfn-lintがインストールされていない場合は Self-hosted RunnerとなるOSにcfn-lintをインストールしておきます。
- (例)
pip install cfn-lint
- (例)
ファイルパス:.github/workflows/provision-vpc.yaml
name: Provision VPC Stack
on:
workflow_dispatch:
inputs:
environment:
type: choice
description: Provision target
options:
- poc
required: true
default: poc
env:
ENVIRONMENT: ${{ github.event.inputs.environment }}
AWS_REGION: ap-northeast-1
AWS_ROLE_ARN: {YOUR_GITHUB_ACTTIONS_ROLE_ARN}}
jobs:
lint:
name: Lint CloudFormation Template
runs-on: self-hosted
permissions:
id-token: write
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run cfn-lint
run: |
cfn-lint cloudformation/vpc.yaml --format junit --output-file cfn-lint-report.xml
continue-on-error: true
- name: Upload lint results
uses: actions/upload-artifact@v4
if: always()
with:
name: cfn-lint-results
path: cfn-lint-report.xml
validate:
name: Validate CloudFormation Template
runs-on: self-hosted
permissions:
id-token: write
contents: read
needs: lint
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ env.AWS_ROLE_ARN }}
aws-region: ${{ env.AWS_REGION }}
- name: Validate CloudFormation template
run: |
aws cloudformation validate-template \
--template-body file://cloudformation/vpc.yaml
- name: Display validation result
run: |
echo "### CloudFormation Validation ✅" >> $GITHUB_STEP_SUMMARY
echo "Template validation successful" >> $GITHUB_STEP_SUMMARY
deploy:
name: Deploy VPC Stack
runs-on: self-hosted
needs: validate
environment:
name: ${{ github.event.inputs.environment }}
permissions:
id-token: write
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ env.AWS_ROLE_ARN }}
aws-region: ${{ env.AWS_REGION }}
- name: Verify AWS credentials
run: aws sts get-caller-identity
- name: Set stack name
id: stack-info
run: |
STACK_NAME="provision-${{ env.ENVIRONMENT }}-vpc"
echo "stack-name=${STACK_NAME}" >> $GITHUB_OUTPUT
echo "Stack name: ${STACK_NAME}"
- name: Deploy CloudFormation Stack
env:
STACK_NAME: ${{ steps.stack-info.outputs.stack-name }}
run: |
echo "Deploying CloudFormation stack: ${STACK_NAME}"
aws cloudformation deploy \
--stack-name ${STACK_NAME} \
--template-file cloudformation/vpc.yaml \
--parameter-overrides \
Environment=${{ env.ENVIRONMENT }} \
Subsystem=common \
Usage=vpc \
--tags \
Environment=${{ env.ENVIRONMENT }} \
ManagedBy=GitHub-Actions \
Repository=${{ github.repository }} \
DeployedBy=${{ github.actor }} \
--no-fail-on-empty-changeset
echo "Deployment completed!"
- name: Get Stack Outputs
id: stack-outputs
env:
STACK_NAME: ${{ steps.stack-info.outputs.stack-name }}
run: |
VPC_ID=$(aws cloudformation describe-stacks \
--stack-name ${STACK_NAME} \
--query 'Stacks[0].Outputs[?OutputKey==`VpcId`].OutputValue' \
--output text 2>/dev/null || echo "Not available")
VPC_CIDR=$(aws cloudformation describe-stacks \
--stack-name ${STACK_NAME} \
--query 'Stacks[0].Outputs[?OutputKey==`VpcCidr`].OutputValue' \
--output text 2>/dev/null || echo "Not available")
echo "vpc-id=${VPC_ID}" >> $GITHUB_OUTPUT
echo "vpc-cidr=${VPC_CIDR}" >> $GITHUB_OUTPUT
- name: Create Deployment Summary
env:
STACK_NAME: ${{ steps.stack-info.outputs.stack-name }}
VPC_ID: ${{ steps.stack-outputs.outputs.vpc-id }}
VPC_CIDR: ${{ steps.stack-outputs.outputs.vpc-cidr }}
run: |
echo "## 🚀 Deployment Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Property | Value |" >> $GITHUB_STEP_SUMMARY
echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| Stack Name | \`${STACK_NAME}\` |" >> $GITHUB_STEP_SUMMARY
echo "| VPC ID | \`${VPC_ID}\` |" >> $GITHUB_STEP_SUMMARY
echo "| CIDR Block | \`${VPC_CIDR}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Environment | \`${{ env.ENVIRONMENT }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Region | \`${{ env.AWS_REGION }}\` |" >> $GITHUB_STEP_SUMMARY
- name: Cleanup
if: always()
run: rm -f /tmp/awscreds
デプロイの実行と確認
- Self-hosted Runnerを実行します。
- GitHubリポジトリの Actions タブへ移動します。
- Provision VPC Stack を選択し、Run workflowを展開します。

- Use workflow fromのBranch:がデフォルトブランチであることとProvision targetがpocであることを確認し、Run workflow をクリックします。

- Run workflow をクリックします。
- しばらくするとProvision VPC Stack が正常終了するので、Provision VPC Stack をクリックします。

- Workflowの各ステップが正常終了していることが確認できます。Deployment SummaryでStack NameやVPC IDなどの情報が確認できます。

- Validate CloudFormation T… をクリックすると、バリデーションが確認できます。

- AWSマネジメントコンソールのCloudFormation画面で、スタックが
CREATE_COMPLETEになっていることを確認します。

- CloudFormationのスタックからリソースを選択し、vpcがデプロイされていることを確認します。

- VPCがデプロイされました。

まとめ:自動化による効率化
GitHub ActionsとCloudFormationを組み合わせることで、手動操作によるミスを減らし、一貫性のあるインフラ構築が可能になります。Self-hosted Runnerを活用して、よりセキュアで柔軟なデプロイパイプラインを構築しましょう。
VPCのデプロイのみとしていますが、CloudFormationで他のAWSリソースもデプロイすることが可能です。
また、今回はcfn-lintによる構文チェックを入れていますが、より品質を高めるためにpytestによるテストや変更セットを自動で作成して差分チェックさせるなどのような、CICDパイプラインにも応用が可能です。