Cloudflare + AWS SSM 자동화 가이드

목적

  • Cloudflare 배포 필수값을 AWS SSM 한 곳에 저장
  • 배포 워크플로가 SSM 에서 직접 읽게 한다 (GitHub Secret/Variable 복사본을 두지 않는다)
  • main push 시 블로그 자동 배포

2026-09-13 에 GitHub Repository Secret 25개·Variable 8개를 전량 삭제했다. 워크플로에 $ · $ 가 하나라도 있으면 scripts/ci/check-no-github-config.mjs 가 CI 를 실패시킨다. 그래서 이 문서에서 “GitHub 에 동기화” 하는 절차는 더 이상 없다.

필요한 값 (사용자가 제공)

  1. CLOUDFLARE_ACCOUNT_ID 또는 CF_ACCOUNT_ID
  2. CLOUDFLARE_API_TOKEN 또는 CF_API_TOKEN
  3. CLOUDFLARE_WEB_ANALYTICS_TOKEN 또는 CF_WEB_ANALYTICS_TOKEN
  4. CLOUDFLARE_PAGES_PROJECT 또는 CF_PROJECT_NAME (kjg-blog) — 비밀이 아니고 SSM 에 두지 않는다. 배포 워크플로가 CLOUDFLARE_PROJECT_NAME 리터럴로 들고 있다.

SSM 경로

배포 워크플로가 실제로 읽는 경로다. 예전 문서는 /solodev/ci/blog/cloudflare/* 를 적어 두었는데 그 경로에는 파라미터가 한 번도 만들어진 적이 없었다 — 등록하는 곳과 읽는 곳이 달라서, 등록 스크립트를 돌려도 배포에는 아무 영향이 없었다(2026-09-13 수정).

키 SSM 경로 타입
Cloudflare Account ID /solodev/ci/github-secrets/CLOUDFLARE_ACCOUNT_ID SecureString
Cloudflare API Token /solodev/ci/github-secrets/CLOUDFLARE_API_TOKEN SecureString
Cloudflare Web Analytics Token /solodev/ci/github-secrets/CLOUDFLARE_WEB_ANALYTICS_TOKEN SecureString

워크플로가 읽는 방법

- uses: ./.github/actions/aws-secrets-read
  with: { role-session-name: solodev-blog-secrets-read }
- run: |
    bash scripts/ci/load-ssm-into-env.sh \
      CLOUDFLARE_ACCOUNT_ID CLOUDFLARE_API_TOKEN CLOUDFLARE_WEB_ANALYTICS_TOKEN

로더가 값을 GITHUB_ENV 에 넣고 로그에서 가린다. 비밀이 아닌 값은 --prefix /solodev/ci/github-variables --no-mask 로 읽는다.

로컬 실행

로컬은 Secret 값을 출력하지 않는 동일한 등록 경로를 사용합니다. --dry-run은 AWS에 쓰지 않고 대상 파라미터와 타입만 확인합니다.

CLOUDFLARE_ACCOUNT_ID=... \
CLOUDFLARE_API_TOKEN=... \
CLOUDFLARE_WEB_ANALYTICS_TOKEN=... \
CLOUDFLARE_PAGES_PROJECT=kjg-blog \
bash scripts/blog/sync-cloudflare-blog-secrets-to-ssm.sh --dry-run

실제 등록:

CLOUDFLARE_ACCOUNT_ID=... \
CLOUDFLARE_API_TOKEN=... \
CLOUDFLARE_WEB_ANALYTICS_TOKEN=... \
CLOUDFLARE_PAGES_PROJECT=kjg-blog \
bash scripts/blog/sync-cloudflare-blog-secrets-to-ssm.sh --overwrite

원클릭 실행

CF_ACCOUNT_ID=... \
CF_API_TOKEN=... \
CF_WEB_ANALYTICS_TOKEN=... \
CF_PROJECT_NAME=kjg-blog \
scripts/blog/bootstrap-cloudflare-pages-from-values.sh --overwrite --create-project --trigger-deploy

실행 내용:

  1. SSM 파라미터 등록
  2. Cloudflare Pages 프로젝트 생성 시도
  3. 배포 워크플로우 실행
  4. 준비 상태 점검

GitHub 쪽 동기화 단계는 없앴다 — 워크플로가 SSM 에서 직접 읽는다.

수동 단계별 실행

1) SSM 등록

CF_ACCOUNT_ID=... \
CF_API_TOKEN=... \
CF_PROJECT_NAME=kjg-blog \
scripts/blog/register-cloudflare-blog-ssm.sh --overwrite

2) GitHub 동기화 — 없앴다

배포 워크플로가 SSM 에서 직접 읽으므로 GitHub 쪽 복사본을 만들지 않는다. 복사본이 있으면 SSM 과 갈라지고, 갈라진 순간 어느 쪽이 맞는지 알 수 없다. scripts/ci/check-no-github-config.mjs 가 이 규칙을 CI 에서 막는다.

3) 준비 상태 점검

scripts/blog/check-cloudflare-blog-ready.sh

4) 배포 실행

gh workflow run deploy-cloudflare-pages-blog.yml

값 회전(최신화)

Cloudflare 토큰 변경 시 SSM 만 갱신하면 된다. 다음 배포가 그 값을 읽는다.

CF_ACCOUNT_ID=... \
CF_API_TOKEN=... \
CF_PROJECT_NAME=kjg-blog \
scripts/blog/register-cloudflare-blog-ssm.sh --overwrite

실패 시 점검

  1. AWS 권한: aws sts get-caller-identity
  2. SSM 값 확인:
    • aws ssm get-parameter --name /solodev/ci/github-secrets/CLOUDFLARE_ACCOUNT_ID --with-decryption
    • aws ssm get-parameter --name /solodev/ci/github-secrets/CLOUDFLARE_API_TOKEN --with-decryption
    • scripts/blog/check-cloudflare-blog-ready.sh 가 위를 한 번에 본다
  3. GitHub 쪽은 볼 것이 없다 — Secret·Variable 을 두지 않는다(gh secret list 는 비어 있어야 정상)
  4. 워크플로우 로그:
    • gh run list --workflow deploy-cloudflare-pages-blog.yml

GitHub Repository Secret 은 전량 SSM 으로 옮겼고, GitHub 쪽은 비웠다

2026-09-13 에 Repository Secret 25개와 Variable 8개를 전부 삭제했다. 값은 전에 이관해 둔 /solodev/ci/github-secrets/<이름>(SecureString)에 그대로 있고, 워크플로는 거기서만 읽는다.

  • 비밀값: /solodev/ci/github-secrets/<이름> (SecureString)
  • 비밀 아닌 설정값: /solodev/ci/github-variables/<이름> (String)
  • 읽는 방법: scripts/ci/load-ssm-into-env.sh (기본은 secrets 프리픽스 + 자동 마스킹, --prefix/--no-mask 로 설정값용)
  • 규칙 강제: scripts/ci/check-no-github-config.mjs — 워크플로에 $ 나 $ 가 하나라도 있으면 CI 가 실패한다
  • 준비 상태 확인: scripts/blog/check-github-secrets-ssm-ready.sh

scripts/blog/github-repository-secrets.txt 는 이관 당시 무엇이 있었는지의 기록이다. GitHub 에는 이제 대응물이 없으므로 새 비밀값을 만들 때 여기 적을 필요가 없다.

자격증명을 얻기 전에 필요한 값(러너 라벨·OIDC 역할 ARN·리전)만 워크플로에 리터럴로 둔다. SSM 을 읽으려면 먼저 역할을 맡아야 하는데 그 역할 ARN 을 SSM 에서 읽을 수는 없기 때문이다. 셋 다 비밀이 아니고, 리터럴이어야 바뀐 이유가 PR diff 에 남는다.

GITHUB_TOKEN 은 Repository Secret 이 아니라 실행마다 발급되는 내장 token 이라 대상이 아니다. 워크플로에서는 $ 을 쓴다.