{"id":1139,"date":"2025-08-15T19:46:42","date_gmt":"2025-08-16T02:46:42","guid":{"rendered":"http:\/\/184.72.63.26\/?p=1139"},"modified":"2025-08-22T16:44:26","modified_gmt":"2025-08-22T23:44:26","slug":"mastering-terraform-as-a-site-reliability-engineer-a-complete-guide-for-multi-cloud-and-multi-environment-automation","status":"publish","type":"post","link":"https:\/\/www.wallacel.com\/index.php\/2025\/08\/15\/mastering-terraform-as-a-site-reliability-engineer-a-complete-guide-for-multi-cloud-and-multi-environment-automation\/","title":{"rendered":"Mastering Terraform as a Site Reliability Engineer: A Complete Guide for Multi-Cloud and Multi-Environment Automation"},"content":{"rendered":"\n<p>Infrastructure as Code (IaC) is at the heart of modern SRE and DevOps practices. As an SRE, using <strong>Terraform<\/strong> empowers you to provision, manage, and scale infrastructure predictably across cloud environments. In this guide, I will walk you through the key reasons and examples that showcase <strong>why and how I use Terraform<\/strong>, particularly for multi-region and multi-cloud deployments, provisioning automation, and secure secret management.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Why Use Terraform?<\/h2>\n\n\n\n<p>Terraform is a declarative IaC tool that helps you manage infrastructure consistently, reliably, and at scale. Here are a few key benefits:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Cloud Agnostic<\/strong>: Supports AWS, Azure, GCP, and many others.<\/li>\n\n\n\n<li><strong>Multi-region Deployment<\/strong>: Define and manage resources across regions from a single codebase.<\/li>\n\n\n\n<li><strong>Modular and Reusable<\/strong>: Write once, reuse with variables and modules.<\/li>\n\n\n\n<li><strong>Version Controlled<\/strong>: Keep infrastructure definitions in Git to track changes and enable collaboration.<\/li>\n\n\n\n<li><strong>Automation-Ready<\/strong>: Integrates seamlessly with CI\/CD pipelines and tools like GitHub Actions.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Multi-Region and Multi-Cloud Deployments<\/h2>\n\n\n\n<p>In a globally distributed system, SREs often need to deploy services across multiple regions or even different cloud providers. Terraform makes this easy by allowing the use of multiple provider blocks. This approach enables:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>High availability<\/strong> and <strong>disaster recovery<\/strong><\/li>\n\n\n\n<li><strong>Latency optimization<\/strong> by placing resources closer to users<\/li>\n\n\n\n<li><strong>Vendor independence<\/strong> and <strong>resiliency<\/strong> across cloud providers<\/li>\n<\/ul>\n\n\n\n<p>This addresses typical SRE goals such as minimizing downtime, reducing failure domains, and simplifying cross-cloud scaling. Using Terraform, you can define <strong>multiple provider blocks with aliases<\/strong> to manage different regions or cloud providers:<\/p>\n\n\n\n<p><strong>Multi-Region Example (AWS)<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"json\" class=\"language-json\">provider \"aws\" {\n  alias  = \"us-east-1\"\n  region = \"us-east-1\"\n}\n\nprovider \"aws\" {\n  alias  = \"us-west-2\"\n  region = \"us-west-2\"\n}\n\nresource \"aws_instance\" \"east\" {\n  ami           = \"ami-0123456789abcdef0\"\n  instance_type = \"t2.micro\"\n  provider      = aws.us-east-1\n}\n\nresource \"aws_instance\" \"west\" {\n  ami           = \"ami-0123456789abcdef0\"\n  instance_type = \"t2.micro\"\n  provider      = aws.us-west-2\n}<\/code><\/pre>\n\n\n\n<p><strong>Multi-Cloud Example (AWS + Azure)<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"json\" class=\"language-json\">provider \"aws\" {\n  region = \"us-east-1\"\n}\n\nprovider \"azurerm\" {\n  features        = {}\n  subscription_id = \"&lt;subscription_id&gt;\"\n  client_id       = \"&lt;client_id&gt;\"\n  client_secret   = \"&lt;client_secret&gt;\"\n  tenant_id       = \"&lt;tenant_id&gt;\"\n}\n\nresource \"aws_instance\" \"example\" {\n  ami           = \"ami-0123456789abcdef0\"\n  instance_type = \"t2.micro\"\n}\n\nresource \"azurerm_virtual_machine\" \"example\" {\n  name     = \"example-vm\"\n  location = \"eastus\"\n  size     = \"Standard_A1\"\n  # other required fields\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Terraform Variables: Inputs, Outputs, and Tfvars<\/h2>\n\n\n\n<p>Variables in Terraform promote <strong>reusability<\/strong>, <strong>parameterization<\/strong>, and <strong>separation of configuration from logic<\/strong>. Instead of hardcoding values like instance types, region names, or AMI IDs, we can define them as variables and supply different values per environment, workspace, or CI\/CD pipeline.<\/p>\n\n\n\n<p>This makes it easier to:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Deploy to multiple environments (e.g., dev, stage, prod)<\/li>\n\n\n\n<li>Reduce code duplication<\/li>\n\n\n\n<li>Handle sensitive values securely<\/li>\n\n\n\n<li>Support team collaboration and maintain clean, scalable infrastructure definitions<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Input Variables<\/h3>\n\n\n\n<p>Variables can be defined in its own (e.g. <code>variables.tf<\/code>) or directly in your <code>main.tf<\/code>, these are the parameters your module or configuration expects.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"json\" class=\"language-json\">variable \"instance_type\" {\n  description = \"EC2 instance type\"\n  type        = string\n  default     = \"t2.micro\"\n}\n\nresource \"aws_instance\" \"example_instance\" {\n  ami           = var.ami_id\n  instance_type = var.instance_type\n}<\/code><\/pre>\n\n\n\n<p>You can override input variable values in several ways:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>From CLI: <code>terraform apply -var=\"instance_type=t3.micro\"<\/code><\/li>\n\n\n\n<li>From a <code>.tfvars<\/code> file<\/li>\n\n\n\n<li>From environment variables prefixed with <code>TF_VAR_<\/code><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Output Variables<\/h3>\n\n\n\n<p>Output variables expose computed information after a Terraform run. These are useful for referencing values in other modules or for displaying information. For example, following output variable prints out the public ip address after an EC2 instance is created.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"json\" class=\"language-json\">output \"public_ip\" {\n  description = \"Public IP address of the EC2 instance\"\n  value       = aws_instance.example_instance.public_ip\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Tfvars File<\/h3>\n\n\n\n<p>The default <code>terraform.tfvars<\/code> file helps organize values for input variables by supplying their values:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"json\" class=\"language-json\">cidr              = \"10.0.0.0\/16\"\ninstance_id       = \"ami-014e30c8a36252ae5\"\ninstance_type     = \"t2.micro\"\nbucket_name       = \"terraform-s3-bucket\"\nregion            = \"us-west-1\"\navailability_zones = [\"us-west-1a\", \"us-west-1b\"]<\/code><\/pre>\n\n\n\n<p>To use a different tfvars file during resources creation:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"json\" class=\"language-json\">terraform apply -var-file=dev.tfvars<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Using Modules for Reusability<\/h2>\n\n\n\n<p>Terraform modules are reusable containers for multiple resources used together. They help you:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Encapsulate logic<\/strong> for deploying standardized components (e.g., EC2 instance, VPC)<\/li>\n\n\n\n<li><strong>Promote DRY principles<\/strong> by avoiding duplicated code<\/li>\n\n\n\n<li><strong>Standardize deployments<\/strong> across teams and environments<\/li>\n<\/ul>\n\n\n\n<p>In SRE practice, this supports automation, consistency, and faster onboarding, while reducing the risk of human error when provisioning infrastructure repeatedly.<\/p>\n\n\n\n<p>For example, in this \\module\\ec2_instance\\main.tf, these blocks defines a module that creates an EC2 instance:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"json\" class=\"language-json\">variable \"ami_value\" {\n  description = \"AMI ID for the EC2 instance\"\n  type        = string\n}\nvariable \"instance_type_value\" {\n  description = \"Type of the EC2 instance\"\n  type        = string\n}\n\nresource \"aws_instance\" \"example\" {\n  ami           = var.ami_value\n  instance_type = var.instance_type_value\n}<\/code><\/pre>\n\n\n\n<p>In a multi-user environment, when any teams want to creates an EC2 instance, they simply write a main.tf that pass the desired values to the modules (i.e. ami_value &amp; instance_type_value) to the module to create the resources they need, thus speed up the time required to provision their infrastructure and reduce human error. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"json\" class=\"language-json\">provider \"aws\" {\n  region = \"us-west-1\"\n}\n\nmodule \"ec2_instance\" {\n  source              = \".\/module\/ec2_instance\"\n  ami_value           = \"ami-014e30c8a36252ae5\"\n  instance_type_value = \"t2.micro\"\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Terraform State Management and Remote Backend<\/h2>\n\n\n\n<p>Terraform uses a <strong>state file<\/strong> to record the current state of your infrastructure. This file is essential because Terraform relies on it to determine what actions are required to bring the infrastructure in sync with your code. It keeps track of created resources, attributes, and dependencies.<\/p>\n\n\n\n<p>By default, this state file is stored <strong>locally<\/strong> on your machine, which works fine for solo projects \u2014 but becomes problematic in <strong>multi-user or team environments<\/strong>.<\/p>\n\n\n\n<p><strong>Drawbacks of Local State in Team Settings<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>No shared visibility<\/strong> \u2014 Only the person with the local file knows the current state.<\/li>\n\n\n\n<li><strong>High risk of overwrites<\/strong> \u2014 Two users applying changes simultaneously may corrupt or overwrite each other&#8217;s work.<\/li>\n\n\n\n<li><strong>No locking<\/strong> \u2014 There&#8217;s no mechanism to prevent multiple Terraform runs from happening at once.<\/li>\n\n\n\n<li><strong>Configuration drift<\/strong> \u2014 Without a consistent, central state, environments can fall out of sync.<\/li>\n\n\n\n<li><strong>Security risks<\/strong> \u2014 Local state may contain sensitive data (e.g. passwords, tokens) and is vulnerable to leaks if not properly secured.<\/li>\n<\/ul>\n\n\n\n<p><strong>Remote Backend: The Solution<\/strong><\/p>\n\n\n\n<p>To solve these issues, Terraform supports <strong>remote backends<\/strong> \u2014 storage systems where the state file is saved and accessed centrally. Popular options include:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>AWS S3 (with DynamoDB for locking)<\/li>\n\n\n\n<li>Terraform Cloud<\/li>\n\n\n\n<li>Azure Blob Storage<\/li>\n<\/ul>\n\n\n\n<p>Using a remote backend:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Provides a <strong>central source of truth<\/strong><\/li>\n\n\n\n<li>Enables <strong>team collaboration<\/strong><\/li>\n\n\n\n<li>Supports <strong>state locking<\/strong> and <strong>history tracking<\/strong><\/li>\n\n\n\n<li>Helps enforce <strong>CI\/CD workflows<\/strong> safely<\/li>\n<\/ul>\n\n\n\n<p><strong>What is Locking and Why Use DynamoDB?<\/strong><\/p>\n\n\n\n<p>When multiple people or automation pipelines interact with the same Terraform state, <strong>locking<\/strong> prevents conflicts. It ensures that only one process can update the state at a time.<\/p>\n\n\n\n<p><strong>Amazon DynamoDB<\/strong> is often used with S3 backends to manage this lock:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Atomic operations<\/strong> to prevent race conditions<\/li>\n\n\n\n<li><strong>Blocking and waiting<\/strong> if a lock is already held<\/li>\n\n\n\n<li><strong>Scalable and highly available<\/strong> \u2014 no single point of failure<\/li>\n<\/ul>\n\n\n\n<p>In SRE practices, this setup ensures reliable and concurrent-safe infrastructure changes \u2014 even with multiple developers or automation workflows.<\/p>\n\n\n\n<p><strong>Remote Backend with Locking Example<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"json\" class=\"language-json\">terraform {\n  backend \"s3\" {\n    bucket         = \"wallace-s3-terraform-state-files\"\n    key            = \"wallace\/terraform.tfstate\"\n    region         = \"us-west-1\"\n    dynamodb_table = \"terraform-lock\"  # Enables locking\n  }\n}<\/code><\/pre>\n\n\n\n<p>This blocks tells Terraform to use AWS S3 bucket to maintain the state file and lock file centrally in DynamoDB.<\/p>\n\n\n\n<p>Following is a sample block to provision a DynamoDB for maintain the lock file using Terraform:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"json\" class=\"language-json\">resource \"aws_dynamodb_table\" \"terraform_lock\" {\n  name         = \"terraform-lock\"\n  billing_mode = \"PAY_PER_REQUEST\"\n  hash_key     = \"LockID\"\n\n  attribute {\n    name = \"LockID\"\n    type = \"S\"\n  }\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Terraform Provisioners<\/h2>\n\n\n\n<p>Provisioners in Terraform allow you to run scripts or commands <strong>after<\/strong> a resource is created. This is useful for bootstrapping, installing dependencies, or configuring systems on first boot.<\/p>\n\n\n\n<p>In an SRE context, provisioners enable:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Rapid test environment setup<\/strong> for integration\/QA<\/li>\n\n\n\n<li>On-demand <strong>debugging or patching<\/strong> in dynamic environments<\/li>\n\n\n\n<li>Simplified <strong>deployment pipelines<\/strong> for quick validation of infrastructure-as-code<\/li>\n<\/ul>\n\n\n\n<p>Use them cautiously \u2014 if a provisioner fails, Terraform may mark the resource as tainted.<\/p>\n\n\n\n<p><strong>Full Provisioner-Based Infrastructure Example<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"json\" class=\"language-json\">terraform {\n  required_providers {\n    aws = {\n      source  = \"hashicorp\/aws\"\n      version = \"6.8.0\"\n    }\n  }\n}\n\nprovider \"aws\" {\n  region = \"us-west-1\"\n}\n\nvariable \"cidr\" {\n  default = \"10.0.0.0\/16\"\n}\n\nresource \"aws_key_pair\" \"example\" {\n  key_name   = \"terraform-demo-wallace\"\n  public_key = file(\"~\/.ssh\/id_rsa.pub\")\n}\n\nresource \"aws_vpc\" \"main\" {\n  cidr_block = var.cidr\n}\n\nresource \"aws_subnet\" \"sub1\" {\n  vpc_id                  = aws_vpc.main.id\n  cidr_block              = \"10.0.0.0\/24\"\n  availability_zone       = \"us-west-1a\"\n  map_public_ip_on_launch = true\n}\n\nresource \"aws_internet_gateway\" \"igw\" {\n  vpc_id = aws_vpc.main.id\n}\n\nresource \"aws_route_table\" \"main\" {\n  vpc_id = aws_vpc.main.id\n\n  route {\n    cidr_block = \"0.0.0.0\/0\"\n    gateway_id = aws_internet_gateway.igw.id\n  }\n}\n\nresource \"aws_route_table_association\" \"rta1\" {\n  subnet_id      = aws_subnet.sub1.id\n  route_table_id = aws_route_table.main.id\n}\n\nresource \"aws_security_group\" \"web_sg\" {\n  name   = \"web\"\n  vpc_id = aws_vpc.main.id\n\n  ingress {\n    description = \"HTTP from VPC\"\n    from_port   = 8000\n    to_port     = 8000\n    protocol    = \"tcp\"\n    cidr_blocks = [\"0.0.0.0\/0\"]\n  }\n\n  ingress {\n    description = \"SSH\"\n    from_port   = 22\n    to_port     = 22\n    protocol    = \"tcp\"\n    cidr_blocks = [\"0.0.0.0\/0\"]\n  }\n\n  egress {\n    description = \"Allow all outbound traffic\"\n    from_port   = 0\n    to_port     = 0\n    protocol    = \"-1\"\n    cidr_blocks = [\"0.0.0.0\/0\"]\n  }\n}\n\nresource \"aws_instance\" \"web\" {\n  ami                    = \"ami-014e30c8a36252ae5\"\n  instance_type          = \"t2.micro\"\n  key_name               = aws_key_pair.example.key_name\n  subnet_id              = aws_subnet.sub1.id\n  vpc_security_group_ids = [aws_security_group.web_sg.id]\n  associate_public_ip_address = true\n\n  tags = {\n    Name = \"WebServer\"\n  }\n\n  connection {\n    type        = \"ssh\"\n    user        = \"ubuntu\"\n    private_key = file(\"~\/.ssh\/id_rsa\")\n    host        = self.public_ip\n  }\n\n  provisioner \"file\" {\n    source      = \"app.py\"\n    destination = \"\/home\/ubuntu\/app.py\"\n  }\n\n  provisioner \"remote-exec\" {\n    inline = [\n      \"echo 'Hello from the remote instance'\",\n      \"sudo apt update -y\",\n      \"sudo apt-get install -y python3-venv\",\n      \"cd \/home\/ubuntu\",\n      \"python3 -m venv appenv\",\n      \"\/home\/ubuntu\/appenv\/bin\/pip install --upgrade pip\",\n      \"\/home\/ubuntu\/appenv\/bin\/pip install flask\",\n      \"chmod +x \/home\/ubuntu\/app.py\",\n      \"\/home\/ubuntu\/appenv\/bin\/python \/home\/ubuntu\/app.py\"\n    ]\n  }\n}<\/code><\/pre>\n\n\n\n<p>This example provisions an entire testing environment automatically:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Creates a <strong>VPC<\/strong>, <strong>public subnet<\/strong>, <strong>internet gateway<\/strong>, and <strong>route table<\/strong><\/li>\n\n\n\n<li>Sets up a <strong>security group<\/strong> that allows SSH and app traffic (port 8000)<\/li>\n\n\n\n<li>Provisions an <strong>EC2 instance<\/strong> using a key pair and public subnet<\/li>\n\n\n\n<li>Uses <code>file<\/code> provisioner to copy a local <code>app.py<\/code> script to the instance<\/li>\n\n\n\n<li>Uses <code>remote-exec<\/code> provisioner to install dependencies and start a Flask app<\/li>\n<\/ul>\n\n\n\n<p>This automation enables rapid deployment of test or staging environments during development or CI\/CD pipeline runs \u2014 a core goal of modern SRE teams.<\/p>\n\n\n\n<p><strong>GitHub Actions Integration Example<\/strong><\/p>\n\n\n\n<p>In a DevOps workflow, it&#8217;s common to automate infrastructure provisioning when code changes occur. Here&#8217;s how you can use <strong>GitHub Actions<\/strong> to automatically trigger Terraform when <code>app.py<\/code> is updated:<\/p>\n\n\n\n<p>Create a GitHub Actions workflow in <code>.github\/workflows\/deploy.yml<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"json\" class=\"language-json\">name: Auto Provision EC2 on App Change\n\non:\n  push:\n    paths:\n      - app.py\n\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout repository\n        uses: actions\/checkout@v3\n\n      - name: Setup Terraform\n        uses: hashicorp\/setup-terraform@v3\n        with:\n          terraform_version: 1.5.0\n\n      - name: Terraform Init\n        run: terraform init\n\n      - name: Terraform Plan\n        run: terraform plan -out=tfplan\n\n      - name: Terraform Apply\n        run: terraform apply -auto-approve tfplan\n\n      - name: Cleanup Plan File\n        run: rm tfplan<\/code><\/pre>\n\n\n\n<p>This pipeline listens for changes to <code>app.py<\/code> and automatically runs <code>terraform init<\/code>, <code>plan<\/code>, and <code>apply<\/code>. It ensures that any changes to the application are immediately provisioned onto the EC2 instance via the configured provisioners.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Workspaces for Multi-Environment Support<\/h2>\n\n\n\n<p>Workspaces in Terraform allow you to maintain <strong>isolated state files<\/strong> for different environments (like dev, staging, prod) using the same configuration.<\/p>\n\n\n\n<p>This solves several problems in SRE practice:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Avoids <strong>overwriting infrastructure<\/strong> across environments<\/li>\n\n\n\n<li>Enables <strong>safe parallel deployments<\/strong><\/li>\n\n\n\n<li>Simplifies <strong>CI\/CD automation<\/strong> with clearly separated states<\/li>\n<\/ul>\n\n\n\n<p>Workspaces allow you to isolate environments (dev\/stage\/prod) with separate state files.<\/p>\n\n\n\n<p><strong>Workspace Commands<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">terraform workspace new dev\nterraform workspace select dev\nterraform workspace show<\/code><\/pre>\n\n\n\n<p>This example creates AWS EC2 instance with different instance type according to the workspace by using map() and lookup() functions:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"json\" class=\"language-json\">provider \"aws\" {\n  region = \"us-west-1\"\n}\n\nvariable \"ami_value\" {\n  description = \"AMI ID for the EC2 instance\"\n  type        = string\n}\n\nvariable \"instance_type_value\" {\n  description = \"Type of the EC2 instance\"\n  type = map(string)    #use map to allow different instance types for different environments\n\n  default = {\n    \"dev\"   = \"t2.micro\"\n    \"stage\" = \"t2.small\"\n    \"prod\"  = \"t2.large\"\n  }\n}\n\nmodule \"ec2_instance\" {\n  source                = \".\/modules\/ec2_instance\"\n  ami_value             = var.ami_value\n  instance_type_value   = lookup(var.instance_type_value, terraform.workspace, \"t2.micro\") # Use lookup to get the instance type based on the workspace   \n}<\/code><\/pre>\n\n\n\n<p>By using different workspaces for dev\/stage\/prod environments, state file (<strong>terraform.tfstate<\/strong>) are maintain separately as seen in the following folder structure:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">wallacelee@imac % tree\n.\n\u251c\u2500\u2500 main.tf\n\u251c\u2500\u2500 modules\n\u2502   \u2514\u2500\u2500 ec2_instance\n\u2502       \u2514\u2500\u2500 main.tf\n\u251c\u2500\u2500 stage.tfvars\n\u251c\u2500\u2500 terraform.tfstate.d\n\u2502   \u251c\u2500\u2500 dev\n\u2502   \u2502   \u251c\u2500\u2500 terraform.tfstate\n\u2502   \u2502   \u2514\u2500\u2500 terraform.tfstate.backup\n\u2502   \u251c\u2500\u2500 prod\n\u2502   \u2502   \u251c\u2500\u2500 terraform.tfstate\n\u2502   \u2502   \u2514\u2500\u2500 terraform.tfstate.backup\n\u2502   \u2514\u2500\u2500 stage\n\u2502       \u251c\u2500\u2500 terraform.tfstate\n\u2502       \u2514\u2500\u2500 terraform.tfstate.backup\n\u2514\u2500\u2500 terraform.tfvars\n\n7 directories, 10 files<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Secret Management with Vault + Terraform<\/h2>\n\n\n\n<p>HashiCorp Vault provides a secure way to store and access secrets. Terraform can authenticate to Vault using AppRole and retrieve secrets like API keys, passwords, or bucket names at runtime.<\/p>\n\n\n\n<p>This addresses several critical SRE concerns:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Avoid hardcoding sensitive data<\/strong> in Terraform code or state files<\/li>\n\n\n\n<li>Centralized <strong>secret lifecycle management<\/strong><\/li>\n\n\n\n<li>Enforce <strong>access control and auditability<\/strong> for secrets usage<\/li>\n<\/ul>\n\n\n\n<p>Integrating Vault with Terraform boosts your infrastructure security posture while maintaining automation.<\/p>\n\n\n\n<p><strong>Use Case:<\/strong> Securely retrieve a secret value (e.g., S3 bucket name) from Vault and use it in a resource:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"json\" class=\"language-json\">resource \"aws_s3_bucket\" \"example\" {\n  bucket = data.vault_kv_secret_v2.example.data[\"s3-bucket-name\"]\n}<\/code><\/pre>\n\n\n\n<p><strong>How to Setup Hashicorp Vault:<\/strong><\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>Install Vault<\/strong> on an EC2 instance.<\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">sudo apt update &amp;&amp; sudo apt install gpg\n\nwget -O- https:\/\/apt.releases.hashicorp.com\/gpg | sudo gpg --dearmor -o \/usr\/share\/keyrings\/hashicorp-archive-keyring.gpg\n\ngpg --no-default-keyring --keyring \/usr\/share\/keyrings\/hashicorp-archive-keyring.gpg --fingerprint\n\necho \"deb [arch=$(dpkg --print-architecture) signed-by=\/usr\/share\/keyrings\/hashicorp-archive-keyring.gpg] https:\/\/apt.releases.hashicorp.com $(lsb_release -cs) main\" | sudo tee \/etc\/apt\/sources.list.d\/hashicorp.list\n\nsudo apt update\n\nsudo apt install vault\n\n#start the vault in ec2 instance:\n\nvault server -dev -dev-listen-address=\"0.0.0.0:8200\"<\/code><\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li>Enable KV (key-pair value) secrets engine: <code>vault secrets enable -path=kv kv-v2<\/code><\/li>\n\n\n\n<li>Create a secret: <code>vault kv put kv\/myapp s3-bucket-name=wallace-prod-bucket<\/code><\/li>\n\n\n\n<li>Create a policy and role:<\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">vault policy write terraform - &lt;&lt;EOF\npath \"kv\/data\/*\" {\n  capabilities = [\"create\", \"read\", \"update\", \"delete\", \"list\"]\n}\nEOF\n\nvault write auth\/approle\/role\/terraform \\\n    secret_id_ttl=10m \\\n    token_num_uses=10 \\\n    token_ttl=20m \\\n    token_max_ttl=30m \\\n    secret_id_num_uses=40 \\\n    token_policies=terraform<\/code><\/pre>\n\n\n\n<ol start=\"5\" class=\"wp-block-list\">\n<li>Get <code>role_id<\/code> and <code>secret_id<\/code>:<\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">vault read auth\/approle\/role\/terraform\/role-id\nvault write -f auth\/approle\/role\/terraform\/secret-id<\/code><\/pre>\n\n\n\n<p>The following example creates a S3 bucket with bucket name securely retrieved from the secret key <strong>s3-bucket-name<\/strong>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"json\" class=\"language-json\">provider \"aws\" {\n  region = \"us-west-1\"\n}\n\nprovider \"vault\" {\n  address = \"http:\/\/x.x.x.x:8200\"\n  skip_child_token = true\n\n  auth_login {\n    path = \"auth\/approle\/login\" # Use the AppRole auth method\n\n    parameters = {\n      role_id   = \"42fb0f72-2c2a-abb9-7b8e-b2f73ac75e83\"\n      secret_id = \"ba45c19c-206e-c201-f454-83f772ba5f40\"\n    }\n  }\n}\n\ndata \"vault_kv_secret_v2\" \"example\" {\n  mount = \"kv\" # Change it according to your mount\n  name  = \"test-secret\" #name of the secret in Vault\n}\n\nresource \"aws_s3_bucket\" \"example\" {\n  bucket = data.vault_kv_secret_v2.example.data[\"s3-bucket-name\"]}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Final Thoughts<\/h2>\n\n\n\n<p>Terraform is a must-have tool in any SRE or DevOps engineer&#8217;s toolkit. Whether you&#8217;re managing complex multi-cloud infrastructure, isolating environments with workspaces, or automating test environments with provisioners, <strong>Terraform brings structure, safety, and scalability<\/strong> to your infrastructure operations.<\/p>\n\n\n\n<p>You can find all my Terraform sample scripts <a href=\"https:\/\/github.com\/wallylee99\/github-mastering-terraform\" data-type=\"link\" data-id=\"https:\/\/github.com\/wallylee99\/github-mastering-terraform\">here<\/a>. Thank you for reading my blog and I hope you like it.<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Infrastructure as Code (IaC) is at the heart of modern SRE and DevOps practices. As an SRE, using Terraform empowers you to provision, manage, and scale infrastructure predictably across cloud environments. In this guide, I will walk you through the key reasons and examples that showcase why and how I use Terraform, particularly for multi-region [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":1162,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1,72],"tags":[19,22],"class_list":["post-1139","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-aws","category-terraform","tag-aws","tag-terraform"],"_links":{"self":[{"href":"https:\/\/www.wallacel.com\/index.php\/wp-json\/wp\/v2\/posts\/1139","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.wallacel.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.wallacel.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.wallacel.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.wallacel.com\/index.php\/wp-json\/wp\/v2\/comments?post=1139"}],"version-history":[{"count":15,"href":"https:\/\/www.wallacel.com\/index.php\/wp-json\/wp\/v2\/posts\/1139\/revisions"}],"predecessor-version":[{"id":1160,"href":"https:\/\/www.wallacel.com\/index.php\/wp-json\/wp\/v2\/posts\/1139\/revisions\/1160"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.wallacel.com\/index.php\/wp-json\/wp\/v2\/media\/1162"}],"wp:attachment":[{"href":"https:\/\/www.wallacel.com\/index.php\/wp-json\/wp\/v2\/media?parent=1139"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.wallacel.com\/index.php\/wp-json\/wp\/v2\/categories?post=1139"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.wallacel.com\/index.php\/wp-json\/wp\/v2\/tags?post=1139"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}