<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Production Engineering on Fabio Lopes | Cloud Architect &amp; DevOps | AWS, Azure, GCP Expert</title><link>https://fabiolopes.page/tags/production-engineering/</link><description>Recent content in Production Engineering on Fabio Lopes | Cloud Architect &amp; DevOps | AWS, Azure, GCP Expert</description><generator>Hugo -- gohugo.io</generator><language>en</language><copyright>Fabio M. Lopes</copyright><lastBuildDate>Sun, 25 Oct 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://fabiolopes.page/tags/production-engineering/index.xml" rel="self" type="application/rss+xml"/><item><title>Beyond Day-1 Terraform: Rethinking Platform Operations in My Environment</title><link>https://fabiolopes.page/p/beyond-day-1-terraform-rethinking-platform-operations-in-my-environment/</link><pubDate>Sun, 25 Oct 2026 00:00:00 +0000</pubDate><guid>https://fabiolopes.page/p/beyond-day-1-terraform-rethinking-platform-operations-in-my-environment/</guid><description>&lt;img src="https://fabiolopes.page/p/beyond-day-1-terraform-rethinking-platform-operations-in-my-environment/terraform.jpg" alt="Featured image of post Beyond Day-1 Terraform: Rethinking Platform Operations in My Environment" />&lt;h2 id="introduction-the-day-2-challenge">Introduction: The Day-2 Challenge
&lt;/h2>&lt;p>I&amp;rsquo;ve been thinking about a challenge in the infrastructure environment I work with: how to keep Terraform relevant after the initial provisioning phase, without interfering with ongoing application deployments or resources managed by other teams.&lt;/p>
&lt;p>Provisioning infrastructure with Terraform is relatively straightforward when the boundaries are clear and the environment is being built from scratch. The situation becomes more complicated once applications are running, multiple teams share responsibilities, and operational changes start happening through different workflows.&lt;/p>
&lt;p>In our context, we operate across multiple AWS accounts, with workloads involving AWS Outposts, ECS, and supporting infrastructure. Networking, security, platform operations, and application delivery have different ownership boundaries.&lt;/p>
&lt;p>This introduces a practical question:&lt;/p>
&lt;p>&lt;strong>How can we continue managing infrastructure through Terraform while respecting the operational autonomy of the teams responsible for other parts of the environment?&lt;/strong>&lt;/p>
&lt;p>I don&amp;rsquo;t think there&amp;rsquo;s a single answer that applies to every organization. What follows are some of the challenges I&amp;rsquo;ve encountered, the architectural decisions I&amp;rsquo;m exploring, and the trade-offs I consider relevant to our environment.&lt;/p>
&lt;h2 id="1-understanding-the-environment-and-its-boundaries">1. Understanding the Environment and Its Boundaries
&lt;/h2>&lt;p>Our environment follows a multi-account model, with separate AWS accounts for development, pre-production, and production.&lt;/p>
&lt;p>Several teams participate in operating the platform:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Network team:&lt;/strong> Responsible for core networking components such as VPCs, subnets, and connectivity.&lt;/li>
&lt;li>&lt;strong>Security team:&lt;/strong> Responsible for centralized security policies, edge protection, and components such as the F5 WAF.&lt;/li>
&lt;li>&lt;strong>Platform team:&lt;/strong> Responsible for infrastructure components supporting application workloads, including ECS, load balancers, databases, and infrastructure automation.&lt;/li>
&lt;li>&lt;strong>Application teams and external partners:&lt;/strong> Responsible for developing and delivering applications, with production access mediated through internal processes.&lt;/li>
&lt;/ul>
&lt;p>These responsibilities introduce an important distinction: not everything an application depends on should necessarily be managed by the same Terraform configuration.&lt;/p>
&lt;p>The challenge is not simply technical. It is also about respecting ownership boundaries.&lt;/p>
&lt;h3 id="decision-1-reference-infrastructure-owned-by-other-teams">Decision 1: Reference Infrastructure Owned by Other Teams
&lt;/h3>&lt;p>In our case, some infrastructure components already exist and are managed independently of the application platform.&lt;/p>
&lt;p>For example, the networking team may own the VPC and subnets used by our workloads. Having our Terraform configuration manage those resources directly would introduce overlapping responsibilities.&lt;/p>
&lt;p>For this reason, I prefer referencing externally managed resources through Terraform data sources when the corresponding AWS provider supports them.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-hcl" data-lang="hcl">&lt;span class="line">&lt;span class="cl">&lt;span class="k">data&lt;/span> &lt;span class="s2">&amp;#34;aws_vpc&amp;#34; &amp;#34;platform_vpc&amp;#34;&lt;/span> {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">filter&lt;/span> {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n"> name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;tag:Name&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n"> values&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;PROD-NETWORK-VPC&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">data&lt;/span> &lt;span class="s2">&amp;#34;aws_subnets&amp;#34; &amp;#34;application&amp;#34;&lt;/span> {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">filter&lt;/span> {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n"> name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;vpc-id&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n"> values&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="k">data&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">aws_vpc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">platform_vpc&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">id&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n"> tags&lt;/span> &lt;span class="o">=&lt;/span> {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n"> Tier&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;Private-App&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>This allows our Terraform configuration to consume existing infrastructure without assuming responsibility for its lifecycle.&lt;/p>
&lt;p>Of course, using data sources doesn&amp;rsquo;t eliminate dependencies. If another team changes or removes a referenced resource, our deployments may still be affected.&lt;/p>
&lt;p>But in our organizational structure, this separation better reflects how responsibilities are actually distributed.&lt;/p>
&lt;h2 id="2-separating-infrastructure-management-from-application-delivery">2. Separating Infrastructure Management from Application Delivery
&lt;/h2>&lt;p>Another challenge comes from how Terraform interacts with ECS application deployments.&lt;/p>
&lt;p>In our environment, application teams deliver new container images through internal GitLab CI/CD pipelines. These pipelines can register new ECS task definition revisions and update running services.&lt;/p>
&lt;p>Terraform, however, may still contain references to the task definitions used during initial provisioning.&lt;/p>
&lt;p>This creates a potential conflict.&lt;/p>
&lt;p>Consider the following scenario:&lt;/p>
&lt;ol>
&lt;li>Terraform provisions an ECS service using an initial task definition.&lt;/li>
&lt;li>An application pipeline deploys a new version and updates the ECS service.&lt;/li>
&lt;li>A platform engineer later runs Terraform to modify an unrelated infrastructure component.&lt;/li>
&lt;li>Terraform detects that the ECS service differs from its configuration and may attempt to restore the previously declared task definition.&lt;/li>
&lt;/ol>
&lt;p>This is not necessarily a Terraform problem. It is a consequence of having two different workflows attempting to control the same resource attribute.&lt;/p>
&lt;h3 id="decision-2-define-which-workflow-owns-each-attribute">Decision 2: Define Which Workflow Owns Each Attribute
&lt;/h3>&lt;p>For our environment, I&amp;rsquo;m considering a clearer separation between the infrastructure lifecycle and the application release lifecycle.&lt;/p>
&lt;p>Terraform remains responsible for provisioning and managing infrastructure components such as ECS clusters, capacity providers, networking configuration, load balancers, and the initial service structure.&lt;/p>
&lt;p>Application delivery pipelines manage new task definition revisions and ECS service deployments.&lt;/p>
&lt;p>One way to support this arrangement is to use Terraform&amp;rsquo;s &lt;code>ignore_changes&lt;/code> lifecycle argument:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-hcl" data-lang="hcl">&lt;span class="line">&lt;span class="cl">&lt;span class="k">resource&lt;/span> &lt;span class="s2">&amp;#34;aws_ecs_service&amp;#34; &amp;#34;application&amp;#34;&lt;/span> {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n"> name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;app-core-service&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n"> cluster&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">aws_ecs_cluster&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">platform&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">id&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n"> task_definition&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">aws_ecs_task_definition&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">initial&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">arn&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n"> desired_count&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="m">2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">lifecycle&lt;/span> {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n"> ignore_changes&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">task_definition&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">desired_count&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>In this configuration, Terraform does not attempt to reconcile changes to these attributes for an existing service during normal planning.&lt;/p>
&lt;p>There is an important trade-off here.&lt;/p>
&lt;p>By ignoring changes to &lt;code>task_definition&lt;/code>, we are intentionally giving up Terraform&amp;rsquo;s ability to detect and correct differences in that attribute. The deployment pipeline becomes responsible for managing it.&lt;/p>
&lt;p>The same applies to &lt;code>desired_count&lt;/code>, which should only be ignored if another mechanism, such as Application Auto Scaling or an operational workflow, is expected to manage the running task count.&lt;/p>
&lt;p>&lt;strong>I see &lt;code>ignore_changes&lt;/code> less as a workaround and more as an explicit ownership decision.&lt;/strong>&lt;/p>
&lt;p>It can be useful when responsibilities are well-defined, but it can also hide configuration drift if applied too broadly.&lt;/p>
&lt;h2 id="3-security-groups-that-reflect-the-actual-traffic-flow">3. Security Groups That Reflect the Actual Traffic Flow
&lt;/h2>&lt;p>Security group management is another area where ownership and infrastructure design intersect.&lt;/p>
&lt;p>Our application traffic follows a path that includes an institutional F5 WAF before reaching the AWS Application Load Balancer and ECS workloads.&lt;/p>
&lt;p>A simplified representation is:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">Client
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> v
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">F5 WAF
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> v
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Application Load Balancer
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> v
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ECS Services
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> +----&amp;gt; Database
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> +----&amp;gt; Shared Storage
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Given this topology, my preference is to make security group rules reflect the actual communication requirements between components.&lt;/p>
&lt;p>For example, where the network architecture supports it:&lt;/p>
&lt;ul>
&lt;li>The ALB accepts application traffic from the expected WAF endpoints.&lt;/li>
&lt;li>ECS tasks accept application traffic from the ALB security group.&lt;/li>
&lt;li>Database services accept connections only from the application components that require access.&lt;/li>
&lt;li>Shared storage access is limited to the appropriate workloads and protocols.&lt;/li>
&lt;/ul>
&lt;p>This approach helps reduce unnecessarily broad ingress permissions.&lt;/p>
&lt;p>However, it also requires understanding the infrastructure outside AWS, particularly how traffic reaches the ALB through the F5 appliances.&lt;/p>
&lt;p>Depending on the network architecture, preserving source addresses, NAT behavior, routing, and the availability of security group references can influence the implementation.&lt;/p>
&lt;p>For this reason, I don&amp;rsquo;t consider a generic security group template sufficient.&lt;/p>
&lt;p>The rules need to represent the actual traffic flow of our environment, not just an idealized architecture diagram.&lt;/p>
&lt;h2 id="4-reducing-manual-work-in-application-deployments">4. Reducing Manual Work in Application Deployments
&lt;/h2>&lt;p>One operational concern in our setup is the separation between application delivery and production infrastructure access.&lt;/p>
&lt;p>External application partners should not require broad AWS production permissions just to release a new version of their applications.&lt;/p>
&lt;p>At the same time, making every release dependent on a manual intervention from the infrastructure team introduces unnecessary operational work.&lt;/p>
&lt;p>The challenge is finding a balance between controlled access and deployment autonomy.&lt;/p>
&lt;h3 id="decision-3-use-internal-cicd-as-the-deployment-boundary">Decision 3: Use Internal CI/CD as the Deployment Boundary
&lt;/h3>&lt;p>An approach that makes sense for our environment is to use internal GitLab pipelines as the controlled interface for production deployments.&lt;/p>
&lt;p>The intended workflow is:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">Application Change
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> v
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">GitLab CI - Build &amp;amp; Test
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> v
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Container Image Publication
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> v
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Image Promotion / Synchronization
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> v
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Production Approval Gate
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> v
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Register ECS Task Revision
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> v
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">Update ECS Service
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Under this model, application teams interact with the deployment process rather than directly managing AWS production resources.&lt;/p>
&lt;p>The pipeline can use scoped IAM permissions, with short-lived credentials through OIDC where supported and configured.&lt;/p>
&lt;p>An approval gate can be applied according to our production change-control requirements.&lt;/p>
&lt;p>Terraform remains responsible for the infrastructure configuration, while the deployment pipeline handles application revisions.&lt;/p>
&lt;p>This separation does not eliminate the need for monitoring, rollback procedures, or permission management.&lt;/p>
&lt;p>But it potentially reduces repetitive operational tasks while maintaining the access boundaries required by our organization.&lt;/p>
&lt;h2 id="5-making-terraform-changes-more-predictable">5. Making Terraform Changes More Predictable
&lt;/h2>&lt;p>The main reason I want to revisit our Terraform practices is not simply to automate more resources.&lt;/p>
&lt;p>It is to make subsequent infrastructure changes easier to understand, review, and execute.&lt;/p>
&lt;p>In a production environment, a Terraform plan that contains unexpected changes to databases, application revisions, and networking resources is difficult to approve with confidence.&lt;/p>
&lt;p>A few practices appear particularly relevant to our situation.&lt;/p>
&lt;h3 id="separating-terraform-state-by-operational-responsibility">Separating Terraform State by Operational Responsibility
&lt;/h3>&lt;p>I prefer separating Terraform state according to meaningful infrastructure boundaries.&lt;/p>
&lt;p>For example:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">terraform-platform/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── environments/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── prod/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── 01-base-iam/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── 02-datastores/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── 03-compute-ecs/
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>This structure is only illustrative. The appropriate boundaries depend on resource dependencies, ownership, and how changes are approved.&lt;/p>
&lt;p>Separating state can reduce the scope of an individual plan and make changes easier to review.&lt;/p>
&lt;p>It does not guarantee complete isolation, however. Resources managed in different states may still depend on one another through IDs, outputs, networking, or application behavior.&lt;/p>
&lt;p>The goal is to reduce unnecessary coupling, not to assume that independent state files imply independent systems.&lt;/p>
&lt;h3 id="protecting-stateful-resources">Protecting Stateful Resources
&lt;/h3>&lt;p>For infrastructure such as production databases, accidental resource replacement is a concern.&lt;/p>
&lt;p>Terraform&amp;rsquo;s &lt;code>prevent_destroy&lt;/code> lifecycle argument is one safeguard worth considering.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-hcl" data-lang="hcl">&lt;span class="line">&lt;span class="cl">&lt;span class="k">resource&lt;/span> &lt;span class="s2">&amp;#34;aws_db_instance&amp;#34; &amp;#34;production&amp;#34;&lt;/span> {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n"> identifier&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;prod-db-postgres&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">lifecycle&lt;/span> {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n"> prevent_destroy&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kt">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>This prevents Terraform from planning the destruction of that resource while the lifecycle rule remains in the configuration.&lt;/p>
&lt;p>However, it is not a substitute for AWS deletion protection, backups, access controls, or change reviews.&lt;/p>
&lt;p>In particular, removing the resource configuration may also remove the lifecycle protection, so it should not be treated as an absolute safeguard.&lt;/p>
&lt;p>For our environment, I see it as one layer in a broader production protection strategy.&lt;/p>
&lt;h3 id="detecting-unexpected-changes">Detecting Unexpected Changes
&lt;/h3>&lt;p>Another practice I&amp;rsquo;m evaluating is running automated Terraform plans to identify differences between the declared configuration and the current infrastructure.&lt;/p>
&lt;p>For instance:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">terraform plan -detailed-exitcode
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>The exit codes can distinguish between an unchanged configuration, a plan containing proposed changes, and an execution error.&lt;/p>
&lt;p>One limitation is that not every difference represents unintended drift. Some changes may be expected, while others result from configuration updates rather than manual infrastructure modifications.&lt;/p>
&lt;p>A dedicated refresh-only plan can help identify changes in remote infrastructure:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">terraform plan -refresh-only -detailed-exitcode
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>In either case, the results still require interpretation.&lt;/p>
&lt;p>The objective is to make unexpected changes visible before they become part of a production deployment.&lt;/p>
&lt;h2 id="6-the-trade-offs-im-willing-to-make">6. The Trade-Offs I&amp;rsquo;m Willing to Make
&lt;/h2>&lt;p>Looking at these decisions together, the underlying theme is ownership.&lt;/p>
&lt;p>In our environment, there are several competing objectives:&lt;/p>
&lt;ul>
&lt;li>Infrastructure should remain reproducible and manageable through code.&lt;/li>
&lt;li>Application teams need a practical way to release software.&lt;/li>
&lt;li>Network and security teams must retain control over their respective components.&lt;/li>
&lt;li>Production changes need to remain auditable and predictable.&lt;/li>
&lt;/ul>
&lt;p>These objectives do not always align automatically.&lt;/p>
&lt;p>Using &lt;code>ignore_changes&lt;/code> means accepting that some resource attributes will no longer be reconciled by Terraform.&lt;/p>
&lt;p>Separating state means introducing explicit dependencies between independently managed infrastructure layers.&lt;/p>
&lt;p>Using CI/CD for production deployment means investing in secure pipeline identities, approval mechanisms, and recovery procedures.&lt;/p>
&lt;p>None of these choices comes without operational cost.&lt;/p>
&lt;p>But for the environment I&amp;rsquo;m working with, I believe these trade-offs are worth examining because they address the actual boundaries between our teams and systems.&lt;/p>
&lt;h2 id="final-thoughts">Final Thoughts
&lt;/h2>&lt;p>My intention is not to argue that every organization should structure Terraform this way.&lt;/p>
&lt;p>An environment where a single platform team owns networking, infrastructure, and application delivery may benefit from a completely different approach.&lt;/p>
&lt;p>The same applies to organizations using Kubernetes, GitOps-based workflows, or other deployment mechanisms.&lt;/p>
&lt;p>What I&amp;rsquo;m trying to improve in our context is the relationship between infrastructure provisioning and ongoing platform operations.&lt;/p>
&lt;p>Terraform should not necessarily stop being useful once the environment is created. At the same time, keeping Terraform involved in Day-2 operations requires deciding which parts of the infrastructure it should continue to control.&lt;/p>
&lt;p>For me, the most important takeaway is that &lt;strong>Day-2 infrastructure management is as much about defining ownership boundaries as it is about writing Terraform code.&lt;/strong>&lt;/p>
&lt;p>The next step is to validate these boundaries through actual operational changes, review the resulting Terraform plans, and adjust the approach based on what works in our environment.&lt;/p>
&lt;p>That is the direction I&amp;rsquo;m pursuing: not a universal Terraform architecture, but a more deliberate and predictable way of operating the platform I work with.&lt;/p></description></item></channel></rss>