getting start
3 devops people collaboration
we are team of 3 devops works on infra we have aws cloud github as vcs and terraform cloud for state dev and prod env
how should be flow you can think about it
Got it 👍 thanks for the context. Since you’re 3 DevOps engineers, managing AWS infra, with GitHub as VCS and Terraform Cloud as remote backend, here’s a solid workflow design I’d suggest:
🚀 Infra Workflow (Team of 3)
1. Branching Strategy
main → Production infra
develop → Development infra
feature/* → Individual changes
This keeps prod changes isolated and allows safe dev testing.
2. GitHub Flow
DevOps engineer creates a feature branch → commits Terraform code.
Open a Pull Request (PR) into
develop(for dev env) ormain(for prod).Peer review required → at least 1 other team member must approve.
✅ Ensures code-level approval before Terraform runs.
3. Terraform Cloud Integration
GitHub PR → triggers Terraform Cloud plan automatically.
Terraform Cloud:
Runs
terraform planusing remote execution.Stores state securely.
Shows the plan in the Terraform Cloud UI (or posts back to GitHub as status check).
4. Approval & Apply
If PR approved and merged:
For develop branch:
Terraform Cloud runs
terraform apply(auto-apply can be ON for dev).
For main branch:
Terraform Cloud runs plan, but requires manual “Confirm & Apply” in UI (no auto-apply for prod).
✅ This gives you two gates for prod:
PR Approval ✅
Terraform Cloud Apply Approval ✅
5. Environment Separation
Options:
Separate workspaces in Terraform Cloud (
dev,prod).Both point to same repo, different branches (
develop,main).
Or Workspaces per module (if infra split, e.g.,
networking-dev,networking-prod).
For a small team, workspace per env is enough.
6. Secrets Management
AWS creds (Access Key + Secret) are stored in Terraform Cloud variables (scoped per workspace).
GitHub Actions doesn’t need AWS creds (since Terraform Cloud runs execution).
🔹 Example Flow in Action
You want to add an S3 bucket → create
feature/add-s3branch.Push → PR → GitHub triggers Terraform Cloud plan in
devworkspace.PR reviewer checks plan → approves → merge to
develop.Terraform Cloud auto-applies to
dev.After testing, create PR from
develop→main.Terraform Cloud runs plan for
prod, team reviews plan, someone clicks Confirm & Apply.Prod updated safely 🚀.
✅ This way:
Code review is in GitHub.
Execution, state, approval gates are in Terraform Cloud.
Small team of 3 can safely manage both dev and prod infra without stepping on each other.
Do you want me to draw you a diagram of this flow (GitHub + Terraform Cloud + AWS) so it’s super clear?
2
suppose your devops engineer create eks cluster from module in dev environment not in prod senion devops review code and accept pull request and tell me remaining flow i want end to end flow eks from this moudle code (file) dev features branch how he creates, write code, commands both terrform and git and push check plan so how it works in real time you can take time and think
Awesome—here’s a clean, end-to-end real-world flow for creating an EKS cluster in dev from a module, using GitHub (PRs) + Terraform Cloud (state & runs). I’ll show:
Repo layout
Minimal module-based code
Exact git + terraform commands
What happens at each stage (PR plan → review → apply → verify)
How promotion to prod works later
1) One-time setup (Terraform Cloud)
Create two TFC workspaces and connect your GitHub repo via VCS:
Workspace:
dev-eks→ Tracks branch:develop→ Auto-apply: ONWorkspace:
prod-eks→ Tracks branch:main→ Auto-apply: OFF (manual “Confirm & Apply”)
In each workspace:
Variables → Environment variables (sensitive):
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY, (optional)AWS_SESSION_TOKEN
Variables → Terraform: anything env-specific you want (or use
*.tfvarsin repo)Make sure “Speculative Plans on Pull Requests” is enabled (default for VCS workspaces). This gives you PR-time plans without applying.
2) Repo layout (simple & clear)
infra/
├─ modules/
│ └─ eks/
│ ├─ main.tf
│ ├─ variables.tf
│ └─ outputs.tf
├─ environments/
│ ├─ dev/
│ │ ├─ main.tf
│ │ ├─ versions.tf
│ │ ├─ providers.tf
│ │ ├─ variables.tf
│ │ └─ dev.tfvars
│ └─ prod/
│ ├─ main.tf
│ ├─ versions.tf
│ ├─ providers.tf
│ ├─ variables.tf
│ └─ prod.tfvars
└─ .github/workflows/terraform-checks.yml (optional: lint/validate only)Tip: With VCS-driven TFC runs, do not add a
cloud {}block. TFC discovers the workspace via the VCS connection + tracked branch.
3) Minimal EKS module (you can pin your favorite)
infra/modules/eks/main.tf
terraform {
required_version = ">= 1.7.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
# Simple VPC for EKS (dev only; in prod you may reuse shared VPC)
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.0"
name = var.name
cidr = var.vpc_cidr
azs = var.azs
public_subnets = var.public_subnets
private_subnets = var.private_subnets
enable_nat_gateway = true
single_nat_gateway = true
enable_dns_hostnames = true
enable_dns_support = true
tags = var.tags
}
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "~> 20.0"
cluster_name = var.cluster_name
cluster_version = var.cluster_version
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnets
cluster_endpoint_public_access = true
enable_irsa = true
eks_managed_node_groups = {
default = {
instance_types = var.node_instance_types
desired_size = var.node_desired
min_size = var.node_min
max_size = var.node_max
}
}
tags = var.tags
}infra/modules/eks/variables.tf
variable "name" { type = string }
variable "cluster_name" { type = string }
variable "cluster_version" { type = string }
variable "vpc_cidr" { type = string }
variable "azs" { type = list(string) }
variable "public_subnets" { type = list(string) }
variable "private_subnets" { type = list(string) }
variable "node_instance_types"{ type = list(string) }
variable "node_desired" { type = number }
variable "node_min" { type = number }
variable "node_max" { type = number }
variable "tags" { type = map(string), default = {} }infra/modules/eks/outputs.tf
output "cluster_name" { value = module.eks.cluster_name }
output "cluster_endpoint"{ value = module.eks.cluster_endpoint }
output "oidc_provider_arn"{ value = module.eks.oidc_provider_arn }
output "cluster_security_group_id" { value = module.eks.cluster_security_group_id }4) Dev environment wiring
infra/environments/dev/versions.tf
terraform {
required_version = ">= 1.7.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
# No cloud{} block here for VCS-driven TFC runs
}infra/environments/dev/providers.tf
provider "aws" {
region = var.region
}infra/environments/dev/variables.tf
variable "region" { type = string }
variable "cluster_version" { type = string }
variable "tags" { type = map(string) }infra/environments/dev/main.tf
module "eks_stack" {
source = "../../modules/eks"
name = "dev-eks"
cluster_name = "dev-eks"
cluster_version = var.cluster_version
vpc_cidr = "10.20.0.0/16"
azs = ["ap-south-1a","ap-south-1b","ap-south-1c"]
public_subnets = ["10.20.0.0/24","10.20.1.0/24","10.20.2.0/24"]
private_subnets = ["10.20.10.0/24","10.20.11.0/24","10.20.12.0/24"]
node_instance_types = ["t3.medium"]
node_desired = 2
node_min = 2
node_max = 4
tags = var.tags
}infra/environments/dev/dev.tfvars
region = "ap-south-1"
cluster_version = "1.29"
tags = {
env = "dev"
app = "platform"
owner = "devops"
}For prod, mirror this structure under
environments/prod/with your prod CIDR, names, sizes, etc. Theprodworkspace will trackmain.
5) Day-to-day flow (engineer creates dev EKS in a feature branch)
A) Create feature branch & write code (locally)
cd infra
git checkout develop
git pull origin develop
git checkout -b feature/dev-eks
# create/edit the files shown above
git add .
git commit -m "feat(eks): add dev EKS via module"(Optional quick local checks; these don’t run a real plan because TFC will handle that)
terraform -chdir=environments/dev fmt -recursive
terraform -chdir=environments/dev init -backend=false
terraform -chdir=environments/dev validatePush:
git push -u origin feature/dev-eksB) Open PR → feature/dev-eks → into develop
feature/dev-eks → into developGitHub shows your diff.
Terraform Cloud auto-triggers a Speculative Plan (read-only, no apply) for the
dev-eksworkspace, because that workspace tracks thedevelopbranch and TFC is VCS-connected.The plan appears in TFC and usually posts a status back to the PR.
C) Senior DevOps review
Reviews code in PR + the TFC speculative plan (no resources are created yet).
May request changes or approve.
D) Merge PR → develop
developAfter merge, TFC runs a real plan in
dev-eksand, since Auto-apply = ON, it will apply automatically.You can watch the run live in TFC (locks state, shows events, outputs, etc.).
6) Verify the new dev cluster (post-apply)
From your workstation (with AWS CLI + kubectl):
# Set kubeconfig for the new cluster
aws eks update-kubeconfig --name dev-eks --region ap-south-1
# Confirm nodes are ready
kubectl get nodes
# Check cluster info
kubectl cluster-infoIf you need IRSA confirmation:
# OIDC provider should exist (module enable_irsa = true)
aws iam list-open-id-connect-providers | grep eksRBAC/Access: if you need to grant a team IAM role admin access, configure the
aws-authmapping (the eks module supports managing it). Keep that as a follow-up commit if required.
7) Optional CI in GitHub (lint/validate before TFC plan)
.github/workflows/terraform-checks.yml
name: Terraform Checks
on:
pull_request:
branches: [ develop, main ]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
with:
terraform_version: 1.9.x
- name: fmt
run: terraform -chdir=infra/environments/dev fmt -recursive -check
- name: init (no backend)
run: terraform -chdir=infra/environments/dev init -backend=false
- name: validate
run: terraform -chdir=infra/environments/dev validateThis catches format/validate issues before reviewers look and before TFC’s speculative plan runs.
8) Promote to prod later (release PR)
When dev is tested, raise a PR: develop → main.
TFC triggers a Speculative Plan for
prod-ekson the PR.Senior reviews code + plan.
Merge → TFC runs real plan in
prod-eks.Since Auto-apply is OFF for prod, someone must click “Confirm & Apply” in TFC.
Verify prod cluster similarly:
aws eks update-kubeconfig --name prod-eks --region ap-south-1 kubectl get nodes
9) Rollback / changes
To rollback, revert the PR (or commit) → TFC plans & applies the inverse change.
To make changes, repeat the same feature → PR to develop → merge → auto-apply dev flow.
TL;DR flow you’ll actually follow
git checkout -b feature/dev-eks→ add module code → commit → push.Open PR → GitHub shows diff; TFC runs Speculative Plan.
Senior reviews (code + plan) → approves → merge to
develop.TFC auto-applies to
dev-eks.aws eks update-kubeconfig ...→kubectl get nodesto verify.When ready, PR
develop → main→ TFC plan forprod-eks→ Confirm & Apply in TFC.
If you want, I can tailor the dev.tfvars (regions, sizes) to your exact AWS account setup, or add aws-auth role mappings in the module so your team gets cluster-admin out of the box.
3
as dev workspace run plan when there is push to develop branch right or run plan on pull request? and also i will create workspace per env not module
Great clarifications 👍
Let’s carefully separate what happens in Terraform Cloud (TFC) workspaces when you connect your GitHub repo:
🔹 When does Terraform Cloud run a plan?
Pull Request (PR) opened / updated
If the workspace is VCS-connected, TFC will run a Speculative Plan.
This is a read-only plan (no apply).
Purpose: review the impact of the change before merging.
Shown in TFC UI and (if enabled) as a GitHub status check on the PR.
Push to branch tracked by workspace
Example:
devworkspace tracksdevelopbranch.When PR is merged into
develop, TFC will run a real plan indevworkspace.Depending on workspace settings:
Auto-apply = ON → TFC runs plan + immediately applies.
Auto-apply = OFF → TFC runs plan, waits for manual "Confirm & Apply".
So:
On PR → Speculative Plan (preview only).
On merge to tracked branch → Real Plan (+ apply if enabled).
🔹 Workspace per environment (your choice)
✅ That’s the right approach for a team of 3 with dev/prod.
Workspace:
dev→ connected todevelopbranch.Workspace:
prod→ connected tomainbranch.
Each workspace has:
Its own state file (no risk of mixing dev/prod infra).
Its own variables (AWS creds, region, cluster size, etc.).
Its own apply behavior (auto in dev, manual approval in prod).
🔹 Example Flow With Your Setup
DevOps creates branch
feature/eks.Opens PR →
feature/eks → develop.TFC (
devworkspace) runs speculative plan (read-only).Senior reviews both code + plan.
Merge PR →
develop.TFC (
devworkspace) runs real plan + auto-apply.EKS cluster gets created in dev.
After testing, PR →
develop → main.TFC (
prodworkspace) runs speculative plan.Senior reviews.
Merge PR →
main.TFC (
prodworkspace) runs real plan.Since auto-apply is OFF, someone must click Confirm & Apply.
👉 So to answer directly:
Plan runs on both PR and push.
PR = Speculative (no changes).
Push to tracked branch = Real plan (with apply if enabled).
Do you want me to sketch a flow diagram (GitHub PR → TFC dev/prod workspaces → AWS) so your team can use it as a quick reference?
my thought is as u say merge develop to main after testing but production environment have some extra things so why merge to main develop is for dev environment workload/infra and prod/main for production workload/infra
Exactly 👌 you’ve spotted the key nuance:
dev ≠ prod. The two environments often need different infrastructure (extra monitoring, bigger instance sizes, WAF, backup policies, etc.), not just the same code promoted across branches.
Let me break it down clearly:
🔹 Why not blindly merge develop → main?
develop → main?Dev environment is meant for experimenting and testing.
Prod environment usually has:
Extra services (e.g. GuardDuty, WAF, CloudTrail, backups).
Different instance/node sizes.
Different scaling policies.
Tighter IAM/RBAC.
So if you just merge develop into main, you risk pushing dev-only stuff into prod.
🔹 Better Strategy: Keep develop and main as environment-specific branches
develop and main as environment-specific branchesdevelop→ changes apply only to dev workspace.main→ changes apply only to prod workspace.Both share modules from
/modules/…(so logic is consistent).Each env (
/environments/dev,/environments/prod) defines its own variables, tfvars, and any extra resources.
This way:
Shared building blocks live in
modules/.Env differences live in
environments/devvsenvironments/prod.
🔹 Practical Workflow (team of 3)
Engineer wants new feature (say, an EKS module update).
Creates
feature/eks-updatebranch from develop.Works under
/environments/dev/.Opens PR →
develop.TFC runs speculative plan in
dev.Reviewer approves → merge → TFC auto-applies → Dev infra updated.
After testing in dev, engineer prepares prod rollout.
Creates new branch from main:
feature/prod-eks-update.Brings the same module change, but adjusts prod-specific vars in
/environments/prod/.Opens PR →
main.TFC runs speculative plan in
prod.Reviewer checks carefully → merge.
TFC runs real plan in
prod.Someone clicks Confirm & Apply → Prod infra updated.
🔹 Benefits of this model
No forced merging dev → prod. Each env is managed separately.
Flexibility: prod can have extra resources not in dev.
Safety: only explicitly prepared PRs touch prod.
Consistency: shared modules keep dev & prod logic aligned where it makes sense.
👉 So, in your case:
Use
developstrictly for dev infra.Use
mainstrictly for prod infra.Promote module changes, not environment branches.
Would you like me to draft a concrete Git workflow example (with exact git commands + PR targets) that shows how your 3 DevOps engineers should handle a real feature (e.g., “add EKS logging addon”) in both dev and prod using this setup?
Last updated