Opinionated scaffolding to provision Azure Kubernetes Service (AKS) with Terraform, network and monitoring primitives, and bootstrap Argo CD for GitOps. Comes with CI workflows (lint/scan/plan, gated apply, safe destroy) and local-demo scripts.
- Azure Resource Group (randomized name for uniqueness)
- Virtual Network with an AKS subnet
- AKS cluster with system-assigned managed identity
- Log Analytics Workspace wired to AKS (Azure Monitor for containers)
- Argo CD installed via the Helm provider in the cluster (Service type LoadBalancer)
- Clean separation via reusable Terraform modules (
vnet,aks,argocd-bootstrap) - Secure-by-default CI using GitHub OIDC to log in to Azure (no long‑lived secrets)
- Ready‑to‑run local demo using
k3dthat mirrors the cloud bootstrap flow
.
├── backend.tf # Partial backend; pass config at init time
├── main.tf # Resource group, LAW, and module wiring
├── variables.tf # Root input variables
├── versions.tf # Provider versions and azurerm provider
├── outputs.tf # Surface AKS name and Argo CD details
├── modules/
│ ├── aks/ # AKS module (node pool, network profile, LAW)
│ ├── vnet/ # VNet + Subnets module
│ └── argocd-bootstrap/ # Helm‑based Argo CD install + outputs
├── scripts/
│ ├── run-terraform.sh # init → plan → apply wrapper
│ ├── terraform-destroy.sh # safe destroy wrapper
│ ├── security-scan.sh # tflint + trivy config scan
│ ├── setup-local-dev.sh # local k3d demo (Argo CD included)
│ └── teardown-local.sh # delete local k3d cluster
├── envs/
│ ├── backend.tfvars # Convenience file for backend init
│ └── production.tfvars # Example non‑sensitive vars for prod
├── examples/local/README.md # Local demo notes
└── .github/workflows/ # CI: plan/scan, gated apply, manual destroy
- Terraform ≥ 1.9
- Azure subscription + permissions to create RG/AKS/VNet/LAW
- For local runs:
az loginto the target subscription - Remote state: Azure Storage Account + Blob Container
Note: This repo uses a partial
azurermbackend config (seebackend.tf). Pass the actual backend settings atterraform inittime.
Provision a local cluster with k3d and install Argo CD:
./scripts/setup-local-dev.sh demo
# Access Argo CD locally (in a separate terminal)
kubectl -n argocd port-forward svc/argocd-server 8080:443
# Initial credentials
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath='{.data.password}' | base64 -d; echo
# Open http://localhost:8080 (user: admin)
From the Argo CD UI, point an Application at your GitOps repo (default in variables.tf points to eknathdj/project-gitops).
- Create remote state resources (one‑time):
RESOURCE_GROUP=rg-tfstate
STORAGE_ACCOUNT=mystatestorageacct # must be globally unique
CONTAINER=tfstate
az group create -n "$RESOURCE_GROUP" -l centralindia
az storage account create -g "$RESOURCE_GROUP" -n "$STORAGE_ACCOUNT" -l centralindia --sku Standard_LRS
az storage container create --account-name "$STORAGE_ACCOUNT" -n "$CONTAINER"- Initialize Terraform with backend settings:
terraform init \
-backend-config="resource_group_name=rg-tfstate" \
-backend-config="storage_account_name=mystatestorageacct" \
-backend-config="container_name=tfstate" \
-backend-config="key=prod.terraform.tfstate"Alternatively, use the provided convenience file:
terraform init -backend-config=envs/backend.tfvars- Plan and apply (using the provided example vars):
./scripts/run-terraform.sh envs/production.tfvars
# or, raw Terraform
terraform plan -var-file=envs/production.tfvars -out=tfplan
terraform apply tfplanThe AKS resource group name is randomized via
random_petto avoid collisions.
After apply, fetch the outputs:
terraform output argocd_server_url
terraform output -raw argocd_initial_password | base64 -dIf the LoadBalancer IP is still pending right after the Helm install, wait a minute and re‑run the outputs.
Root inputs you are most likely to change (see variables.tf):
location: Azure region (e.g.,Central India)cluster_name: Base name used in some resource namestags: Map of tags applied to resources
AKS module overrides (optional, see modules/aks/variables.tf):
default_node_pool_vm_size(defaultStandard_D2s_v3)default_node_pool_count(default1, autoscaling enabled by default)network_plugin(defaultazure) andnetwork_policy(defaultcalico)
Networking (see modules/vnet/variables.tf):
vnet_address_space(default10.0.0.0/16)subnetsmap; by default defines anakssubnet10.0.1.0/24
Argo CD bootstrap (see modules/argocd-bootstrap):
- Installs chart
argo/argo-cdvia Helm provider - Exposes server as
LoadBalancerfor simplicity - Outputs
argocd_server_urlandargocd_initial_password
Note: The
terraform.tfvars.exampleincludes additional placeholders (e.g., subscription IDs) for CI scenarios; not all are used by the root module in local runs.
Workflows in .github/workflows/:
terraform-ci.yml— Lint (tflint), config scan (trivy), init, validate, planterraform-apply.yml— Gated apply to theproductionenvironment after planterraform-destroy.yml— Manual, protected destroy with a confirmation phrase
Authentication uses GitHub OIDC via azure/login. Configure a federated identity in Azure AD and set these repository secrets:
AZURE_CLIENT_IDAZURE_TENANT_IDAZURE_SUBSCRIPTION_ID- Backend config secrets (match your state):
AZURE_BACKEND_RESOURCE_GROUP_NAMEAZURE_BACKEND_STORAGE_ACCOUNT_NAMEAZURE_BACKEND_CONTAINER_NAMEAZURE_BACKEND_KEY
The workflows currently reference
production.tfvarsandci.tfvarsat the repo root. Provide those files or adjust the paths toenvs/production.tfvarsas preferred.
- Argo CD URL is empty: the LoadBalancer IP can take time; re‑run
terraform outputor checkkubectl -n argocd get svc argocd-server. terraform initfails: ensure backend settings are correct and the storage account/container exist.- AKS provider auth errors: confirm you are logged in with
az loginand the subscription is selected (az account show).
To destroy cloud resources safely:
./scripts/terraform-destroy.sh envs/production.tfvars
For the local demo cluster:
./scripts/teardown-local.sh demo
This project is licensed under the terms of the LICENSE file.