0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Day 12:ブランチで自動的に STAGING / PROD を切り替える

Posted at

この記事は AWS Lambda 実践入門 Advent Calendar 2025 12日目の記事です。

はじめに

Lambda のデプロイで最も多い事故は、

  • 本番に誤って STAGING 用の設定を流した
  • STAGING に本番の Layer を適用してしまった
  • デプロイ先の Stack 名を間違えた(検証のつもりが本番更新)

など、環境の取り違えです。

そしてこの手の事故は、
「気をつける」「チェックリストで防ぐ」では再発します。

根絶する最も簡単な方法が、
“ブランチ名で環境を自動判定し、人が環境を選べないようにする” 仕組みです。

前提:ブランチ戦略を「環境」と一致させる

まず前提として、運用ルールをこのように固定します。

  • main への変更=本番(PROD)
  • main 以外(feature / develop 等)=検証(STAGING)

これを CI/CD 側が強制すれば、「環境を選ぶ UI」「手入力の ENV」自体が不要になります。

実装例(CircleCI):ブランチで ENV_NAME を決める

if [ "${CIRCLE_BRANCH}" = "main" ]; then
  export ENV_NAME="PROD"
else
  export ENV_NAME="STAGING"
fi

echo "Using environment: ${ENV_NAME}"

ポイントは ENV_NAME を “出力して終わり” にしないことです。
この値を Stack 名、SAM Parameters、参照する Layer、設定取得先まで一気通貫で反映させると、事故がほぼ消えます。

SAM へのパラメータ注入:EnvName を “中心キー” にする

sam deploy \
  --parameter-overrides EnvName=${ENV_NAME} \
  --no-fail-on-empty-changeset

template.yaml 側は最低限これで受け取れます。

Parameters:
  EnvName:
    Type: String
    AllowedValues:
      - STAGING
      - PROD

AllowedValues を入れるだけで、想定外の値(typo など)をテンプレ側で弾けます。地味ですが事故防止に効きます。

事故が “ほぼゼロ” になる設計(重要)

ENV_NAME を受け取るだけだと、まだ事故は残ります。
「人が間違えても壊れない」状態にするには、次の 3 点まで連動させるのが実務パターンです。

1) Stack 名を環境で固定する(取り違え防止の本丸)

STACK_NAME="myapp-${ENV_NAME,,}"  # prod / staging にしたい場合
sam deploy --stack-name "${STACK_NAME}" ...

Stack 名が環境で分かれていれば STAGING のつもりで PROD を更新する 事故が構造的に起きにくくなります。

2) “設定の参照先” を環境で分ける(SSM/Secrets の混線を防ぐ)

例えば SSM Parameter Store を使うなら、パスを環境で分けます。

  • /myapp/STAGING/db/url
  • /myapp/PROD/db/url

Lambda 側は EnvName でパスを組み立てる、あるいは Globals の環境変数で渡します。

Globals:
  Function:
    Environment:
      Variables:
        ENV_NAME: !Ref EnvName
        PARAM_PREFIX: !Sub "/myapp/${EnvName}"

3) Layer / リソース参照も “環境で固定” する(最もやりがちな事故)

STAGING には STAGING の Layer、PROD には PROD の Layer。これをテンプレ側で Conditions にして固定すると安全です。

Conditions:
  IsProd: !Equals [!Ref EnvName, "PROD"]

Resources:
  MyFunction:
    Type: AWS::Serverless::Function
    Properties:
      # 例:環境別で参照する Layer ARN を切替
      Layers:
        - !If
          - IsProd
          - arn:aws:lambda:ap-northeast-1:123456789012:layer:my-layer-prod:10
          - arn:aws:lambda:ap-northeast-1:123456789012:layer:my-layer-staging:42

ここまでやると、人間が “Layer を貼り間違える余地” がなくなります。

CircleCI 側を「再利用可能な形」にする(実務向け)

上の bash を毎回コピペすると、いつかズレます。CircleCI の commands: に切り出すと運用が安定します。

commands:
  set_env_from_branch:
    steps:
      - run:
          name: Set ENV_NAME from branch
          command: |
            if [ "${CIRCLE_BRANCH}" = "main" ]; then
              echo 'export ENV_NAME=PROD' >> $BASH_ENV
            else
              echo 'export ENV_NAME=STAGING' >> $BASH_ENV
            fi
            source $BASH_ENV
            echo "Using environment: ${ENV_NAME}"

以降の job はこれを呼ぶだけにします。

図解:環境切替フロー(全体像)

元の図は分かりやすいので、“どこに効くか” まで線を伸ばすとさらに良くなります。

まとめ

  • 環境事故の最大原因は「人が環境を選べる」こと
  • main なら PROD、それ以外は STAGING を CI が自動決定すればヒューマンエラーが消える
  • EnvName を中心に Stack 名・設定参照先・Layerまで連動させると、実務で壊れない
  • 次回は Lambda の「本番デプロイ戦略」(段階リリース/ロールバック/承認フローなど)へ

付録:CircleCI config.yml 完成形サンプル(ENV 切替 + OIDC Role 切替)

以下は、Day11(OIDC で鍵なし認証)→ Day12(ブランチで環境自動判定)に自然接続する 付録:CircleCI config.yml “完成形サンプル” です。
狙いは「環境取り違え」を “人の操作” から排除し、さらに PROD は強い権限・STAGING は弱い権限 を OIDC Role で分離(または Permission Boundary で縛る)して事故耐性を上げることです。

想定

  • ブランチ mainPROD

  • それ以外 → STAGING

  • AWS IAM Role は環境別に分離

    • AWS_ROLE_ARN_PROD
    • AWS_ROLE_ARN_STAGING
  • sam deploy--stack-name--parameter-overrides EnvName=... を強制

注意:下記は “動く骨格” に寄せています。あなたの既存 orbs / jobs 構成に合わせて、aws-cli/setup 等は読み替えてください。

version: 2.1

orbs:
  aws-cli: circleci/aws-cli@5.1.1

executors:
  sam-machine:
    machine:
      image: ubuntu-2204:2023.10.1
    resource_class: medium

commands:
  set_env_from_branch:
    description: "Derive ENV_NAME and AWS_ROLE_ARN from branch"
    steps:
      - run:
          name: Set ENV_NAME / AWS_ROLE_ARN
          command: |
            if [ "${CIRCLE_BRANCH}" = "main" ]; then
              echo 'export ENV_NAME=PROD' >> "$BASH_ENV"
              echo 'export AWS_ROLE_ARN=${AWS_ROLE_ARN_PROD}' >> "$BASH_ENV"
            else
              echo 'export ENV_NAME=STAGING' >> "$BASH_ENV"
              echo 'export AWS_ROLE_ARN=${AWS_ROLE_ARN_STAGING}' >> "$BASH_ENV"
            fi

            # 以降の step で参照できるように読み込み
            source "$BASH_ENV"

            # サニティチェック(未設定事故を明確に落とす)
            test -n "${ENV_NAME}"
            test -n "${AWS_ROLE_ARN}"

            echo "ENV_NAME=${ENV_NAME}"
            echo "AWS_ROLE_ARN=${AWS_ROLE_ARN}"

  assume_role_with_oidc:
    description: "Assume AWS role via OIDC (no static keys)"
    steps:
      - aws-cli/setup:
          role_arn: ${AWS_ROLE_ARN}
          role_session_name: "circleci-<< pipeline.id >>"
          session_duration: "900"
      - run:
          name: Verify caller identity
          command: |
            aws sts get-caller-identity

  sam_deploy:
    description: "Deploy with forced env mapping"
    parameters:
      region:
        type: string
        default: "ap-northeast-1"
      stack_base:
        type: string
        default: "myapp"
    steps:
      - run:
          name: Deploy with SAM
          command: |
            source "$BASH_ENV"

            STACK_NAME="<< parameters.stack_base >>-${ENV_NAME,,}"
            echo "STACK_NAME=${STACK_NAME}"

            sam deploy \
              --region "<< parameters.region >>" \
              --stack-name "${STACK_NAME}" \
              --capabilities CAPABILITY_IAM \
              --no-confirm-changeset \
              --no-fail-on-empty-changeset \
              --parameter-overrides EnvName="${ENV_NAME}"

jobs:
  deploy:
    executor: sam-machine
    steps:
      - checkout
      - set_env_from_branch
      - assume_role_with_oidc
      - sam_deploy:
          region: "ap-northeast-1"
          stack_base: "lambda-function-sample"

workflows:
  deploy_pipeline:
    jobs:
      - deploy

この構成で「事故が消える」理由

  • ENV_NAME と Role をブランチで確定し、手入力や選択 UI を排除
  • PROD/STAGING で別 Roleなので、仮にデプロイコマンドを誤っても “権限が足りずに落ちる” 可能性が上がる
  • STACK_NAME も環境連動で固定するため、同名 Stack 上書き事故が起きにくい

代替案:Role は 1 本のまま、Permission Boundary で縛る(設計パターン)

「Role を 2 本に分けるのは管理が大変」という場合、Role は 1 本でもできます。
その代わり、IAM 側で次を徹底します。

  • CircleCI が Assume する Role は 1 本(例:circleci-deploy-role

  • その Role に Permission Boundary を必ず付与

  • Boundary で、環境ごとの操作対象を制限

    • 例:lambda-function-sample-prod を更新できる権限は main 相当だけ…など

ただし、Boundary で「ブランチ」自体は判定できません。
そのため現実的には以下のどちらかになります。

  1. Role 分離(推奨):PROD 用 Role と STAGING 用 Role を分ける
  2. Boundary + 対象制限:環境別リソース名(Stack 名 / Function 名 / Parameter パス)を固定し、境界で “触れる対象” を絞る
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?