<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rishav Dhar</title>
    <description>The latest articles on DEV Community by Rishav Dhar (@rdhar).</description>
    <link>https://dev.to/rdhar</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F164508%2F7e23ad9c-6ab2-4561-bb81-da8c7545676b.jpg</url>
      <title>DEV Community: Rishav Dhar</title>
      <link>https://dev.to/rdhar</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rdhar"/>
    <language>en</language>
    <item>
      <title>Enhance Terraform/Tofu Automation with GitHub Action</title>
      <dc:creator>Rishav Dhar</dc:creator>
      <pubDate>Mon, 21 Oct 2024 21:12:13 +0000</pubDate>
      <link>https://dev.to/rdhar/enhance-terraformtofu-automation-with-github-action-29be</link>
      <guid>https://dev.to/rdhar/enhance-terraformtofu-automation-with-github-action-29be</guid>
      <description>&lt;p&gt;Provisioning infrastructure-as-code (IaC) in a GitOps framework can feel like walking a tightrope: balancing pipeline security while trying to communicate changes clearly. This blog explores a GitHub Action I maintain—&lt;a href="https://github.com/DevSecTop/TF-via-PR" rel="noopener noreferrer"&gt;DevSecTop/TF-via-PR&lt;/a&gt;—which addresses common pitfalls to plan and apply IaC, including:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Summarize plan changes (with diff)&lt;/li&gt;
&lt;li&gt;Reuse plan file (with encryption)&lt;/li&gt;
&lt;li&gt;Apply on PR merge (before OR after)&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;&lt;br&gt;
Throughout this blog, 'TF' is used to reference both Terraform and OpenTofu interchangeably due to first-class support for both tools.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Summarize plan changes (with diff)
&lt;/h2&gt;

&lt;p&gt;While the plan should be transparent, reviewing 1000s of lines of planned changes is simply not feasible. On the other hand, a brief 1-liner like "Plan: 2 to add, 2 to change, 2 to destroy" fails to convey the scope of impact. So why not visualize the &lt;strong&gt;summary of changes&lt;/strong&gt; the same way Git does—with diff syntax highlighting.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8pmd7muto52bcydb9kx9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8pmd7muto52bcydb9kx9.png" alt="Figure: PR comment of TF plan with " width="800" height="618"&gt;&lt;/a&gt;&lt;em&gt;Figure: PR comment of TF plan with "Diff of changes" section expanded.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Right after the color-coded diff, there’s another collapsible section with the trimmed plan output for a more detailed view. Below that, we’ll find a &lt;strong&gt;direct link&lt;/strong&gt; to the full workflow log—handy for when the output exceeds a PR comment’s character limit. Speaking of workflows, the same output is attached to the job summary, even when used in &lt;strong&gt;matrix strategy&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzqijk60ynxoamr82u2op.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzqijk60ynxoamr82u2op.png" alt="Figure: Workflow log with command-specific job summary." width="800" height="919"&gt;&lt;/a&gt;&lt;em&gt;Figure: Workflow log with command-specific job summary.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is achieved with the following few lines of GitHub Action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;provision&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup TF&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hashicorp/setup-terraform@v3&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Provision TF&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;devsectop/tf-via-pr@v12&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;plan&lt;/span&gt;
          &lt;span class="na"&gt;arg-lock&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
          &lt;span class="na"&gt;arg-var-file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;env/dev.tfvars&lt;/span&gt;
          &lt;span class="na"&gt;working-directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stacks&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;By the way, you may have noticed the oddly-named "terraform[...]tfplan" artifact in the previous screenshot and wondered what it's all about.&lt;/p&gt;


&lt;h2&gt;
  
  
  Reuse plan file (with encryption)
&lt;/h2&gt;

&lt;p&gt;Too often, when a plan is approved for merge, the IaC pipeline is rerun to apply changes with auto-approve enabled. This can lead to unpredictable results, especially if configuration drift occurs due to changes made outside the workflow. Since both the code and pipeline are hosted on GitHub, we can take advantage of &lt;strong&gt;workflow artifacts&lt;/strong&gt; to store and reuse the plan file between runs.&lt;/p&gt;

&lt;p&gt;To ensure the triggered workflow picks up the correct plan file artifact, it needs a &lt;strong&gt;uniquely identifiable name&lt;/strong&gt; which accounts for variables, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tool: either &lt;code&gt;terraform&lt;/code&gt; or &lt;code&gt;tofu&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;PR number: so multiple PR branches can plan simultaneously without over-writing each other.&lt;/li&gt;
&lt;li&gt;CLI arguments: workspace, working directory, backend-config, var-file, and destroy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionally, we want to avoid the risk of exposing sensitive data by uploading the plan file as-is. Instead, the file should be &lt;strong&gt;encrypted with a secret&lt;/strong&gt; string before upload. Here's how we can add this to the GitHub Action workflow step from before.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Provision TF&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;devsectop/tf-via-pr@v12&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;plan&lt;/span&gt;
    &lt;span class="na"&gt;arg-lock&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event_name == 'push' }}&lt;/span&gt;
    &lt;span class="na"&gt;arg-var-file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;env/dev.tfvars&lt;/span&gt;
    &lt;span class="na"&gt;working-directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stacks&lt;/span&gt;
    &lt;span class="na"&gt;plan-encrypt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.PASSPHRASE }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;&lt;br&gt;
For a deeper dive into securing cloud provisioning pipelines, &lt;a href="https://dev.to/rdhar/secure-cloud-provisioning-pipeline-with-github-automation-27g4"&gt;check this blog out&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/rdhar" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F164508%2F7e23ad9c-6ab2-4561-bb81-da8c7545676b.jpg" alt="rdhar"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/rdhar/secure-cloud-provisioning-pipeline-with-github-automation-27g4" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Secure cloud provisioning pipeline with GitHub automation&lt;/h2&gt;
      &lt;h3&gt;Rishav Dhar ・ Oct 20 '24&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#infrastructureascode&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#terraform&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#githubactions&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#devops&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;br&gt;
&lt;/blockquote&gt;

&lt;p&gt;Speaking of reuse, it’s common for a PR to accumulate several commits before it’s ready to merge. In this case, plan updates can be rendered in one of two ways.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Update (default): the existing PR comment is &lt;strong&gt;updated in place&lt;/strong&gt;, complete with a revision history to track changes over time.&lt;/li&gt;
&lt;li&gt;Recreate: the existing PR comment is &lt;strong&gt;deleted and replaced&lt;/strong&gt; with a new one after each commit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F01k7mpi6rhfjnivyo608.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F01k7mpi6rhfjnivyo608.png" alt="Figure: PR comment revision history comparing plan and apply outputs." width="800" height="400"&gt;&lt;/a&gt;&lt;em&gt;Figure: PR comment revision history comparing plan and apply outputs.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;By now, you’ll have noticed that we’re applying changes with the same GitHub Action—the final piece of the &lt;del&gt;puzzle&lt;/del&gt; pipeline.&lt;/p&gt;




&lt;h2&gt;
  
  
  Apply on PR merge (before OR after)
&lt;/h2&gt;

&lt;p&gt;Whether you decide to apply IaC changes before or after merging, the workflow adapts to fit your needs. By using unique identifiers, we can retrieve the relevant plan file even if the PR branch has been pushed to the default branch, outside of 'pull_request' context. Here’s a complete workflow example to illustrate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;provision&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;        &lt;span class="c1"&gt;# Required to identify workflow run.&lt;/span&gt;
      &lt;span class="na"&gt;checks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;        &lt;span class="c1"&gt;# Required to add status summary.&lt;/span&gt;
      &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;       &lt;span class="c1"&gt;# Required to checkout repository.&lt;/span&gt;
      &lt;span class="na"&gt;pull-requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt; &lt;span class="c1"&gt;# Required to add comment and label.&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup TF&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hashicorp/setup-terraform@v3&lt;/span&gt;

      &lt;span class="c1"&gt;# Only plan by default, or apply with lock on merge.&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Provision TF&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;devsectop/tf-via-pr@v12&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event_name == 'push' &amp;amp;&amp;amp; 'apply' || 'plan' }}&lt;/span&gt;
          &lt;span class="na"&gt;arg-lock&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event_name == 'push' }}&lt;/span&gt;
          &lt;span class="na"&gt;arg-var-file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;env/dev.tfvars&lt;/span&gt;
          &lt;span class="na"&gt;working-directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stacks&lt;/span&gt;
          &lt;span class="na"&gt;plan-encrypt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.PASSPHRASE }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We're not limited to just these workflow triggers; it's compatible with 'merge_group' using a &lt;a href="https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/managing-a-merge-queue" rel="noopener noreferrer"&gt;merge queue&lt;/a&gt; to filter out failed apply attempts. Other triggers, like 'cron' (for scheduled drift checks) and 'workflow_dispatch' (for manual checks), are also supported, with their plan file shared between workflows.&lt;/p&gt;


&lt;h2&gt;
  
  
  Bonus Extras
&lt;/h2&gt;

&lt;p&gt;While this blog has primarily focused on planning and applying changes, we can also perform 'fmt' and 'validate' checks, along with 'workspace' selection. In fact, we can pass the full range of TF options and flags using the 'arg-' prefix, such as '&lt;code&gt;arg-auto-approve: true&lt;/code&gt;', '&lt;code&gt;arg-destroy: true&lt;/code&gt;', '&lt;code&gt;arg-workspace: dev&lt;/code&gt;', and '&lt;code&gt;arg-parallelism: 20&lt;/code&gt;'.&lt;/p&gt;

&lt;p&gt;For more complex workflows, detailed 'exitcode' and 'identifier' outputs are provided to help with decision chains or to integrate with linting  and security scans. You can find the full list of parameters documented in the &lt;a href="https://github.com/DevSecTop/TF-via-PR" rel="noopener noreferrer"&gt;Readme&lt;/a&gt;.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/OP5dev" rel="noopener noreferrer"&gt;
        OP5dev
      &lt;/a&gt; / &lt;a href="https://github.com/OP5dev/TF-via-PR" rel="noopener noreferrer"&gt;
        TF-via-PR
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Plan and apply Terraform/OpenTofu via PR automation, using best practices for secure and scalable IaC workflows.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;&lt;a href="https://github.com/hashicorp/setup-terraform" title="Terraform Compatible." rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/7131c6a8309c72bb4b54f33ba384aab0a55a699412546eafd3adf89b6ba54d54/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5465727261666f726d2d436f6d70617469626c652d3834344642413f6c6f676f3d7465727261666f726d266c6f676f436f6c6f723d7768697465" alt="Terraform Compatible"&gt;&lt;/a&gt;
&lt;a href="https://github.com/opentofu/setup-opentofu" title="OpenTofu Compatible." rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/6ade6417c983cd691f3b30f398f7f19d279b2423a8772db29044b9311bd012d9/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4f70656e546f66752d436f6d70617469626c652d4646444131383f6c6f676f3d6f70656e746f6675266c6f676f436f6c6f723d7768697465" alt="OpenTofu Compatible"&gt;&lt;/a&gt;
*
&lt;a href="https://github.com/OP5dev/TF-via-PRLICENSE" title="Apache License 2.0." rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/b528e29656e9f4607b439927ce4ef96e2c58f9fca1cbc864a196fc5097969d48/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f6f70356465762f74662d7669612d70723f6c6f676f3d617061636865266c6162656c3d4c6963656e7365" alt="GitHub license"&gt;&lt;/a&gt;
&lt;a href="https://github.com/op5dev/tf-via-pr/releases" title="View all releases." rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/0b690ee45f1d7ec50f4badf2d8082058e5cf437ca8820071f5cec5b603cd5a91/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f762f72656c656173652f6f70356465762f74662d7669612d70723f6c6f676f3d73656d616e74696372656c65617365266c6162656c3d52656c65617365" alt="GitHub release tag"&gt;&lt;/a&gt;
*
&lt;a href="https://github.com/op5dev/tf-via-pr" title="Become a stargazer." rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/c258833d0a61bdd8791640aac1ee79a1b6255ea16b7bf9c95d2be9773c799ac8/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f73746172732f6f70356465762f74662d7669612d7072" alt="GitHub repository stargazers"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Terraform/OpenTofu via Pull Request (TF-via-PR)&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;br&gt;
  &lt;tbody&gt;
&lt;br&gt;
&lt;tr&gt;
&lt;br&gt;
    &lt;th&gt;
&lt;br&gt;
      &lt;div class="markdown-heading"&gt;

&lt;h3 class="heading-element"&gt;What does it do?&lt;/h3&gt;


&lt;/div&gt;
&lt;br&gt;
    &lt;/th&gt;
&lt;br&gt;
    &lt;th&gt;
&lt;br&gt;
      &lt;div class="markdown-heading"&gt;

&lt;h3 class="heading-element"&gt;Who is it for?&lt;/h3&gt;


&lt;/div&gt;
&lt;br&gt;
    &lt;/th&gt;
&lt;br&gt;
  &lt;/tr&gt;
&lt;br&gt;
  &lt;tr&gt;
&lt;br&gt;
    &lt;td&gt;
&lt;br&gt;
      &lt;ul&gt;

        &lt;li&gt;Plan and apply changes with CLI arguments and &lt;strong&gt;encrypted plan file&lt;/strong&gt; to avoid configuration drift.&lt;/li&gt;

        &lt;li&gt;Outline diff within up-to-date &lt;strong&gt;PR comment&lt;/strong&gt; and matrix-friendly workflow summary, complete with log.&lt;/li&gt;

      &lt;/ul&gt;
&lt;br&gt;
    &lt;/td&gt;
&lt;br&gt;
    &lt;td&gt;
&lt;br&gt;
      &lt;ul&gt;

        &lt;li&gt;DevOps and Platform engineers wanting to empower their teams to &lt;strong&gt;self-service&lt;/strong&gt; scalably.&lt;/li&gt;

        &lt;li&gt;Maintainers looking to &lt;strong&gt;secure&lt;/strong&gt; their pipeline without the overhead of containers or VMs.&lt;/li&gt;

      &lt;/ul&gt;
&lt;br&gt;
    &lt;/td&gt;
&lt;br&gt;
  &lt;/tr&gt;
&lt;br&gt;
&lt;/tbody&gt;
&lt;br&gt;
&lt;/table&gt;&lt;/div&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;View: &lt;a href="https://github.com/OP5dev/TF-via-PR#usage-examples" rel="noopener noreferrer"&gt;Usage Examples&lt;/a&gt; · &lt;a href="https://github.com/OP5dev/TF-via-PR#inputs" rel="noopener noreferrer"&gt;Inputs&lt;/a&gt; · &lt;a href="https://github.com/OP5dev/TF-via-PR#outputs" rel="noopener noreferrer"&gt;Outputs&lt;/a&gt; · &lt;a href="https://github.com/OP5dev/TF-via-PR#security" rel="noopener noreferrer"&gt;Security&lt;/a&gt; · &lt;a href="https://github.com/OP5dev/TF-via-PR#changelog" rel="noopener noreferrer"&gt;Changelog&lt;/a&gt; · &lt;a href="https://github.com/OP5dev/TF-via-PR#license" rel="noopener noreferrer"&gt;License&lt;/a&gt;
&lt;/h3&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://raw.githubusercontent.com/op5dev/tf-via-pr/refs/heads/main/.github/assets/comment.png" title="View full-size image." rel="nofollow noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FOP5dev%2FTF-via-PR%2F.github%2Fassets%2Fcomment.png" alt='PR comment of plan output with "Diff of changes" section expanded.'&gt;&lt;/a&gt;&lt;/p&gt;



&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Usage Examples&lt;/h2&gt;

&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;How to get started?&lt;/h3&gt;

&lt;/div&gt;

&lt;div class="highlight highlight-source-yaml notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-ent"&gt;on&lt;/span&gt;
  &lt;span class="pl-ent"&gt;pull_request&lt;/span&gt;:
  &lt;span class="pl-ent"&gt;push&lt;/span&gt;:
    &lt;span class="pl-ent"&gt;branches&lt;/span&gt;: &lt;span class="pl-s"&gt;[main]&lt;/span&gt;

&lt;span class="pl-ent"&gt;jobs&lt;/span&gt;:
  &lt;span class="pl-ent"&gt;provision&lt;/span&gt;:
    &lt;span class="pl-ent"&gt;runs-on&lt;/span&gt;: &lt;span class="pl-s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="pl-ent"&gt;permissions&lt;/span&gt;:
      &lt;span class="pl-ent"&gt;actions&lt;/span&gt;: &lt;span class="pl-s"&gt;read        &lt;/span&gt;&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Required to identify workflow run.&lt;/span&gt;
      &lt;span class="pl-ent"&gt;checks&lt;/span&gt;: &lt;span class="pl-s"&gt;write        &lt;/span&gt;&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Required to add status summary.&lt;/span&gt;
      &lt;span class="pl-ent"&gt;contents&lt;/span&gt;: &lt;span class="pl-s"&gt;read       &lt;/span&gt;&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Required to checkout repository.&lt;/span&gt;
      &lt;span class="pl-ent"&gt;pull-requests&lt;/span&gt;: &lt;span class="pl-s"&gt;write &lt;/span&gt;&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Required to add PR comment.&lt;/span&gt;

    &lt;span class="pl-ent"&gt;steps&lt;/span&gt;:
      - &lt;span class="pl-ent"&gt;uses&lt;/span&gt;: &lt;span class="pl-s"&gt;actions/checkout@v4&lt;/span&gt;

      - &lt;span class="pl-ent"&gt;uses&lt;/span&gt;: &lt;span class="pl-s"&gt;hashicorp/setup-terraform@v3&lt;/span&gt;&lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/OP5dev/TF-via-PR" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;All forms of &lt;strong&gt;contribution are welcome&lt;/strong&gt; and appreciated for fostering open-source projects. Please feel free to open a discussion to share your ideas, or become a stargazer if you find this project useful.&lt;/p&gt;

&lt;p&gt;Whether it's interpolating dynamic backends, bulk-provisioning environments simultaneously, or triggering actions through labels and comments, this workflow offers plenty of flexibility. Is there a specific setup you'd like us to dive into next?&lt;/p&gt;

</description>
      <category>devops</category>
      <category>terraform</category>
      <category>infrastructureascode</category>
      <category>githubactions</category>
    </item>
    <item>
      <title>Secure cloud provisioning pipeline with GitHub automation</title>
      <dc:creator>Rishav Dhar</dc:creator>
      <pubDate>Sun, 20 Oct 2024 00:54:11 +0000</pubDate>
      <link>https://dev.to/rdhar/secure-cloud-provisioning-pipeline-with-github-automation-27g4</link>
      <guid>https://dev.to/rdhar/secure-cloud-provisioning-pipeline-with-github-automation-27g4</guid>
      <description>&lt;p&gt;As a member of the Platforms engineering team, we understand that security is a shared responsibility throughout the DevSecOps lifecycle for provisioning infrastructure. As a result, we set about championing best practices across the organization, with a focus on:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Configuring short-lived credentials&lt;/li&gt;
&lt;li&gt;Automating cloud-provisioning pipelines&lt;/li&gt;
&lt;li&gt;Comparing infrastructure-as-code tooling&lt;/li&gt;
&lt;li&gt;Securing deployments from code-to-delivery&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgc84x15xmdotd1zrkeds.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgc84x15xmdotd1zrkeds.png" alt="Figure: How to provision infrastructure-as-code." width="607" height="289"&gt;&lt;/a&gt;&lt;em&gt;Figure: How to provision infrastructure-as-code.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Short-lived credentials
&lt;/h2&gt;

&lt;p&gt;GitHub Actions form the basis of our continuous integration/continuous deployment (CI/CD) pipeline as it integrates seamlessly with GitOps: the framework by which we ship peer-reviewed code early and often. It enables us to extend our workflow with Actions from verified creators, such as &lt;a href="https://github.com/aws-actions/configure-aws-credentials" rel="noopener noreferrer"&gt;aws-actions&lt;/a&gt; (other cloud computing platforms are available).&lt;/p&gt;

&lt;p&gt;This streamlines the process of configuring AWS credentials via OpenID Connect (OIDC): our favorite “keyless” authentication method. OIDC only permits access from federated providers using short-lived credentials, which pairs perfectly with GitHub Actions’ ephemeral runners. What’s more, we can grant least-privilege provisioning permissions to the &lt;a href="https://github.blog/security/supply-chain-security/secure-deployments-openid-connect-github-actions-generally-available/" rel="noopener noreferrer"&gt;assumed-role&lt;/a&gt; (and include &lt;a href="https://github.blog/changelog/2023-06-27-github-actions-update-on-oidc-integration-with-aws/" rel="noopener noreferrer"&gt;both thumbprints&lt;/a&gt;). Let’s add it to our workflow.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;&lt;br&gt;
While the following pseudo-code is for legibility, we will build up to the complete workflow by the end of this post.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;provision&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Authenticate AWS&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-actions/configure-aws-credentials&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcjhfeh7tmrfsusggta57.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcjhfeh7tmrfsusggta57.png" alt="Figure: Security hardening GitHub Action workflow with OpenID Connect." width="593" height="198"&gt;&lt;/a&gt;&lt;em&gt;Figure: Security hardening GitHub Action workflow with OpenID Connect (&lt;a href="https://github.blog/changelog/2021-10-27-github-actions-secure-cloud-deployments-with-openid-connect/" rel="noopener noreferrer"&gt;source&lt;/a&gt;).&lt;/em&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Automated provisioning pipeline
&lt;/h2&gt;

&lt;p&gt;With authentication sorted and access to infrastructure secured, we turned to &lt;a href="https://testdouble.com/insights/github-actions-terraform-automation" rel="noopener noreferrer"&gt;Test Double&lt;/a&gt; for inspiration on a “roll-your-own” deployment workflow.&lt;/p&gt;

&lt;p&gt;It goes on to link &lt;a href="https://github.com/OP5dev/TF-via-PR" rel="noopener noreferrer"&gt;OP5dev/TF-via-PR&lt;/a&gt;: another open-source project which combines the outlined concepts into a reusable Action. In support of trunk-based Pull Request (PR) automation, this workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Runs &lt;code&gt;init &amp;gt; workspace &amp;gt; plan&lt;/code&gt; with optional arguments when a PR is opened, returning the plan output in a PR comment and storing the encrypted plan file as an artifact.&lt;/li&gt;
&lt;li&gt;Reruns &lt;code&gt;plan&lt;/code&gt; when the PR is updated with new commits, returning the latest output and replacing the old PR comment to reduce noise.&lt;/li&gt;
&lt;li&gt;Runs &lt;code&gt;init &amp;gt; workspace &amp;gt; apply&lt;/code&gt; with the previous plan file artifact, along with optional arguments when the PR is approved and merged.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;&lt;br&gt;
Passing in the previously-generated plan file when applying prevents any configuration drift from stale plans.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;By default, each PR and associated workflow runs hold a complete log of infrastructure change/proposals for ease of collaboration and audit compliance within a set retention period. The use of GitHub Actions also removes the overhead of maintaining dedicated containers or self-hosted compute instances: lending itself to empower development teams to self-service scalably. Time to add another step to our workflow.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;provision&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Authenticate AWS&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-actions/configure-aws-credentials&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Provision infrastructure&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;op5dev/tf-via-pr&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5zupek91pql6hb0cqavx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5zupek91pql6hb0cqavx.png" alt="Figure: Flow chart of provisioning pipeline steps." width="450" height="131"&gt;&lt;/a&gt;&lt;em&gt;Figure: Flow chart of provisioning pipeline steps.&lt;/em&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Infrastructure-as-code tooling
&lt;/h2&gt;

&lt;p&gt;Although plan file encryption is one of the options provided by the Action, it’s also a feature native to &lt;a href="https://opentofu.org/" rel="noopener noreferrer"&gt;OpenTofu&lt;/a&gt;: a project backed by The Linux Foundation as an open-source alternative to Terraform.&lt;/p&gt;

&lt;p&gt;More recently, OpenTofu released support for &lt;a href="https://opentofu.org/docs/intro/whats-new/#early-variablelocals-evaluation" rel="noopener noreferrer"&gt;early static evaluation&lt;/a&gt; of variables/locals, enabling the likes of module versions and backend inputs to be loosely coupled with don’t-repeat-yourself (DRY) configuration. While a number of teams have successfully experimented by replacing terraform with tofu, many others are holding out for &lt;a href="https://github.com/opentofu/opentofu/issues/300" rel="noopener noreferrer"&gt;dynamically configured provider&lt;/a&gt; support as well as &lt;a href="https://github.com/opentofu/opentofu/issues/1236" rel="noopener noreferrer"&gt;Dependabot automation&lt;/a&gt; before making the switch.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;&lt;br&gt;
For anyone else experimenting between the two, whole-heartedly recommend &lt;a href="https://github.com/tofuutils/tenv" rel="noopener noreferrer"&gt;tofuutils/tenv&lt;/a&gt; as the go-to package manager of choice with broad compatibility.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Secured deployments workflow
&lt;/h2&gt;

&lt;p&gt;With the GitHub Action dependencies accounted for, all that’s left is to bring them together in a workflow that is reactive to the provisioning result. In other words, if apply fails for any reason, then reject the PR from merge and return the error output in a PR comment for follow-up. This happens to be the ideal use-case for a &lt;a href="https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/managing-a-merge-queue" rel="noopener noreferrer"&gt;merge queue&lt;/a&gt; of size and concurrency one.&lt;/p&gt;

&lt;p&gt;Now that we have all the pieces to form the complete workflow below, can you spot what else has been done to shore up security? Answers below!&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;merge_group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;checks_requested&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;provision&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-24.04&lt;/span&gt;

    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;        &lt;span class="c1"&gt;# Required to download artifact.&lt;/span&gt;
      &lt;span class="na"&gt;checks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;        &lt;span class="c1"&gt;# Required to add status summary.&lt;/span&gt;
      &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;       &lt;span class="c1"&gt;# Required to checkout repository.&lt;/span&gt;
      &lt;span class="na"&gt;id-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;      &lt;span class="c1"&gt;# Required to authenticate via OIDC.&lt;/span&gt;
      &lt;span class="na"&gt;pull-requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt; &lt;span class="c1"&gt;# Required to add PR comment and label.&lt;/span&gt;

    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;AWS_ACCOUNT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;platforms-dev&lt;/span&gt;
      &lt;span class="na"&gt;AWS_REGION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-east-1&lt;/span&gt;
      &lt;span class="na"&gt;AWS_ROLE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;arn:aws:iam::123456789012:role/provision-cicd&lt;/span&gt;

    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event_name == 'merge_group' &amp;amp;&amp;amp; env.AWS_ACCOUNT || '' }}&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Authenticate AWS&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502&lt;/span&gt; &lt;span class="c1"&gt;# v4.0.2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;aws-region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.AWS_REGION }}&lt;/span&gt;
          &lt;span class="na"&gt;role-to-assume&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.AWS_ROLE }}&lt;/span&gt;

     &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout repository&lt;/span&gt;
       &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@d632683dd7b4114ad314bca15554477dd762a938&lt;/span&gt; &lt;span class="c1"&gt;# v4.2.0&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;persist-credentials&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup TF&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd&lt;/span&gt; &lt;span class="c1"&gt;# v3.1.2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;terraform_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.8.5"&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Provision infrastructure&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;op5dev/tf-via-pr@f455e10e6e83d28f53f505a2d8242433a17536f3&lt;/span&gt; &lt;span class="c1"&gt;# v13.0.0&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event_name == 'merge_group' &amp;amp;&amp;amp; 'apply' || 'plan' }}&lt;/span&gt;
          &lt;span class="na"&gt;arg-lock&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event_name == 'merge_group' }}&lt;/span&gt;
          &lt;span class="na"&gt;working-directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;path/to/${{ env.AWS_ACCOUNT }}&lt;/span&gt;
          &lt;span class="na"&gt;plan-encrypt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.TF_ENCRYPTION }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Make Actions immutable by &lt;a href="https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions#using-third-party-actions" rel="noopener noreferrer"&gt;pinning to a commit SHA&lt;/a&gt; to reduce third-party supply-chain risks (and automate updates with &lt;a href="https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuring-dependabot-version-updates#enabling-dependabot-version-updates" rel="noopener noreferrer"&gt;Dependabot&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;environment:&lt;/code&gt; to enforce &lt;a href="https://docs.github.com/en/actions/managing-workflow-runs-and-deployments/managing-deployments/managing-environments-for-deployment#deployment-protection-rules" rel="noopener noreferrer"&gt;deployment protection rules&lt;/a&gt; when attempting to apply changes to infrastructure.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;persist-credentials: false&lt;/code&gt; with checkout to &lt;a href="https://github.com/actions/checkout/issues/485" rel="noopener noreferrer"&gt;prevent leaking the GitHub token&lt;/a&gt; to subsequent steps of the workflow.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;&lt;br&gt;
For a deeper dive into TF-via-PR GitHub Action, &lt;a href="https://dev.to/rdhar/enhance-terraformtofu-automation-with-github-action-29be"&gt;check this blog out&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/rdhar" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F164508%2F7e23ad9c-6ab2-4561-bb81-da8c7545676b.jpg" alt="rdhar"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/rdhar/enhance-terraformtofu-automation-with-github-action-29be" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Enhance Terraform/Tofu Automation with GitHub Action&lt;/h2&gt;
      &lt;h3&gt;Rishav Dhar ・ Oct 21 '24&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#devops&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#terraform&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#infrastructureascode&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#githubactions&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;br&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;There’s plenty of potential with this workflow: from interpolating workspaces with dynamic backends to bulk-provisioning multiple accounts in matrix strategy concurrently. Is there any particular setup or tooling you’d like us to explore next?&lt;/p&gt;

</description>
      <category>infrastructureascode</category>
      <category>terraform</category>
      <category>githubactions</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
