Infrastructure as Code: Terraform, Ansible
Infrastructure as Code: Terraform, Ansible
1) Why IaC and how tools differ
Terraform - declarative orchestrator-IaC: creates/modifies cloud infrastructure (VPC, K8s, LB, DB, IAM) through providers. Manages resource lifecycle and stores state.
Ansible - procedural config-IaC and configuration management: configures hosts/software (packages, files, services), knows how to provide applications, templating, orchestration of steps. Without an agent (SSH/WinRM), with idempotency of tasks.
Combination: Terraform - "what the platform is made of," Ansible - "how it is configured and launched."
2) Structure of repositories and layers
We recommend 3 layers:1. Foundation (Landing Zone): networks, IAM, KMS, base logs/monitoring.
2. Platform: Kubernetes clusters, databases, queues, observability stack.
3. Workloads: spaces/namespaces, service accounts, policies, configs.
Repo:- 'iac/terraform/' - modules and environments ('modules/',' envs/').
- 'iac/ansible/' - roles ('roles/'), playbooks (' playbooks/'), inventories ('inventions/').
- 'policies/' - OPA/Conftest rules.
- 'pipelines/' - CI/CD scripts (lint/plan/apply/test).
3) Terraform: modules, state, environments
3. 1 Modules
Small, reused: 'vpc', 'eks', 'rds', 'redis', 'alb', 'iam-role'.
Inputs → 'variables. tf ', outputs →' outputs. tf ', versions - through the registry of modules/git-tags.
hcl module "vpc" {
source = "git::ssh://git@repo/iac. git//modules/vpc? ref=v1. 6. 0"
name = "prod-core"
cidr = "10. 0. 0. 0/16"
azs = ["eu-central-1a","eu-central-1b"]
}
3. 2 State
Remote backend (S3/GCS + DynamoDB/LockTable) + locks.
Separation by environment: 'prod. tfstate`, `staging. tfstate`.
State-drift: 'terraform plan' in CI on schedule; alert when drifting.
hcl terraform {
backend "s3" {
bucket = "iac-state"
key = "prod/network/terraform. tfstate"
region = "eu-central-1"
dynamodb_table = "iac-locks"
encrypt = true
}
}
3. 3 Workspaces / Envs
Options:- Separate directories' envs/prod ',' envs/stage '(visual).
- Workspaces for light variations, but avoid mixing secrets.
- Parameters via 'tfvars': 'prod. tfvars`, `stage. tfvars`.
4) Ansible: Roles, Inventories, Idempotence
4. 1 Roles and playbooks
Galaxy standard:
roles/
nginx/
tasks/main. yml templates/.j2 handlers/main. yml defaults/main. yml playbooks/
site. yml
Playbook example:
yaml
- hosts: web become: true roles:
- role: nginx vars:
nginx_listen_port: 8080
4. 2 Inventories and dynamics
`inventories/prod/hosts. yml '+ variables' group _ vars/host _ vars'.
Dynamic inventory: 'aws _ ec2', 'gcp _ compute', 'kubernetes. core`.
4. 3 Idempotency
Use modules (not 'shell') where possible.
'changed _ when '/' check _ mode'for dry runs.
Handlers only reflect on changes.
5) Secrets and configs
Terraform: secretion of values through 'sensitive = true'; the secrets themselves are not in the state (store in AWS Secrets Manager/HashiCorp Vault/KMS and refer to the data source).
Ansible: Vault for variable encryption ('ansible-vault encrypt'), integration with HashiCorp Vault/KMS.
GitOps: secrets in a separate repo/storage, access by least-privilege.
6) Policy as Code
OPA/Conftest for Terraform plans: banning public S3, open SG, untagged resources (tags).
Terraform Cloud/Enterprise - Sentinel as an alternative.
Ansible Lint: style and security (without 'sudo: yes' where not needed, without raw-shell).
rego package terraform. security
deny[msg] {
input. resource_type == "aws_security_group_rule"
input. values. cidr_blocks[_] == "0. 0. 0. 0/0"
input. values. from_port == 22 msg:= "SSH from 0. 0. 0. 0/0 is forbidden"
}
7) IaC testing
Terraform: 'tflint', 'tfsec '/' checkov', Terratest (Go) for integration checks.
Ansible: 'ansible-lint', Molecule (+ Docker/Podman/EC2) to test roles.
Smoke tests after apply: HTTP probes, port/processes, rights.
8) CI/CD и GitOps
Pipeline (on Pull/Merge Request):1. Lint/Sec: tflint, tfsec/checkov, ansible-lint.
2. Plan: 'terraform plan' with publication of artifact (comment in MR).
3. Policy Gate: Conftest/Sentinel.
4. Apply (manual/approved): only on main with the signature of artifacts.
5. Ansible deploy: '--check' on stage, then '--diff' in prod.
6. Post-checks: synthetics/Probe, release annotation dashboard.
GitOps:- Keep manifestos as a source of truth; Argo CD/Flux for Kubernetes, but basic clusters/primitives through Terraform.
9) Patterns for Kubernetes, networks, databases
9. 1 Kubernetes
Terraform: EKS/GKE/AKS clusters, nodes, IAM, StorageClass, LB.
Ansible: AMI/bastion preparation, image assembly/repositories, post-install (loggers/agents/OTel).
9. 2 Networks and perimeter
Terraform: VPC/subnets/NAT/Transit-Gateway/WAF, routes.
Ansible: NGINX/Envoy/HAProxy config, TLS, WAF rules config.
9. 3 Bases/caches/queues
Terraform: RDS/CloudSQL group parameter, Redis/ElastiCache, Kafka/MSK.
Ansible: cache warm-up, migration jobs, setting up backups/agents.
10) Examples of configs
10. 1 Terraform — RDS PostgreSQL + SG
hcl module "db" {
source = "terraform-aws-modules/rds/aws"
engine = "postgres"
engine_version = "15. 4"
instance_class = "db. m6g. large"
allocated_storage = 100 name = "core"
username = var. db_user password = var. db_password # см. secret data source vpc_security_group_ids = [module. db_sg. security_group_id]
multi_az = true backup_retention = 7
}
module "db_sg" {
source = "terraform-aws-modules/security-group/aws"
name = "db-core"
vpc_id = module. vpc. vpc_id ingress_cidr_blocks = ["10. 0. 0. 0/8"]
ingress_rules = ["postgresql-tcp"]
}
10. 2 Terraform - data source of the secret
hcl data "aws_secretsmanager_secret_version" "db" {
secret_id = "prod/db/core"
}
variable "db_password" {
type = string sensitive = true default = jsondecode(data. aws_secretsmanager_secret_version. db. secret_string). password
}
10. 3 Ansible - postgresql-client role (fragment)
yaml
- name: Install packages apt:
name: [ "postgresql-client-15" ]
state: present update_cache: yes
- name: Create. pgpass copy:
dest: /home/deploy/.pgpass mode: '0600'
content: "{{ db_host }}:5432:core:{{ db_user }}:{{ db_password }}"
no_log: true
- name: Smoke query shell: psql -h {{ db_host }} -U {{ db_user }} -d core -c "select 1"
register: psql_out changed_when: false
10. 4 Ansible - NGINX with template
yaml
- name: Deploy nginx. conf template:
src: templates/nginx. conf. j2 dest: /etc/nginx/nginx. conf notify: Restart nginx
- name: Ensure nginx running service:
name: nginx state: started enabled: true
handlers:
- name: Restart nginx service: { name: nginx, state: restarted }
11) Drift and compliance management
Periodic 'terraform plan' in read-only key; creates a ticket when there is a discrepancy.
Ansible --check scheduled for critical roles (audit mode).
Reports: failed OPA/Conftest policies, untagged/backup/monitoring resources.
12) Observability and audit
'terraform apply'logs and plan artifacts - save to object storage.
Ansible: 'callback _ plugins' (json) → central log/ELK; include job ID, commit SHA, trace_id.
Metrics: execution time of plans/playbooks, frequency of changes, test coverage.
13) Safety
Module/role signature or fixed tags/hashes.
Minimum IAM rights ('plan' ≠ 'apply'), separate the roles of CI and human app.
Encryption of state/logs, private registers of modules/roles.
Policy "no plaintext secrets in VCS," secret scanners (gitleaks/trufflehog).
14) Anti-patterns
One Terraform monster module "for everything"; missing module versions.
Local'terraform. tfstate 'and no locks.
Manual edits in the cloud over IaC → eternal drift.
Ansible 'shell '/' command' instead of modules (breaks idempotency).
Inventory "in one file" without groups/variables, mixing prod/stage.
Secrets in vars/repositories, absence of Vault/KMS.
Apply without Plan and without Peer Review.
15) Implementation checklist (0-45 days)
0-10 days
Configure remote backend/lock, expand Terraform modules.
Enable tflint/tfsec/checkov, ansible-lint; negotiate resource tags/labels.
Create Ansible roles for NGINX/agents/loggers; organize inventories.
11-25 days
Add Conftest/OPA, Terratest and Molecule for critical modules/roles.
CI: 'plan' on MR, plan artifact, manual-apply with approw; Ansible `--check` на stage.
Integrate Secret Storage (Vault/KMS/Secrets Manager).
26-45 days
Auto schedule for drift detection, policy reports.
Directory of modules/roles with versioning; 'README. md 'in each.
GitOps: annotations of releases, a combination with monitoring and alert gates.
16) Maturity metrics
% of environments with remote state and locks = 100%.
The share of modules/roles with tests (Terratest/Molecule) ≥ 70%.
The average time from MR to apply (prod) is hours, not days.
Zero "manual drift" (all changes go through MR).
100% of critical resources are covered by Policy as Code (tags, encryption, backup).
17) Conclusion
Terraform specifies a predictable, repeatable infrastructure base; Ansible brings hosts and services to the desired state. Add Policy as Code, remote state with location, tests, secret management and CI/CD with plan→review→apply - and your IaC loop becomes manageable, secure and fast, and releases are atomic and reversible.