こんにちは。
アプリケーションサービス部、DevOps担当の兼安です。
今回は、GitHub Actionsのお話をします。
GitHub Actionsは、GitHub上でCI/CDパイプラインを構築するためのツールです。
シンプルで使いやすいツールですが、その構造を理解すると、より複雑なことができるようになります。
特に、複数のAWSアカウントにデプロイを分岐する際には、構造の理解が重要になると思うので、今回はそのお話をしたいと思います。
- 本記事のターゲット
- GitHub Actionsの構造
- WorkflowとJobの依存関係
- Workflowのトリガー
- Jobの実行環境
- Environmentと変数
- EnvironmentとJobの紐付け
- Environmentに条件を設ける
- AWSアカウント認証情報の取得
- デプロイ先AWSアカウントの分岐
- まとめ
- 余談
本記事のターゲット
GitHub Actionsを使い始めたばかりの方を対象にしています。
GitHub Actionsの構造
GitHub Actionsは、以下のような階層構造になっています。
- Workflow
- Job
- Step
- Job
Workflowは、GitHub Actionsの最上位の単位です。
.github/workflows
ディレクトリにYAMLファイルとして定義されます。
1つのYAMLファイルが1つのWorkflowを定義します。
Workflow同士は、デフォルトでは互いに独立して実行されます。
Workflowは、複数のJobを持つことができます。
Jobは、Workflow内で実行されるタスクの単位です。
Jobもデフォルトではそれぞれ独立して実行されます。
Jobの中には、複数のStepを定義することができます。
Stepは、実際に実行されるコマンドやアクションの単位です。
Jobの中のStepは、直列で実行されます。
ワークフロー実行は、既定で並列実行される 1 つ以上の jobs で構成されます。
graph TD R["Repository<br>.github/workflows"] --> A["01_basic_workflow_1.yml"] R --> D["02_basic_workflow_2.yml"] A --> B[Job 1] A --> C[Job 2] D --> E[Job 1] D --> F[Job 2] B --> G[Step 1] G --> H[Step 2] H --> I[Step 3] C --> J[Step 1] J --> K[Step 2] E --> L[Step 1] L --> M[Step 2] F --> N[Step 1] N --> O[Step 2] O --> P[Step 3] style R fill:#f9f9f9 style A fill:#e1f5fe style D fill:#e1f5fe style B fill:#f3e5f5 style C fill:#f3e5f5 style E fill:#f3e5f5 style F fill:#f3e5f5 style G fill:#fff3e0 style H fill:#fff3e0 style I fill:#fff3e0 style J fill:#fff3e0 style K fill:#fff3e0 style L fill:#fff3e0 style M fill:#fff3e0 style N fill:#fff3e0 style O fill:#fff3e0 style P fill:#fff3e0
この場合、ワークフローのYAMLファイルは以下のように配置します。
.github/workflows/ ├── 01_basic_workflow_1.yml └── 02_basic_workflow_2.yml
それぞれのYAMLファイルは、以下のような内容になります。
01_basic_workflow_1.yml
name: Basic Workflow 1 on: [push] jobs: job1: runs-on: ubuntu-latest steps: - name: Step 1 run: | echo "Job1-Step1 started: $(date '+%Y-%m-%d %H:%M:%S')" sleep 30 echo "Job1-Step1 completed: $(date '+%Y-%m-%d %H:%M:%S')" - name: Step 2 run: | echo "Job1-Step2 started: $(date '+%Y-%m-%d %H:%M:%S')" sleep 30 echo "Job1-Step2 completed: $(date '+%Y-%m-%d %H:%M:%S')" - name: Step 3 run: | echo "Job1-Step3 started: $(date '+%Y-%m-%d %H:%M:%S')" sleep 30 echo "Job1-Step3 completed: $(date '+%Y-%m-%d %H:%M:%S')" job2: runs-on: ubuntu-latest steps: - name: Step 1 run: | echo "Job2-Step1 started: $(date '+%Y-%m-%d %H:%M:%S')" sleep 30 echo "Job2-Step1 completed: $(date '+%Y-%m-%d %H:%M:%S')" - name: Step 2 run: | echo "Job2-Step2 started: $(date '+%Y-%m-%d %H:%M:%S')" sleep 30 echo "Job2-Step2 completed: $(date '+%Y-%m-%d %H:%M:%S')"
02_basic_workflow_2.yml
name: Basic Workflow 2 on: [push] jobs: job1: runs-on: ubuntu-latest steps: - name: Step 1 run: | echo "Workflow2 Job1 Step1 started: $(date '+%Y-%m-%d %H:%M:%S')" sleep 30 echo "Workflow2 Job1 Step1 completed: $(date '+%Y-%m-%d %H:%M:%S')" - name: Step 2 run: | echo "Workflow2 Job1 Step2 started: $(date '+%Y-%m-%d %H:%M:%S')" sleep 30 echo "Workflow2 Job1 Step2 completed: $(date '+%Y-%m-%d %H:%M:%S')" job2: runs-on: ubuntu-latest steps: - name: Step 1 run: | echo "Workflow2 Job2 Step1 started: $(date '+%Y-%m-%d %H:%M:%S')" sleep 30 echo "Workflow2 Job2 Step1 completed: $(date '+%Y-%m-%d %H:%M:%S')" - name: Step 2 run: | echo "Workflow2 Job2 Step2 started: $(date '+%Y-%m-%d %H:%M:%S')" sleep 30 echo "Workflow2 Job2 Step2 completed: $(date '+%Y-%m-%d %H:%M:%S')" - name: Step 3 run: | echo "Workflow2 Job2 Step3 started: $(date '+%Y-%m-%d %H:%M:%S')" sleep 30 echo "Workflow2 Job2 Step3 completed: $(date '+%Y-%m-%d %H:%M:%S')"
実行の様子
実行履歴から、Workflow・Jobsは独立して実行され、Stepsは直列で実行されることが確認できます。



WorkflowとJobの依存関係
WorkflowやJobは、デフォルトでは互いに独立して実行されますが、依存関係を設定することもできます。
Workflow同士の依存関係
Workflow間の依存関係は、workflow_run
イベントを使用して設定します。
これにより、あるWorkflowが完了した後に別のWorkflowを実行することができます。
ワークフローをトリガーするイベント - GitHub Docs
name: Dependent Workflow on: workflow_run: workflows: ["Basic Workflow 1"] types: - completed jobs: dependent_job: runs-on: ubuntu-latest steps: - name: Run after Basic Workflow 1 run: | echo "Dependent workflow started: $(date '+%Y-%m-%d %H:%M:%S')" echo "This job runs after Basic Workflow 1 is completed" sleep 30 echo "Dependent workflow processing completed: $(date '+%Y-%m-%d %H:%M:%S')" - name: Check previous workflow status run: | echo "Previous workflow status check started: $(date '+%Y-%m-%d %H:%M:%S')" echo "Previous workflow conclusion: ${{ github.event.workflow_run.conclusion }}" if [[ "${{ github.event.workflow_run.conclusion }}" == "success" ]]; then echo "Previous workflow succeeded. Continuing process..." else echo "Previous workflow failed, but this dependent workflow continues" fi sleep 30 echo "Previous workflow status check completed: $(date '+%Y-%m-%d %H:%M:%S')"
このYAMLをpushすると、Workflowの依存関係に基づいて実行されることが確認できます。


Job同士の依存関係
Job同士の依存関係は、needs
キーワードを使用して設定します。
name: Workflow with Job Dependencies on: [push] jobs: job1: runs-on: ubuntu-latest steps: - name: Step 1 run: | echo "Job dependencies workflow Job1 Step1 started: $(date '+%Y-%m-%d %H:%M:%S')" sleep 30 echo "Job dependencies workflow Job1 Step1 completed: $(date '+%Y-%m-%d %H:%M:%S')" - name: Create output id: job1_output run: | echo "Job1 output creation started: $(date '+%Y-%m-%d %H:%M:%S')" echo "result=success_from_job1" >> $GITHUB_OUTPUT echo "Job1 completed successfully" sleep 30 echo "Job1 output creation completed: $(date '+%Y-%m-%d %H:%M:%S')" outputs: job1_result: ${{ steps.job1_output.outputs.result }} job2: runs-on: ubuntu-latest needs: job1 steps: - name: Step 1 run: | echo "Job2 Step1 started: $(date '+%Y-%m-%d %H:%M:%S')" echo "This job depends on job1" sleep 30 echo "Job2 Step1 completed: $(date '+%Y-%m-%d %H:%M:%S')" - name: Use output from job1 run: | echo "Using output from job1 started: $(date '+%Y-%m-%d %H:%M:%S')" echo "Received value from job1: ${{ needs.job1.outputs.job1_result }}" sleep 30 echo "Using output from job1 completed: $(date '+%Y-%m-%d %H:%M:%S')" job3: runs-on: ubuntu-latest needs: [job1, job2] steps: - name: Step 1 run: | echo "Job3 Step1 started: $(date '+%Y-%m-%d %H:%M:%S')" echo "This job depends on both job1 and job2" sleep 30 echo "Job3 Step1 completed: $(date '+%Y-%m-%d %H:%M:%S')"
このYAMLをpushすると、Jobの依存関係に基づいて実行されることが確認できます。

Workflowのトリガー
Workflowは、特定のイベントによってトリガーされます。
ワークフローをトリガーするイベント - GitHub Docs
代表的なトリガーはpush
やpull_request
です。
トリガーはさらに条件を指定することもできます。
push
であれば、以下のように、特定のブランチに対してのみWorkflowを実行することができます。
name: Push to Main Branch on: push: branches: - main jobs: main_branch_job: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Main branch specific step run: echo "This job runs only on push to main branch" - name: Show branch info run: | echo "Current branch: ${{ github.ref_name }}" echo "Event: ${{ github.event_name }}"
name: Push to Develop Branch on: push: branches: - develop jobs: develop_branch_job: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Develop branch specific step run: echo "This job runs only on push to develop branch" - name: Show branch info run: | echo "Current branch: ${{ github.ref_name }}" echo "Event: ${{ github.event_name }}"
Jobの実行環境
Jobは、runs-on
キーワードで実行環境を指定します。
実行環境はRunnerと呼ばれ、GitHubから提供されるGitHub-Hosted Runnerを使用します。
GitHub ホステッド ランナーの概要 - GitHub Docs
Runnerは、自前で用意したり、Workflowの中で選択肢を限定することもできます。
このあたりは別の機会に詳しくお話しできればと思います。
GitHub Actionsでは、Job単位でRunnerを指定することができます。
以下の例では、3つのJobを異なるRunnerで実行し、OSのバージョンを確認しています。
name: Job with Different Runners on: [push] jobs: ubuntu_job: runs-on: ubuntu-latest steps: - name: Check Ubuntu Version run: | echo "Running on Ubuntu" lsb_release -a echo "Current user: $(whoami)" echo "Home directory: $HOME" windows_job: runs-on: windows-latest steps: - name: Check Windows Version run: | echo "Running on Windows" systeminfo | findstr /B /C:"OS Name" /C:"OS Version" echo "Current user: $env:USERNAME" echo "Home directory: $env:USERPROFILE" macos_job: runs-on: macos-latest steps: - name: Check macOS Version run: | echo "Running on macOS" sw_vers echo "Current user: $(whoami)" echo "Home directory: $HOME"



Environmentと変数
ここで、一旦Workflowから離れて、Environmentと環境変数について述べます。
GitHubにはEnvironmentという概念があります。
Environmentごとに、VariablesとSecretsを設定することができます。
Variablesは通常の変数、Secretsは機密情報を設定する変数です。
例えば、production
・development
というEnvironmentを作成します。
それぞれに変数を設定します。
この時、変数名が同じであっても、Environmentごとに異なる値を設定することができます。
なお、変数はEnvironmentなしでも設定できますが、本記事では割愛します。
graph TD B[Environment: production] --> D[Variable: API_URL<br/>Value: api.prod.example.com] B --> E[Secret: DATABASE_PASSWORD<br/>Value: ***prod_password***] C[Environment: development] --> F[Variable: API_URL<br/>Value: api.dev.example.com] C --> G[Secret: DATABASE_PASSWORD<br/>Value: ***dev_password***] style B fill:#e8f5e8 style C fill:#e1f5fe style D fill:#f0f8ff style E fill:#ffe4e1 style F fill:#f0f8ff style G fill:#ffe4e1


EnvironmentとJobの紐付け
GitHub Actionsでは、Jobを特定のEnvironmentに紐付けることができます。
graph TD R[Repository] --> A["08_multiple_environments_demo.yml"] A --> B[Job 1] A --> C[Job 2] B --> D[Environment: production] C --> E[Environment: development] style R fill:#f9f9f9 style A fill:#e1f5fe style B fill:#f3e5f5 style C fill:#f3e5f5 style D fill:#e8f5e8 style E fill:#e1f5fe
この場合、ワークフローのYAMLファイルは以下のように配置します。
各Jobは、特定のEnvironmentに紐付けられています。
JobごとにEnvironmentの指定を変更しつつ、Stepで同じ変数を参照すると、Environmentごとに異なる値が出力されます。
name: Multiple Environments Demo on: push: branches: - main jobs: production_job: runs-on: ubuntu-latest environment: production steps: - name: Checkout code uses: actions/checkout@v4 - name: Echo API URL from production environment run: echo "API URL is ${{ vars.API_URL }}" - name: Echo Database password (using fallback if not set) run: echo "Database password is ${{ secrets.DATABASE_PASSWORD || 'not-set' }}" - name: Show environment info run: | echo "Environment: production" echo "Branch: ${{ github.ref_name }}" development_job: runs-on: ubuntu-latest environment: development steps: - name: Checkout code uses: actions/checkout@v4 - name: Echo API URL from development environment run: echo "API URL is ${{ vars.API_URL }}" - name: Echo Database password (using fallback if not set) run: echo "Database password is ${{ secrets.DATABASE_PASSWORD || 'not-set' }}" - name: Show environment info run: | echo "Environment: development" echo "Branch: ${{ github.ref_name }}"


Environmentに条件を設ける
前項の例では、main
ブランチをターゲットとしたWorkflowから、Environment development
にも紐づけているというちょっと不自然な例になっています。
このような不自然な状態を抑制するために、Environmentには条件を設けることができます。
GitHubのSettingsから、Environmentに条件を設定します。

これで、Environment production
は、main
ブランチをトリガーとしたWorkflowからのみ使用できるようになります。
AWSアカウント認証情報の取得
GitHub ActionsでAWSにデプロイするためには、AWSの認証情報が必要です。
2025年6月現在においては、GitHub ActionsでAWSの認証情報を取得するには、OIDCを使用するのがセキュアで良いでしょう。
アマゾン ウェブ サービスでの OpenID Connect の構成 - GitHub Docs
GitHubのOIDCについてはこちらもご覧ください。
デプロイ先のAWSアカウントに、GitHub Actionsからのアクセスを許可するIAMロールを作成します。
作成したIAMロールにデプロイに必要な権限を付与し、信頼ポリシーで特定のGitHubリポジトリからのアクセスのみを許可するようにします。
{ "Version": "2008-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:<組織名>/<リポジトリ名>:<ブランチ(全てのブランチならアスタリスク)>", "token.actions.githubusercontent.com:aud": "sts.amazonaws.com" } } } ] }
EnvironmentにSecretsを作成し、作成したIAMロールのARNを設定します。


Secretsを作成したら、WorkflowのYAMLでそのSecretsを取得して利用します。
name: Deploy to AWS on: push: branches: - main jobs: deploy: runs-on: ubuntu-latest environment: production steps: - name: Checkout code uses: actions/checkout@v4 - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: ${{ secrets.AWS_GITHUB_ACTIONS_ROLE_ARN }} aws-region: ap-northeast-1 - name: Display AWS credentials run: aws sts get-caller-identity
デプロイ先AWSアカウントの分岐
ここまでの内容を踏まえて、デプロイ先のAWSアカウントを分岐させるWorkflowを作成します。
Environmentにproduction
とdevelopment
を作成し、それぞれにOIDC用のIAMロールを設定します。

上述の通り。 Environment production
は、main
ブランチでのみ使用できるように条件を設けます。
そして、main
ブランチのWorkflowでは、production
Environmentを使用し、develop
ブランチのWorkflowでは、development
Environmentを使用します。
graph TD A[Repository] --> B[main branch push] A --> C[develop branch push] B --> D["09_aws_deploy_production.yml"] C --> E["10_aws_deploy_development.yml"] D --> F[Job: deploy_production] E --> G[Job: deploy_development] F --> H[Environment: production] G --> I[Environment: development] E -.-x| 参照不可 | H H --> J[Secret: AWS_GITHUB_ACTIONS_ROLE_ARN<br/>Value: arn:aws:iam::111111111111:role/...] I --> K[Secret: AWS_GITHUB_ACTIONS_ROLE_ARN<br/>Value: arn:aws:iam::222222222222:role/...] J --> L[AWS Account: Production<br/>Account ID: 111111111111] K --> M[AWS Account: Development<br/>Account ID: 222222222222] style A fill:#f9f9f9 style B fill:#e8f5e8 style C fill:#e1f5fe style D fill:#e8f5e8 style E fill:#e1f5fe style F fill:#f3e5f5 style G fill:#f3e5f5 style H fill:#e8f5e8 style I fill:#e1f5fe style J fill:#ffe4e1 style K fill:#ffe4e1 style L fill:#e8f5e8 style M fill:#e1f5fe
YAMLファイルは以下の通りです。
permissions
の設定をしているのと、aws-actions/configure-aws-credentials
でAWSの認証情報を取得しているのがポイントです。
name: Deploy to AWS Production on: push: branches: - main permissions: id-token: write contents: read jobs: deploy_production: runs-on: ubuntu-latest environment: production steps: - name: Checkout code uses: actions/checkout@v4 - name: Configure AWS credentials for production uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: ${{ secrets.AWS_GITHUB_ACTIONS_ROLE_ARN }} aws-region: ap-northeast-1 role-session-name: GitHubActions-Production - name: Display AWS credentials run: | echo "Attempting to get AWS caller identity..." aws sts get-caller-identity || echo "AWS credentials not configured or invalid" - name: List S3 buckets (example deployment check) run: | echo "Listing S3 buckets to verify permissions..." aws s3 ls || echo "Unable to list S3 buckets - check permissions" - name: Deploy simulation run: | echo "Simulating deployment to production environment" echo "Environment: production" echo "Branch: ${{ github.ref_name }}" echo "Commit SHA: ${{ github.sha }}"
name: Deploy to AWS Development on: push: branches: - develop permissions: id-token: write contents: read jobs: deploy_development: runs-on: ubuntu-latest environment: development steps: - name: Checkout code uses: actions/checkout@v4 - name: Configure AWS credentials for development uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: ${{ secrets.AWS_GITHUB_ACTIONS_ROLE_ARN }} aws-region: ap-northeast-1 - name: Display AWS credentials run: | echo "Attempting to get AWS caller identity..." aws sts get-caller-identity || echo "AWS credentials not configured or invalid" - name: List S3 buckets (example deployment check) run: | echo "Listing S3 buckets to verify permissions..." aws s3 ls || echo "Unable to list S3 buckets - check permissions" - name: Deploy simulation run: | echo "Simulating deployment to development environment" echo "Environment: development" echo "Branch: ${{ github.ref_name }}" echo "Commit SHA: ${{ github.sha }}"
これで、それぞれのブランチに対して、異なるAWSアカウントにデプロイすることができます。
また、Environment側に制限を設けているので、誤ってdevelop
ブランチのWorkflowからproduction
のAWSアカウントにデプロイすることを防ぐことができます。
仮に、Deploy to AWS Development
の方に、Job: deploy_production
を追加してproduction
のsecretsにアクセスさせた場合、エラーとなります。

まとめ
- GitHub ActionsはWorkflow、Job、Stepの階層構造を持ちます
- WorkflowとJobは依存関係を設定できます
- Environmentを使用して、変数や機密情報を管理できます
- EnvironmentをJobに紐付けることで、Environmentごとの変数を参照できます
- Environmentは制限を設けることができ、意図しないブランチからのアクセスを防げます
- これらを組み合わせることで、複数のAWSアカウントにデプロイを分岐できます
余談
2025年5月にGitHub CertificationのGitHub Actionsを取得しました。
自宅周辺(広島)にはオフラインの試験会場がなかったため、オンラインで受験したのですが、試験監督の説明がすべて英語テキストだったので、かなり戸惑いました。
まったくわからない内容ではなかったためなんとかなりましたが、これからオンラインで受験される方は私と同じような状況になるかもしれないので、心構えだけしておくことをおすすめします。
兼安 聡(執筆記事の一覧)
アプリケーションサービス部 DS3課所属
2025 Japan AWS Top Engineers (AI/ML Data Engineer)
2025 Japan AWS All Certifications Engineers
2025 AWS Community Builders
Certified ScrumMaster
PMP
広島在住です。今日も明日も修行中です。