Automate your Infrastructure on AWS using Terraform Controller and FluxCD

Introduction In the age of cloud-native operations, infrastructure automation is no longer optional—it's essential. While tools like Terraform have long been the standard for Infrastructure as Code (IaC), integrating them into a GitOps workflow brings unprecedented control, traceability, and scalability. In this blog, we'll explore how to automate your AWS infrastructure using Terraform Controller (from the Crossplane ecosystem) and FluxCD, leveraging Git as the single source of truth. Why Terraform Controller + FluxCD? Terraform Controller: A Kubernetes controller that allows you to manage Terraform executions through Custom Resource Definitions (CRDs). FluxCD: A GitOps toolkit that automates Kubernetes deployments by syncing Git repositories with clusters. GitOps Benefits: Version control, audit trails, CI/CD integration, and reduced configuration drift. Prerequisites Before you begin, ensure you have the following: A Kubernetes cluster (EKS,AKS,GKS,On-Prem or local like KinD) AWS credentials with necessary IAM permissions kubectl, flux, and terraform controller installed A Git repository for storing Terraform manifests Step-by-Step Guide Install Kind Cluster #cat config.yaml kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 nodes: - role: control-plane - role: worker - role: worker #kind create cluster --config=config.yaml Install flux cli #brew install fluxcd/tap/flux BootStrap fluxcd to install flux-system with gitrepo infra-demo #flux bootstrap gitlab --token-auth --owner=infra-demo2 --repository=flux-system --branch=main --path=clusters/dev A namespace name flux-system will be created with required components #kubectl get ns #kubectl get all -n flux-system Lets Install Terraform Controller #helm repo add tofu-controller https://flux-iac.github.io/tofu-controller #helm upgrade -i tofu-controller tofu-controller/tf-controller \ --namespace flux-system Lets Verify Controller in flux-system #kubectl get pods -n flux-system | grep tf Create infrastructure project inside infra-demo repo and map Source list and Kustamization to infra-demo repository to managed by Flux-cd #cat infra-demo.yaml apiVersion: source.toolkit.fluxcd.io/v1beta1 kind: GitRepository metadata: name: vpc-creation namespace: flux-system spec: interval: 30s url: https://gitlab.com/infra-demo2/infrastructure.git ref: branch: main secretRef: name: flux-system --- apiVersion: kustomize.toolkit.fluxcd.io/v1beta2 kind: Kustomization metadata: name: vpc-creation namespace: flux-system spec: prune: true interval: 2m path: ./ sourceRef: kind: GitRepository name: vpc-creation timeout: 3m healthChecks: - apiVersion: infra.contrib.fluxcd.io/v1alpha1 kind: Terraform name: vpc-creation namespace: flux-system Lets Create terraform Code in Infrastructure Project to deploy VPC in AWS Account #cat main.tf resource "aws_vpc" "main" { cidr_block = var.vpc_cidr enable_dns_support = true enable_dns_hostnames = true tags = { Name = "${var.project}-vpc" } } resource "aws_subnet" "public" { count = length(var.public_subnets) vpc_id = aws_vpc.main.id cidr_block = var.public_subnets[count.index] availability_zone = element(var.availability_zones, count.index) map_public_ip_on_launch = true tags = { Name = "${var.project}-public-subnet-${count.index + 1}" } } resource "aws_internet_gateway" "igw" { vpc_id = aws_vpc.main.id tags = { Name = "${var.project}-igw" } } resource "aws_route_table" "public" { vpc_id = aws_vpc.main.id tags = { Name = "${var.project}-public-rt" } } resource "aws_route" "internet_access" { route_table_id = aws_route_table.public.id destination_cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.igw.id } resource "aws_route_table_association" "public_assoc" { count = length(var.public_subnets) subnet_id = aws_subnet.public[count.index].id route_table_id = aws_route_table.public.id } #cat outputs.tf output "vpc_id" { value = aws_vpc.main.id } output "public_subnet_ids" { value = aws_subnet.public[*].id } #cat variables.tf variable "aws_region" { default = "us-east-1" } variable "project" { default = "demo" } variable "vpc_cidr" { default = "10.0.0.0/16" } variable "public_subnets" { default = ["10.0.1.0/24", "10.0.2.0/24"] } variable "availability_zones" { default = ["us-east-1a", "us-east-1b"] } Note: Not Recommended #cat provider.tf provider "aws" { region = var.aws_region access_key = "XXXXXXXXXX" secret_key = "YYYYYYYYYYYYYY" } Now Create Terraform Controller to Deploy Terraform Code #cat terraform.yaml apiVersion: infra.contrib.fluxcd.io/v1alpha1 kind: Terraform metadata: name: vpc-creation namespace: flux-system spec: interv

May 11, 2025 - 12:27
 0
Automate your Infrastructure on AWS using Terraform Controller and FluxCD

Introduction

In the age of cloud-native operations, infrastructure automation is no longer optional—it's essential. While tools like Terraform have long been the standard for Infrastructure as Code (IaC), integrating them into a GitOps workflow brings unprecedented control, traceability, and scalability. In this blog, we'll explore how to automate your AWS infrastructure using Terraform Controller (from the Crossplane ecosystem) and FluxCD, leveraging Git as the single source of truth.

Why Terraform Controller + FluxCD?

  • Terraform Controller: A Kubernetes controller that allows you to manage Terraform executions through Custom Resource Definitions (CRDs).
  • FluxCD: A GitOps toolkit that automates Kubernetes deployments by syncing Git repositories with clusters.
  • GitOps Benefits: Version control, audit trails, CI/CD integration, and reduced configuration drift.

Prerequisites
Before you begin, ensure you have the following:

  • A Kubernetes cluster (EKS,AKS,GKS,On-Prem or local like KinD)
  • AWS credentials with necessary IAM permissions
  • kubectl, flux, and terraform controller installed
  • A Git repository for storing Terraform manifests

Step-by-Step Guide

Install Kind Cluster

#cat config.yaml 
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker

#kind create cluster --config=config.yaml

Image description

Install flux cli

#brew install fluxcd/tap/flux

Image description

BootStrap fluxcd to install flux-system with gitrepo infra-demo
Image description

#flux bootstrap gitlab   --token-auth   --owner=infra-demo2   --repository=flux-system   --branch=main   --path=clusters/dev

Image description

A namespace name flux-system will be created with required components

#kubectl get ns
#kubectl get all -n flux-system

Image description

Lets Install Terraform Controller

#helm repo add tofu-controller https://flux-iac.github.io/tofu-controller

#helm upgrade -i tofu-controller tofu-controller/tf-controller \
    --namespace flux-system

Image description

Lets Verify Controller in flux-system

#kubectl get pods -n flux-system | grep tf

Image description

Create infrastructure project inside infra-demo repo and map Source list and Kustamization to infra-demo repository to managed by Flux-cd

Image description

#cat infra-demo.yaml
apiVersion: source.toolkit.fluxcd.io/v1beta1
kind: GitRepository
metadata:
  name: vpc-creation
  namespace: flux-system
spec:
  interval: 30s
  url: https://gitlab.com/infra-demo2/infrastructure.git
  ref:
    branch: main
  secretRef:
    name: flux-system
---
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
  name: vpc-creation
  namespace: flux-system
spec:
  prune: true
  interval: 2m
  path: ./      
  sourceRef:
    kind: GitRepository
    name: vpc-creation
  timeout: 3m
  healthChecks:
    - apiVersion: infra.contrib.fluxcd.io/v1alpha1
      kind: Terraform
      name: vpc-creation
      namespace: flux-system

Lets Create terraform Code in Infrastructure Project to deploy VPC in AWS Account

#cat main.tf
resource "aws_vpc" "main" {
  cidr_block           = var.vpc_cidr
  enable_dns_support   = true
  enable_dns_hostnames = true
  tags = {
    Name = "${var.project}-vpc"
  }
}

resource "aws_subnet" "public" {
  count             = length(var.public_subnets)
  vpc_id            = aws_vpc.main.id
  cidr_block        = var.public_subnets[count.index]
  availability_zone = element(var.availability_zones, count.index)
  map_public_ip_on_launch = true
  tags = {
    Name = "${var.project}-public-subnet-${count.index + 1}"
  }
}

resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.main.id
  tags = {
    Name = "${var.project}-igw"
  }
}

resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id
  tags = {
    Name = "${var.project}-public-rt"
  }
}

resource "aws_route" "internet_access" {
  route_table_id         = aws_route_table.public.id
  destination_cidr_block = "0.0.0.0/0"
  gateway_id             = aws_internet_gateway.igw.id
}

resource "aws_route_table_association" "public_assoc" {
  count          = length(var.public_subnets)
  subnet_id      = aws_subnet.public[count.index].id
  route_table_id = aws_route_table.public.id
}
#cat outputs.tf
output "vpc_id" {
  value = aws_vpc.main.id
}

output "public_subnet_ids" {
  value = aws_subnet.public[*].id
}

#cat variables.tf
variable "aws_region" {
  default = "us-east-1"
}

variable "project" {
  default = "demo"
}

variable "vpc_cidr" {
  default = "10.0.0.0/16"
}

variable "public_subnets" {
  default = ["10.0.1.0/24", "10.0.2.0/24"]
}

variable "availability_zones" {
  default = ["us-east-1a", "us-east-1b"]
}

Note: Not Recommended

#cat provider.tf
provider "aws" {
  region     = var.aws_region
  access_key = "XXXXXXXXXX"
  secret_key = "YYYYYYYYYYYYYY"
}

Now Create Terraform Controller to Deploy Terraform Code

#cat terraform.yaml
apiVersion: infra.contrib.fluxcd.io/v1alpha1
kind: Terraform
metadata:
  name: vpc-creation
  namespace: flux-system
spec:
  interval: 1m
  approvePlan: auto
  path: ./                     
  sourceRef:
    kind: GitRepository
    name: vpc-creation
    namespace: flux-system  

Now the Repo will look Like
Image description

Now Lets Deploy infrademo.yaml to Map Repo to get managed by flux-system

#flux get sources all | grep vpc-creation

#flux get kustomization | grep vpc-creation

# kubectl apply -f infrademo.yaml

#flux get sources all | grep vpc-creation

#flux get sources all | grep vpc-creation

Image description

Once we Deploy it Terraform Controller is going to Create a runner with name of vpc-creation-tf-runner which is going to deploy the resources in AWS

Image description

Lets verify the log of the runner

kubectl logs vpc-creation-tf-runner -n flus-system -f

Image description

Congratulations: We have deployed our resource successfully

Lets Verify from the AWS Portal

Image description

Image description