cloudNet@ 팀의 가시다 님이 진행하는 AWS EKS Workshop Study(AEWS) 4기 6주차 내용입니다.
GitOps로 구현하는 SaaS 플랫폼 엔지니어링
Amazon EKS와 GitOps 도구
EKS는 Argo CD, Flux, Weave GitOps, Jenkins X, GitLab CI/CD, Spinnaker, Rancher Fleet, Codefresh, Pulumi 등과 같은 다양한 GitOps 도구와 연동이 가능하다.
이 중 가능 자주 사용되는 ArgoCD와 Flux에 대해 비교한 표이다.
영역
Argo CD
Flux
핵심 GitOps 원칙 지원
예
예
아키텍처
Kubernetes GitOps 워크플로우를 구현하는 엔드투엔드 애플리케이션
GitOps를 위한 Kubernetes CRD 및 컨트롤러 제공
설정
간편함
복잡함
Helm 지원
예
예
Kustomize 지원
예
예
통합 GUI
CLI 및 완전한 기능의 웹 UI
CLI 및 선택적 경량 웹 인터페이스
RBAC 지원
세분화된 자체 제어
Kubernetes 네이티브 RBAC 활용
멀티테넌시 및 멀티클러스터 지원
멀티클러스터에 대한 탁월한 지원
멀티테넌시에 대한 탁월한 지원
Single Sign-On 인증
예
예
동기화 자동화
동기화 시간 창(Sync Window) 설정 기능
조정(Reconciliation) 간격 설정 기능
부분 동기화
예
아니요
조정(Reconciliation) 방식
수동 및 자동 동기화 지원. 다양한 전략 선택 가능
수동 및 자동 동기화 지원
확장성(Extensibility)
커스텀 플러그인 지원. 커스터마이징 옵션은 제한적
커스텀 컨트롤러 지원. 높은 확장성과 다양한 서드파티 통합
커뮤니티 지원
크고 활발한 커뮤니티
규모는 작지만 성장 중인 커뮤니티
확장 가능성(Scalability)
양호한 확장성이나 웹 UI의 데이터 조회 속도에 의해 제한됨. 수만 개의 애플리케이션 지원 가능
수평/수직 확장에 대한 명확한 가이드 제공. 수만 개의 애플리케이션까지 지원 가능
DevOps에서 GitOps가 필요한 이유
한편, 복잡한 비즈니스 요구 사항에 대응하기 위해 GitOps를 활용하는 것 또한 가능하며, 이것이 바로 SaaS DevOps 환경에서 GitOps가 필요한 이유에 해당함.
SaaS의 핵심은 민첩성으로, SaaS 모델에서는 고객을 위해 소프트웨어를 실행하고 관리하므로 지속적인 변화와 혁신을 지원하는 DevOps 프로세스가 필요
잦은 릴리스: 신속한 피드백과 기능 제공을 가능하게 지원
운영 효율성: 동일한 프로세스를 통해 모든 테넌트를 일관되게 관리
자동화된 온보딩: 신속하고 일관된 테넌트 생성을 보장하여 효율성을 극대화
성공적인 SaaS 아키텍처는 민첩성, 혁신, 성장 및 비용 효율성을 향상시켜야 한다.
실습1. 앱 및 인프라 설계
왜 개발자 주도형 인프라인가?
현대 조직에서 팀은 일반적으로 애플리케이션 개발팀과 인프라/운영팀, 두 가지로 나뉨. 각 팀의 소유권과 책임은 명확히 구분되어 있으며, 대부분의 애플리케이션은 IAM 역할, SQS 큐, DynamoDB 테이블 같은 특정 인프라 구성 요소를 필요로 함. 이 때문에 개발자가 인프라팀에 리소스 생성을 요청하는 상황이 반복적으로 발생함.
조직 규모가 커질수록 요청이 쌓이며, 인프라팀은 병목이 됨. 현재 관행을 개선할 여유도 없어지고, 테넌트가 늘어날수록 이 문제는 기하급수적으로 악화된다.
개발자 주도형 인프라(Developer-Driven Infrastructure)는 이 문제를 해결하는 패러다임으로, 사전 정의된 보안 및 거버넌스 프레임워크 안에서 개발자가 직접 인프라를 프로비저닝하고 관리할 수 있도록 지원함. IaC(Infrastructure as Code)와 자동화 도구를 통해 셀프서비스 기능을 제공하여, 각 테넌트의 요구 사항에 맞는 인프라를 신속하게 프로비저닝할 수 있음.
Terraform 공식 문서에서 발췌: "모듈은 구성의 일관성을 유지하고, 복잡한 구성을 더 쉽게 이해할 수 있도록 하며, 모든 구성에 걸쳐 모범 사례가 적용되도록 보장함."
실습에서는 Terraform 모듈 → Tofu 컨트롤러 → Helm Chart → HelmRelease의 흐름으로 이 패러다임을 직접 구현해 볼 예정이다.
2. Flux 리소스 탐색: Flux는 이 솔루션의 핵심 엔진으로, Git 및 ECR의 변경 사항을 감시하여 클러스터 상태를 선언된 상태와 일치시키는 역할을 담당함. 다음 명령으로 전체 Flux 리소스를 확인
출력에서 확인할 주요 리소스 유형과 역할
리소스 유형
역할
gitrepository
Flux가 변경 사항을 감시하는 Git 저장소
helmrepository
Helm 차트가 저장된 저장소 위치 (ECR 포함)
helmchart
각 소스에서 가져온 Helm 차트
helmrelease
실제 배포 단위. 하나의 차트를 여러 테넌트에 배포 가능
kustomization
GitRepository를 가리키는 포인터, Kubernetes 구성 관리
imagerepository / imagepolicy
새 컨테이너 이미지 태그 자동 감지 및 정책 적용
imageupdateautomation
새 이미지 감지 시 Git에 자동 커밋 (Image Automation)
# (참고) flux cli https://fluxcd.io/flux/cmd/
flux --help
Command line utility for assembling Kubernetes CD pipelines the GitOps way.
[ec2-user@ip-10-0-1-178 environment]$ flux get all
NAME REVISION SUSPENDED READY MESSAGE
ocirepository/capacitor v0.4.8@sha256:1efcb443 False True stored artifact for digest 'v0.4.8@sha256:1efcb443'
NAME REVISION SUSPENDED READY MESSAGE
gitrepository/flux-system refs/heads/main@sha1:2d19a84a False True stored artifact for revision 'refs/heads/main@sha1:2d19a84a'
gitrepository/terraform-v0-0-1 v0.0.1@sha1:2d19a84a False True stored artifact for revision 'v0.0.1@sha1:2d19a84a'
NAME REVISION SUSPENDED READY MESSAGE
helmrepository/argo sha256:30988989 False True stored artifact: revision 'sha256:30988989'
helmrepository/eks-charts sha256:d8a5d54a False True stored artifact: revision 'sha256:d8a5d54a'
helmrepository/helm-application-chart False True Helm repository is Ready
helmrepository/helm-tenant-chart False True Helm repository is Ready
helmrepository/karpenter False True Helm repository is Ready
helmrepository/kubecost False True Helm repository is Ready
helmrepository/metrics-server sha256:ba69c5bb False True stored artifact: revision 'sha256:ba69c5bb'
helmrepository/tf-controller sha256:1fcad0f6 False True stored artifact: revision 'sha256:1fcad0f6'
NAME REVISION SUSPENDED READY MESSAGE
helmchart/flux-system-argo-events 2.4.3 False True pulled 'argo-events' chart with version '2.4.3'
helmchart/flux-system-argo-workflows 0.40.11 False True pulled 'argo-workflows' chart with version '0.40.11'
helmchart/flux-system-aws-load-balancer-controller 1.6.2 False True pulled 'aws-load-balancer-controller' chart with version '1.6.2'
helmchart/flux-system-karpenter 1.4.0 False True pulled 'karpenter' chart with version '1.4.0'
helmchart/flux-system-kubecost 2.1.0 False True pulled 'cost-analyzer' chart with version '2.1.0'
helmchart/flux-system-metrics-server 3.11.0 False True pulled 'metrics-server' chart with version '3.11.0'
helmchart/flux-system-onboarding-service 0.0.1 False True pulled 'application-chart' chart with version '0.0.1'
helmchart/flux-system-pool-1 0.0.1 False True pulled 'helm-tenant-chart' chart with version '0.0.1'
helmchart/flux-system-tf-controller 0.16.0-rc.4 False True pulled 'tf-controller' chart with version '0.16.0-rc.4'
NAME LAST SCAN SUSPENDED READY MESSAGE
imagerepository/consumer-image-repository 2026-04-13T18:38:28Z False True successful scan: found 2 tags with checksum 1930888933
imagerepository/payments-image-repository 2026-04-13T18:38:28Z False True successful scan: found 2 tags with checksum 1930757861
imagerepository/producer-image-repository 2026-04-13T18:38:29Z False True successful scan: found 2 tags with checksum 1932986088
NAME IMAGE TAG READY MESSAGE
imagepolicy/consumer-image-policy 767397897074.dkr.ecr.ap-northeast-2.amazonaws.com/consumer prd-20260413T183235Z True Latest image tag for 767397897074.dkr.ecr.ap-northeast-2.amazonaws.com/consumer resolved to prd-20260413T183235Z
imagepolicy/payments-image-policy 767397897074.dkr.ecr.ap-northeast-2.amazonaws.com/payments prd-20260413T183217Z True Latest image tag for 767397897074.dkr.ecr.ap-northeast-2.amazonaws.com/payments resolved to prd-20260413T183217Z
imagepolicy/producer-image-policy 767397897074.dkr.ecr.ap-northeast-2.amazonaws.com/producer prd-20260413T183256Z True Latest image tag for 767397897074.dkr.ecr.ap-northeast-2.amazonaws.com/producer resolved to prd-20260413T183256Z
NAME LAST RUN SUSPENDED READY MESSAGE
imageupdateautomation/consumer-update-automation-pooled-envs 2026-04-13T18:37:05Z False True repository up-to-date
imageupdateautomation/consumer-update-automation-tenants 2026-04-13T18:38:29Z False True repository up-to-date
imageupdateautomation/payments-update-automation-pooled-envs 2026-04-13T18:37:05Z False True repository up-to-date
imageupdateautomation/payments-update-automation-tenants 2026-04-13T18:38:29Z False True repository up-to-date
imageupdateautomation/producer-update-automation-pooled-envs 2026-04-13T18:37:05Z False True repository up-to-date
imageupdateautomation/producer-update-automation-tenants 2026-04-13T18:38:29Z False True repository up-to-date
NAME REVISION SUSPENDED READY MESSAGE
helmrelease/argo-events 2.4.3 False True Helm install succeeded for release argo-events/argo-events.v1 with chart argo-events@2.4.3
helmrelease/argo-workflows 0.40.11 False True Helm install succeeded for release argo-workflows/argo-workflows.v1 with chart argo-workflows@0.40.11
helmrelease/aws-load-balancer-controller 1.6.2 False True Helm install succeeded for release aws-system/aws-load-balancer-controller.v1 with chart aws-load-balancer-controller@1.6.2
helmrelease/karpenter 1.4.0 False True Helm install succeeded for release karpenter/karpenter.v1 with chart karpenter@1.4.0
helmrelease/kubecost 2.1.0 False True Helm install succeeded for release kubecost/kubecost.v1 with chart cost-analyzer@2.1.0
helmrelease/metrics-server 3.11.0 False True Helm install succeeded for release kube-system/metrics-server.v1 with chart metrics-server@3.11.0
helmrelease/onboarding-service 0.0.1 False True Helm install succeeded for release onboarding-service/onboarding-service.v1 with chart application-chart@0.0.1
helmrelease/pool-1 0.0.1 False True Helm install succeeded for release pool-1/pool-1.v1 with chart helm-tenant-chart@0.0.1
helmrelease/tf-controller 0.16.0-rc.4 False True Helm install succeeded for release flux-system/tf-controller.v1 with chart tf-controller@0.16.0-rc.4
NAME REVISION SUSPENDED READY MESSAGE
kustomization/capacitor v0.4.8@sha256:1efcb443 False True Applied revision: v0.4.8@sha256:1efcb443
kustomization/controlplane refs/heads/main@sha1:2d19a84a False True Applied revision: refs/heads/main@sha1:2d19a84a
kustomization/dataplane-pooled-envs refs/heads/main@sha1:2d19a84a False True Applied revision: refs/heads/main@sha1:2d19a84a
kustomization/dataplane-tenants refs/heads/main@sha1:2d19a84a False True Applied revision: refs/heads/main@sha1:2d19a84a
kustomization/dependencies refs/heads/main@sha1:2d19a84a False True Applied revision: refs/heads/main@sha1:2d19a84a
kustomization/flux-system refs/heads/main@sha1:2d19a84a False True Applied revision: refs/heads/main@sha1:2d19a84a
kustomization/infrastructure refs/heads/main@sha1:2d19a84a False True Applied revision: refs/heads/main@sha1:2d19a84a
kustomization/sources refs/heads/main@sha1:2d19a84a False True Applied revision: refs/heads/main@sha1:2d19a84a
Gitea 저장소 접근 설정
이번 실습에서는 Gitea에 호스팅된 여러 저장소에 접근하여 클론해야 함. 먼저 접근 권한을 설정하는 것부터 시작
Gitea 웹 인터페이스에 접속하기 위해 다음 스크립트를 복사하여 실행
# Get Gitea IPs from the configuration
export GITEA_PRIVATE_IP=$(kubectl get configmap saas-infra-outputs -n flux-system -o jsonpath='{.data.gitea_url}')
export GITEA_PUBLIC_IP=$(kubectl get configmap saas-infra-outputs -n flux-system -o jsonpath='{.data.gitea_public_url}')
export GITEA_PORT="3000"
# Get Gitea admin password from Systems Manager Parameter Store
export GITEA_ADMIN_PASSWORD=$(aws ssm get-parameter --name "/eks-saas-gitops/gitea-admin-password" --with-decryption --query 'Parameter.Value' --output text)
# Display access information for web browser login
echo "=== Gitea Web Interface Access ==="
echo "Public URL (for browser access): $GITEA_PUBLIC_IP"
echo "Username: admin"
echo "Password: $GITEA_ADMIN_PASSWORD"
echo "=================================="
echo ""
echo "Use the PUBLIC URL above to access Gitea from your web browser."
실행 예시: 출력된 Public URL을 브라우저에서 열어 로그인
URL 접속 안될 경우 AWS 콘솔 내 방화벽 설정에서 본인 IP 주소를 추가
[ec2-user@ip-10-0-1-178 environment]$ # Get Gitea IPs from the configuration
export GITEA_PRIVATE_IP=$(kubectl get configmap saas-infra-outputs -n flux-system -o jsonpath='{.data.gitea_url}')
export GITEA_PUBLIC_IP=$(kubectl get configmap saas-infra-outputs -n flux-system -o jsonpath='{.data.gitea_public_url}')
export GITEA_PORT="3000"
# Get Gitea admin password from Systems Manager Parameter Store
export GITEA_ADMIN_PASSWORD=$(aws ssm get-parameter --name "/eks-saas-gitops/gitea-admin-password" --with-decryption --query 'Parameter.Value' --output text)
# Display access information for web browser login
echo "=== Gitea Web Interface Access ==="
echo "Public URL (for browser access): $GITEA_PUBLIC_IP"
echo "Username: admin"
echo "Password: $GITEA_ADMIN_PASSWORD"
echo "=================================="
echo ""
echo "Use the PUBLIC URL above to access Gitea from your web browser."
=== Gitea Web Interface Access ===
Public URL (for browser access): http://3.36.97.5:3000
Username: admin
Password: ************
==================================
Use the PUBLIC URL above to access Gitea from your web browser.
저장소 접근 변수 설정
# Extract Gitea configuration from ConfigMap
export GITEA_TOKEN=$(kubectl get configmap saas-infra-outputs -n flux-system -o jsonpath='{.data.gitea_token}')
# Set up the repository paths used throughout the workshop
export REPO_PATH="/home/ec2-user/environment/microservice-repos"
export GITOPS_REPO_PATH="/home/ec2-user/environment/gitops-gitea-repo"
mkdir -p $REPO_PATH
필요한 저장소 복제
cd $REPO_PATH
# Clone the microservice repositories
git clone http://admin:${GITEA_TOKEN}@${GITEA_PRIVATE_IP}:${GITEA_PORT}/admin/producer.git
git clone http://admin:${GITEA_TOKEN}@${GITEA_PRIVATE_IP}:${GITEA_PORT}/admin/consumer.git
git clone http://admin:${GITEA_TOKEN}@${GITEA_PRIVATE_IP}:${GITEA_PORT}/admin/payments.git
tree
├── consumer
│ ├── Dockerfile
│ ├── consumer.py
│ └── requirements.txt
├── payments
│ ├── Dockerfile
│ ├── payments.py
│ └── requirements.txt
└── producer
├── Dockerfile
├── producer.py
└── requirements.txt
# Verify the repositories were cloned successfully
echo "Microservice repositories:"
ls -la $REPO_PATH
echo ""
echo "GitOps repository:"
ls -la $GITOPS_REPO_PATH
참고로, Flux 는 Gitea 저장소 변경 사항을 감시하고 클러스터 상태와 일치시키도록 구성되어 있음.
$ kubectl -n flux-system get gitrepository -o yaml
$ kubectl -n flux-system get gitrepository
NAME URL AGE READY STATUS
flux-system http://10.35.48.130:3000/admin/eks-saas-gitops.git 29m True stored artifact for revision 'refs/heads/main@sha1:68db037f5a5600b72a52742be2115a1618400f60'
terraform-v0-0-1 http://10.35.48.130:3000/admin/eks-saas-gitops.git 29m True stored artifact for revision 'v0.0.1@sha1:2d19a84a88a6ded7c9aa8ac76508452e3f1d48b2'
체크포인트: flux-system과 terraform-v0-0-1 두 GitRepository가 READY=True 상태인지 확인.
실습 1-2: Terraform 및 OpenTofu 컨트롤러
애플리케이션 인프라는 Terraform 모듈을 사용하여 정의 및 관리됨. 저장소 내 모듈 구조는 다음과 같음:
tree /home/ec2-user/environment/gitops-gitea-repo/terraform/modules/ -L 2
gitops-gitea-repo/terraform/modules/
├── flux_cd/ # EKS에 Flux를 설치하는 데 필요한 리소스
├── gitea/ # Gitea 저장소에 필요한 리소스
├── gitops-saas-infra/ # 워크숍 전체 인프라 구성 리소스
└── tenant-apps/ # 테넌트 애플리케이션 전용 인프라 구성 요소
├── data.tf
├── main.tf
├── outputs.tf
├── variables.tf
└── versions.tf
참고: Terraform Aggregated Module, Terraform Module, and Terraform Resource 관계
이 실습에서 핵심은 tenanat-apps 모듈로, 이 모듈 하나로 새 테넌트 온보딩 시 필요한 SQS 큐, DynamoDB 테이블, IRSA(IAM Role for Service Account) 등 모든 인프라를 프로비저닝할 수 있음.
Tofu 컨트롤러 통합: GitOps로 Terraform 실행하기
지금까지는 Terraform을 직접 실행했음. 이제 이 과정을 GitOps로 완전 자동화하는 방법을 살펴볼 예정. 핵심은 Kubernetes Custom Resource Definition을 사용하는 Terraform CRD로, Tofu 컨트롤러가 동작하는 흐름은 다음과 같음
1. Git 저장소에 Terraform CRD 파일 추가 (git push)
│
▼
2. Flux가 변경 감지 → 조정 시작
│
▼
3. tf-controller가 Terraform CRD 감지
│
▼
4. tf-runner Pod 실행 → Git에서 Terraform 모듈 pull
│
▼
5. Terraform apply → SQS, DynamoDB, IRSA 생성
│
▼
6. 실행 상태/플랜을 Kubernetes Secret으로 저장
cd /home/ec2-user/environment/gitops-gitea-repo/
git pull origin main
git status
git add .
git commit -am "Added example terraform CRD for testing"
git push origin main
$ **cd /home/ec2-user/environment/gitops-gitea-repo/**
[ec2-user@ip-10-0-1-124 gitops-gitea-repo]$ **git remote -v**
origin <http://admin:46535182b5f35e5051f5c9724527bb3d28a314df@10.35.48.130:3000/admin/eks-saas-gitops.git> (fetch)
origin <http://admin:46535182b5f35e5051f5c9724527bb3d28a314df@10.35.48.130:3000/admin/eks-saas-gitops.git> (push)
[ec2-user@ip-10-0-1-124 gitops-gitea-repo]$ **git status**
On branch main
Your branch is up to date with 'origin/main'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: application-plane/production/tenants/kustomization.yaml
Untracked files:
(use "git add <file>..." to include in what will be committed)
application-plane/production/tenants/example-tenant-terraform-crd.yaml
helm-charts/helm-tenant-chart/values.yaml
terraform_test.tf
no changes added to commit (use "git add" and/or "git commit -a")
[ec2-user@ip-10-0-1-124 gitops-gitea-repo]$ **cd /home/ec2-user/environment/gitops-gitea-repo/
git pull origin main
git status
git add .
git commit -am "Added example terraform CRD for testing"
git push origin main**
remote: Enumerating objects: 17, done.
remote: Counting objects: 100% (17/17), done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 12 (delta 4), reused 0 (delta 0), pack-reused 0 (from 0)
Unpacking objects: 100% (12/12), 1.08 KiB | 553.00 KiB/s, done.
From <http://10.35.48.130:3000/admin/eks-saas-gitops>
* branch main -> FETCH_HEAD
2d19a84..68db037 main -> origin/main
Updating 2d19a84..68db037
Fast-forward
application-plane/production/pooled-envs/pool-1.yaml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
On branch main
Your branch is up to date with 'origin/main'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: application-plane/production/tenants/kustomization.yaml
Untracked files:
(use "git add <file>..." to include in what will be committed)
application-plane/production/tenants/example-tenant-terraform-crd.yaml
helm-charts/helm-tenant-chart/values.yaml
terraform_test.tf
no changes added to commit (use "git add" and/or "git commit -a")
[main 13b7f72] Added example terraform CRD for testing
Committer: EC2 Default User <ec2-user@ip-10-0-1-124.us-west-2.compute.internal>
Your name and email address were configured automatically based
on your username and hostname. Please check that they are accurate.
You can suppress this message by setting them explicitly. Run the
following command and follow the instructions in your editor to edit
your configuration file:
git config --global --edit
After doing this, you may fix the identity used for this commit with:
git commit --amend --reset-author
4 files changed, 132 insertions(+), 1 deletion(-)
create mode 100644 application-plane/production/tenants/example-tenant-terraform-crd.yaml
create mode 100644 helm-charts/helm-tenant-chart/values.yaml
create mode 100644 terraform_test.tf
Enumerating objects: 18, done.
Counting objects: 100% (18/18), done.
Delta compression using up to 2 threads
Compressing objects: 100% (10/10), done.
Writing objects: 100% (11/11), 2.05 KiB | 1.02 MiB/s, done.
Total 11 (delta 3), reused 0 (delta 0), pack-reused 0 (from 0)
remote: . Processing 1 references
remote: Processed 1 references in total
To <http://10.35.48.130:3000/admin/eks-saas-gitops.git>
68db037..13b7f72 main -> main
cd /home/ec2-user/environment/gitops-gitea-repo/
git pull origin main
git add .
git commit -m "Removed Terraform CRD and reference from kustomization.yaml"
git push origin main
tf-runner Pod의 terraform destroy 로그를 확인
$ **flux reconcile source git flux-system**
► annotating GitRepository flux-system in flux-system namespace
✔ GitRepository annotated
◎ waiting for GitRepository reconciliation
✔ fetched revision refs/heads/main@sha1:e8be2a502eed65166afd87b715811938e194cce6
[ec2-user@ip-10-0-1-124 gitops-gitea-repo]$ kubectl logs po/example-tenant-tf-runner -nflux-system -f
2026/04/13 19:52:43 Starting the runner... version sha
{"level":"info","ts":"2026-04-13T19:52:57.853Z","logger":"runner.terraform","msg":"preparing for Upload and Extraction","instance-id":""}
{"level":"info","ts":"2026-04-13T19:52:57.878Z","logger":"runner.terraform","msg":"write backend config","instance-id":"","path":"/tmp/flux-system-example-tenant/terraform/modules/tenant-apps","config":"backend_override.tf"}
{"level":"info","ts":"2026-04-13T19:52:57.881Z","logger":"runner.terraform","msg":"write config to file","instance-id":"","filePath":"/tmp/flux-system-example-tenant/terraform/modules/tenant-apps/backend_override.tf"}
{"level":"info","ts":"2026-04-13T19:52:57.883Z","logger":"runner.terraform","msg":"looking for path","instance-id":"","file":"terraform"}
{"level":"info","ts":"2026-04-13T19:52:57.886Z","logger":"runner.terraform","msg":"creating new terraform","instance-id":"e965d26a-8ece-4644-b678-5fb0c661998f","workingDir":"/tmp/flux-system-example-tenant/terraform/modules/tenant-apps","execPath":"/usr/local/bin/terraform"}
{"level":"info","ts":"2026-04-13T19:52:57.902Z","logger":"runner.terraform","msg":"setting envvars","instance-id":"e965d26a-8ece-4644-b678-5fb0c661998f"}
{"level":"info","ts":"2026-04-13T19:52:57.903Z","logger":"runner.terraform","msg":"getting envvars from os environments","instance-id":"e965d26a-8ece-4644-b678-5fb0c661998f"}
{"level":"info","ts":"2026-04-13T19:52:57.906Z","logger":"runner.terraform","msg":"setting up the input variables","instance-id":"e965d26a-8ece-4644-b678-5fb0c661998f"}
{"level":"info","ts":"2026-04-13T19:52:57.907Z","logger":"runner.terraform","msg":"mapping the Spec.Values","instance-id":"e965d26a-8ece-4644-b678-5fb0c661998f"}
{"level":"info","ts":"2026-04-13T19:52:57.909Z","logger":"runner.terraform","msg":"mapping the Spec.Vars","instance-id":"e965d26a-8ece-4644-b678-5fb0c661998f"}
{"level":"info","ts":"2026-04-13T19:52:57.909Z","logger":"runner.terraform","msg":"mapping the Spec.VarsFrom","instance-id":"e965d26a-8ece-4644-b678-5fb0c661998f"}
{"level":"info","ts":"2026-04-13T19:52:57.912Z","logger":"runner.terraform","msg":"generating the template founds"}
{"level":"info","ts":"2026-04-13T19:52:57.912Z","logger":"runner.terraform","msg":"main.tf.tpl not found, skipping"}
{"level":"info","ts":"2026-04-13T19:52:57.915Z","logger":"runner.terraform","msg":"initializing","instance-id":"e965d26a-8ece-4644-b678-5fb0c661998f"}
{"level":"info","ts":"2026-04-13T19:52:57.915Z","logger":"runner.terraform","msg":"mapping the Spec.BackendConfigsFrom","instance-id":"e965d26a-8ece-4644-b678-5fb0c661998f"}
{"level":"info","ts":"2026-04-13T19:53:13.824Z","logger":"runner.terraform","msg":"workspace select"}
{"level":"info","ts":"2026-04-13T19:53:13.835Z","logger":"runner.terraform","msg":"creating a plan","instance-id":"e965d26a-8ece-4644-b678-5fb0c661998f"}
{"level":"info","ts":"2026-04-13T19:53:41.248Z","logger":"runner.terraform","msg":"save the plan","instance-id":"e965d26a-8ece-4644-b678-5fb0c661998f"}
{"level":"info","ts":"2026-04-13T19:53:41.295Z","logger":"runner.terraform","msg":"loading plan from secret","instance-id":"e965d26a-8ece-4644-b678-5fb0c661998f"}
{"level":"info","ts":"2026-04-13T19:53:41.324Z","logger":"runner.terraform","msg":"running apply","instance-id":"e965d26a-8ece-4644-b678-5fb0c661998f"}
module.consumer_irsa_role[0].aws_iam_role_policy_attachment.this["policy"]: Destroying... [id=consumer-role-example-tenant/arn:aws:iam::656796372676:policy/consumer-policy-example-tenant]
module.producer_irsa_role[0].aws_iam_role_policy_attachment.this["policy"]: Destroying... [id=producer-role-example-tenant/arn:aws:iam::656796372676:policy/producer-policy-example-tenant]
module.producer_irsa_role[0].aws_iam_role_policy_attachment.this["policy"]: Destruction complete after 1s
aws_iam_policy.producer-iampolicy[0]: Destroying... [id=arn:aws:iam::656796372676:policy/producer-policy-example-tenant]
module.producer_irsa_role[0].aws_iam_role.this[0]: Destroying... [id=producer-role-example-tenant]
module.consumer_irsa_role[0].aws_iam_role_policy_attachment.this["policy"]: Destruction complete after 1s
aws_iam_policy.consumer-iampolicy[0]: Destroying... [id=arn:aws:iam::656796372676:policy/consumer-policy-example-tenant]
module.consumer_irsa_role[0].aws_iam_role.this[0]: Destroying... [id=consumer-role-example-tenant]
aws_iam_policy.producer-iampolicy[0]: Destruction complete after 0s
aws_iam_policy.consumer-iampolicy[0]: Destruction complete after 0s
aws_ssm_parameter.dedicated_consumer_ddb[0]: Destroying... [id=/example-tenant/consumer_ddb]
aws_ssm_parameter.dedicated_consumer_sqs[0]: Destroying... [id=/example-tenant/consumer_sqs]
module.consumer_irsa_role[0].aws_iam_role.this[0]: Destruction complete after 0s
aws_ssm_parameter.dedicated_consumer_sqs[0]: Destruction complete after 0s
aws_sqs_queue.consumer_sqs[0]: Destroying... [id=https://sqs.us-west-2.amazonaws.com/656796372676/consumer-example-tenant-s2v]
aws_ssm_parameter.dedicated_consumer_ddb[0]: Destruction complete after 0s
aws_dynamodb_table.consumer_ddb[0]: Destroying... [id=consumer-example-tenant-s2v]
module.producer_irsa_role[0].aws_iam_role.this[0]: Destruction complete after 0s
aws_dynamodb_table.consumer_ddb[0]: Destruction complete after 6s
aws_sqs_queue.consumer_sqs[0]: Still destroying... [id=https://sqs.us-west-2.amazonaws.com/656796372676/consumer-example-tenant-s2v, 10s elapsed]
aws_sqs_queue.consumer_sqs[0]: Still destroying... [id=https://sqs.us-west-2.amazonaws.com/656796372676/consumer-example-tenant-s2v, 20s elapsed]
aws_sqs_queue.consumer_sqs[0]: Still destroying... [id=https://sqs.us-west-2.amazonaws.com/656796372676/consumer-example-tenant-s2v, 30s elapsed]
aws_sqs_queue.consumer_sqs[0]: Still destroying... [id=https://sqs.us-west-2.amazonaws.com/656796372676/consumer-example-tenant-s2v, 40s elapsed]
aws_sqs_queue.consumer_sqs[0]: Still destroying... [id=https://sqs.us-west-2.amazonaws.com/656796372676/consumer-example-tenant-s2v, 50s elapsed]
aws_sqs_queue.consumer_sqs[0]: Still destroying... [id=https://sqs.us-west-2.amazonaws.com/656796372676/consumer-example-tenant-s2v, 1m0s elapsed]
aws_sqs_queue.consumer_sqs[0]: Still destroying... [id=https://sqs.us-west-2.amazonaws.com/656796372676/consumer-example-tenant-s2v, 1m10s elapsed]
aws_sqs_queue.consumer_sqs[0]: Still destroying... [id=https://sqs.us-west-2.amazonaws.com/656796372676/consumer-example-tenant-s2v, 1m20s elapsed]
aws_sqs_queue.consumer_sqs[0]: Still destroying... [id=https://sqs.us-west-2.amazonaws.com/656796372676/consumer-example-tenant-s2v, 1m30s elapsed]
aws_sqs_queue.consumer_sqs[0]: Still destroying... [id=https://sqs.us-west-2.amazonaws.com/656796372676/consumer-example-tenant-s2v, 1m40s elapsed]
aws_sqs_queue.consumer_sqs[0]: Still destroying... [id=https://sqs.us-west-2.amazonaws.com/656796372676/consumer-example-tenant-s2v, 1m50s elapsed]
aws_sqs_queue.consumer_sqs[0]: Still destroying... [id=https://sqs.us-west-2.amazonaws.com/656796372676/consumer-example-tenant-s2v, 2m0s elapsed]
aws_sqs_queue.consumer_sqs[0]: Destruction complete after 2m9s
random_string.random_suffix: Destroying... [id=s2v]
random_string.random_suffix: Destruction complete after 0s
Apply complete! Resources: 0 added, 0 changed, 11 destroyed.
{"level":"info","ts":"2026-04-13T19:56:02.482Z","logger":"runner.terraform","msg":"finalize the output secrets","instance-id":"e965d26a-8ece-4644-b678-5fb0c661998f"}
{"level":"info","ts":"2026-04-13T19:56:02.528Z","logger":"runner.terraform","msg":"cleanup TmpDir","instance-id":"e965d26a-8ece-4644-b678-5fb0c661998f","tmpDir":"/tmp/flux-system-example-tenant"}
{"level":"info","ts":"2026-04-13T19:56:02.609Z","logger":"runner.terraform","msg":"cleanup TmpDir","instance-id":"e965d26a-8ece-4644-b678-5fb0c661998f","tmpDir":""}