<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>python &amp;mdash; For The Love Of Cloud!</title>
    <link>https://blog.chelo.dev/tag:python</link>
    <description></description>
    <pubDate>Fri, 01 May 2026 07:38:11 -0600</pubDate>
    <item>
      <title>Exporting Terraform Plan to Excel with Python</title>
      <link>https://blog.chelo.dev/exporting-terraform-plan-to-excel-with-python</link>
      <description>&lt;![CDATA[#terraform #python #devops&#xA;&#xA;Recently, I was working with an Azure design for a customer, and I needed to compare what my terraform scripts had planned for against my customer&#39;s Excel template.!--more--&#xA;&#xA;GitHub Repository&#xA;&#xA;Table of Contents&#xA;Terraform Prep&#xA;Python Script&#xA;  Parsing arguments&#xA;  Executing Terraform&#xA;  Parsing the Terraform plan&#xA;  Final Script&#xA;Usage&#xA;Final thoughts&#xA;&#xA;If we were talking about a few resources with static names and properties, it wouldn&#39;t be too hard to comb through the terraform scripts and compare them against the Excel template. However, the customer&#39;s design had about 60+ resources, distributed in 15 Resource Groups.&#xA;&#xA;So, I decided to do everything harder (now) so that it was easier (later), and started writing a Python script that would create a Terraform Plan, parse it and then generate an Excel file with all the resources grouped by type in different worksheets.&#xA;&#xA;Terraform Prep&#xA;&#xA;First of all, since my customer was using Azure as the Terraform backend and deploying via Azure DevOps agent, I had to do some prepping in the form of a Terraform override file so that I could execute my plan locally and get a full rundown as if there were no deployed resources.&#xA;&#xA;Since it was mainly backend and provider, what I had to override, I named my file backendoverride.tf, which I placed in the same folder where the rest of my Terraform scripts were.&#xA;&#xA;The file ended looking something like this:&#xA;terraform {&#xA;  backend &#34;local&#34; {&#xA;    path = &#34;./.local-state&#34;&#xA;  }&#xA;}&#xA;&#xA;provider &#34;azurerm&#34; {&#xA;  features {}&#xA;  clientid       = &#34;00000000-0000-0000-0000-000000000000&#34;&#xA;  clientsecret   = &#34;MySuperSecretPassword&#34;&#xA;  tenantid       = &#34;10000000-0000-0000-0000-000000000000&#34;&#xA;  subscriptionid = &#34;20000000-0000-0000-0000-000000000000&#34;&#xA;}&#xA;&#xA;Python Script&#xA;&#xA;The script has two main parts:&#xA; Terraform planning&#xA; Plan flattener and Excel file creation&#xA;&#xA;Parsing arguments&#xA;&#xA;First, I had to write the base for the script. The script would receive a couple of arguments:&#xA;&#xA;| Argument | Description | Optional | Example |&#xA;|---|---|---|---|&#xA;| --tfpath | The path to the folder that contains the terraform files | false | --tfpath &#34;terraform/&#34; |&#xA;| --set | The variables that would normally be passed via command line o additional tfvars files | true | --set location=&#34;Central US&#34; testing=true  |&#xA;&#xA;This arguments are parsed using argparse and saving the Terraform path to tfpath and the variables as a dict in vars.&#xA;  I based this part on Sam Starkman&#39;s article and Laurent Franceschetti&#39;s gist&#xA;&#xA;import argparse&#xA;&#xA;def parsevar(s):&#xA;    items = s.split(&#39;=&#39;)&#xA;    key = items[0].strip()&#xA;    if len(items)   1:&#xA;        value = &#39;=&#39;.join(items[1:])&#xA;    return (key, value)&#xA;&#xA;def parsevars(items):&#xA;    d = {}&#xA;&#xA;    if items:&#xA;        for item in items:&#xA;            key, value = parsevar(item)&#xA;            d[key] = value&#xA;    return d&#xA;&#xA;vars = {}&#xA;parser = argparse.ArgumentParser(description=&#34;...&#34;)&#xA;parser.addargument(&#34;--set&#34;,&#xA;                        metavar=&#34;KEY=VALUE&#34;,&#xA;                        nargs=&#39;+&#39;)&#xA;parser.addargument(&#34;--tfpath&#34;,&#xA;                    type=str,&#xA;                    required=True)&#xA;args = parser.parseargs()&#xA;vars = parsevars(args.set)&#xA;&#xA;tfpath = args.tfpath&#xA;&#xA;Executing Terraform&#xA;&#xA;To execute Terraform, I used the pythonterraform library. I generated the plan, passing vars as an argument value for var, saved it first to plan.tfplan and the read it as a JSON into the variable plan. The code to generate the Terraform plan is really simple:&#xA;&#xA;from pythonterraform import &#xA;import json&#xA;&#xA;tf = Terraform(workingdir=tfpath)&#xA;tf.init()&#xA;tf.plan(out=&#34;plan.tfplan&#34;,var=vars)&#xA;jsondata = tf.show(&#34;plan.tfplan&#34;,json=IsFlagged)&#xA;&#xA;plan = json.loads(jsondata[1])&#xA;&#xA;Parsing the Terraform plan&#xA;&#xA;Finally, I needed to parse the plan, especifically resourcechanges. Since it contained everything from null and false, all the way to list and dict values, I decided to do a recursive function (flattener) that would iterate through all the resources.&#xA;&#xA;  The bit where I get the current directory, for the Excel file name, is based on vinithravit&#39;s answer over at StackOverflow.&#xA;&#xA;import json&#xA;import xlsxwriter&#xA;&#xA;def ofname(tfpath=&#34;.&#34;,extension=&#34;.xlsx&#34;):&#xA;    os.chdir(tfpath)&#xA;    str1=os.getcwd()&#xA;    str2=str1.split(&#39;/&#39;)&#xA;    n=len(str2)&#xA;    name = str2[n-1] + extension&#xA;    return name&#xA;&#xA;def flattener(jdata,row,column,worksheet,itemc):&#xA;    if isinstance(jdata,dict):&#xA;        for k,v in jdata.items():&#xA;            if isinstance(v,dict) or isinstance(v,list):&#xA;                worksheet.write(row,column,k)&#xA;                if isinstance(v,list) and len(v) == 1 :&#xA;                    row = flattener(v[0],row,column+1,worksheet,len(v))&#xA;                else:&#xA;                    row = flattener(v,row,column+1,worksheet,len(v))&#xA;            else:&#xA;                worksheet.write(row,column,k)&#xA;                worksheet.write(row,column+1,v)&#xA;                row = row + 1&#xA;    else:&#xA;        for v in jdata:&#xA;            if isinstance(v,dict) or isinstance(v,list):&#xA;                row = flattener(v,row,column,worksheet,len(v))&#xA;            else:&#xA;                worksheet.write(row,column,v)&#xA;                row = row + 1&#xA;    return row&#xA;&#xA;classed = {}&#xA;&#xA;for rc in plan[&#39;resourcechanges&#39;]:&#xA;    classed[rc[&#34;type&#34;]] = {}&#xA;&#xA;for rc in plan[&#39;resourcechanges&#39;]:&#xA;    rcdict = rc&#39;change&#39;&#xA;    rcdict[&#39;address&#39;] = rc[&#39;address&#39;]&#xA;    rcdict[&#39;type&#39;] = rc[&#39;type&#39;]&#xA;    classed[rc[&#34;type&#34;]].update({rc[&#39;address&#39;]: rcdict})&#xA;&#xA;workbook = xlsxwriter.Workbook(ofname(tfpath))&#xA;cellformat = workbook.addformat()&#xA;cellformat.settextwrap()&#xA;cellformat.setalign(&#34;vcenter&#34;)&#xA;for type,data in classed.items():&#xA;    sheet = type[:31]&#xA;    worksheet = workbook.addworksheet(sheet)&#xA;    worksheet.setcolumn(0,1000,42,cellformat)&#xA;    flattener(data,1,0,worksheet,0)&#xA;workbook.close()&#xA;&#xA;Final Script&#xA;&#xA;Putting everything together, plus a couple of minor adjustments (like the addition of tfcheck [a small bash script that I wrote to validate Terraform scripts] and rm plan.tfplan for cleaning up), the script ends up as follows:&#xA;&#xA;from pythonterraform import &#xA;import json&#xA;import os&#xA;import xlsxwriter&#xA;import argparse&#xA;&#xA;def parsevar(s):&#xA;    items = s.split(&#39;=&#39;)&#xA;    key = items[0].strip()&#xA;    if len(items)   1:&#xA;        value = &#39;=&#39;.join(items[1:])&#xA;    return (key, value)&#xA;&#xA;def parsevars(items):&#xA;    d = {}&#xA;&#xA;    if items:&#xA;        for item in items:&#xA;            key, value = parsevar(item)&#xA;            d[key] = value&#xA;    return d&#xA;&#xA;def ofname(tfpath=&#34;.&#34;,extension=&#34;.xlsx&#34;):&#xA;    os.chdir(tfpath)&#xA;    str1=os.getcwd()&#xA;    str2=str1.split(&#39;/&#39;)&#xA;    n=len(str2)&#xA;    name = str2[n-1] + extension&#xA;    return name&#xA;&#xA;def flattener(jdata,row,column,worksheet,itemc):&#xA;    if isinstance(jdata,dict):&#xA;        for k,v in jdata.items():&#xA;            if isinstance(v,dict) or isinstance(v,list):&#xA;                worksheet.write(row,column,k)&#xA;                if isinstance(v,list) and len(v) == 1 :&#xA;                    row = flattener(v[0],row,column+1,worksheet,len(v))&#xA;                else:&#xA;                    row = flattener(v,row,column+1,worksheet,len(v))&#xA;            else:&#xA;                worksheet.write(row,column,k)&#xA;                worksheet.write(row,column+1,v)&#xA;                row = row + 1&#xA;    else:&#xA;        for v in jdata:&#xA;            if isinstance(v,dict) or isinstance(v,list):&#xA;                row = flattener(v,row,column,worksheet,len(v))&#xA;            else:&#xA;                worksheet.write(row,column,v)&#xA;                row = row + 1&#xA;    return row&#xA;&#xA;vars = {}&#xA;parser = argparse.ArgumentParser(description=&#34;...&#34;)&#xA;parser.addargument(&#34;--set&#34;,&#xA;                        metavar=&#34;KEY=VALUE&#34;,&#xA;                        nargs=&#39;+&#39;)&#xA;parser.addargument(&#34;--tfpath&#34;,&#xA;                    type=str,&#xA;                    required=True)&#xA;args = parser.parseargs()&#xA;vars = parsevars(args.set)&#xA;&#xA;tfpath = args.tfpath&#xA;&#xA;tf = Terraform(workingdir=tfpath)&#xA;tf.init()&#xA;tf.plan(out=&#34;plan.tfplan&#34;,var=vars)&#xA;jsondata = tf.show(&#34;plan.tfplan&#34;,json=IsFlagged)&#xA;&#xA;plan = json.loads(jsondata[1])&#xA;&#xA;classed = {}&#xA;&#xA;for rc in plan[&#39;resourcechanges&#39;]:&#xA;    classed[rc[&#34;type&#34;]] = {}&#xA;&#xA;for rc in plan[&#39;resourcechanges&#39;]:&#xA;    rcdict = rc&#39;change&#39;&#xA;    rcdict[&#39;address&#39;] = rc[&#39;address&#39;]&#xA;    rcdict[&#39;type&#39;] = rc[&#39;type&#39;]&#xA;    classed[rc[&#34;type&#34;]].update({rc[&#39;address&#39;]: rcdict})&#xA;&#xA;workbook = xlsxwriter.Workbook(ofname(tfpath))&#xA;cellformat = workbook.addformat()&#xA;cellformat.settextwrap()&#xA;cellformat.setalign(&#34;vcenter&#34;)&#xA;for type,data in classed.items():&#xA;    sheet = type[:31]&#xA;    worksheet = workbook.addworksheet(sheet)&#xA;    worksheet.setcolumn(0,1000,42,cell_format)&#xA;    flattener(data,1,0,worksheet,0)&#xA;workbook.close()&#xA;&#xA;os.system(&#34;tfcheck&#34;)&#xA;os.system(&#34;rm plan.tfplan&#34;)&#xA;&#xA;I&#39;m no Python expert, by any means, and I&#39;m sure that this script can be improved and optimized.&#xA;&#xA;Usage&#xA;&#xA;Now, how do we use this script? Pretty easily. Once we&#39;ve created our override file for Terraform, we simply run the script, passing the arguments we require:&#xA;&#xA;python3 main.py --tfpath terraform/ --set location=&#34;Central US&#34; testing=true&#xA;&#xA;Final thoughts&#xA;&#xA;It&#39;s been over a year since my last post... And what a year it has been! My baby daughter was born recently, I got a new job, my grandma died...&#xA;&#xA;Anyway, I got several ideas I&#39;d like to share with you, so I&#39;ll try to post more frequently.&#xA;&#xA;See you soon! (Hopefully)]]&gt;</description>
      <content:encoded><![CDATA[<p><a href="https://blog.chelo.dev/tag:terraform" class="hashtag"><span>#</span><span class="p-category">terraform</span></a> <a href="https://blog.chelo.dev/tag:python" class="hashtag"><span>#</span><span class="p-category">python</span></a> <a href="https://blog.chelo.dev/tag:devops" class="hashtag"><span>#</span><span class="p-category">devops</span></a></p>

<p>Recently, I was working with an Azure design for a customer, and I needed to compare what my terraform scripts had planned for against my customer&#39;s Excel template.</p>

<p><a href="https://github.com/peanutsguy/terraformplan2excel">GitHub Repository</a></p>

<h3 id="table-of-contents">Table of Contents</h3>
<ul><li><a href="#terraform-prep">Terraform Prep</a></li>
<li><a href="#python-script">Python Script</a>
<ul><li><a href="#parsing-arguments">Parsing arguments</a></li>
<li><a href="#executing-terraform">Executing Terraform</a></li>
<li><a href="#parsing-the-terraform-plan">Parsing the Terraform plan</a></li>
<li><a href="#final-script">Final Script</a></li></ul></li>
<li><a href="#usage">Usage</a></li>
<li><a href="#final-thoughts">Final thoughts</a></li></ul>

<p>If we were talking about a few resources with static names and properties, it wouldn&#39;t be too hard to comb through the terraform scripts and compare them against the Excel template. However, the customer&#39;s design had about 60+ resources, distributed in 15 Resource Groups.</p>

<p>So, I decided to do everything harder (now) so that it was easier (later), and started writing a Python script that would create a Terraform Plan, parse it and then generate an Excel file with all the resources grouped by type in different worksheets.</p>

<h2 id="terraform-prep">Terraform Prep</h2>

<p>First of all, since my customer was using Azure as the Terraform backend and deploying via Azure DevOps agent, I had to do some prepping in the form of a <a href="https://developer.hashicorp.com/terraform/language/files/override">Terraform override file</a> so that I could execute my plan locally and get a full rundown as if there were no deployed resources.</p>

<p>Since it was mainly backend and provider, what I had to override, I named my file <code>backend_override.tf</code>, which I placed in the same folder where the rest of my Terraform scripts were.</p>

<p>The file ended looking something like this:</p>

<pre><code class="language-json">terraform {
  backend &#34;local&#34; {
    path = &#34;./.local-state&#34;
  }
}

provider &#34;azurerm&#34; {
  features {}
  client_id       = &#34;00000000-0000-0000-0000-000000000000&#34;
  client_secret   = &#34;MySuperSecretPassword&#34;
  tenant_id       = &#34;10000000-0000-0000-0000-000000000000&#34;
  subscription_id = &#34;20000000-0000-0000-0000-000000000000&#34;
}
</code></pre>

<h2 id="python-script">Python Script</h2>

<p>The script has two main parts:
 – Terraform planning
 – Plan flattener and Excel file creation</p>

<h3 id="parsing-arguments">Parsing arguments</h3>

<p>First, I had to write the base for the script. The script would receive a couple of arguments:</p>

<table>
<thead>
<tr>
<th>Argument</th>
<th>Description</th>
<th>Optional</th>
<th>Example</th>
</tr>
</thead>

<tbody>
<tr>
<td><code>--tfpath</code></td>
<td>The path to the folder that contains the terraform files</td>
<td><code>false</code></td>
<td><code>--tfpath &#34;terraform/&#34;</code></td>
</tr>

<tr>
<td><code>--set</code></td>
<td>The variables that would normally be passed via command line o additional <code>tfvars</code> files</td>
<td><code>true</code></td>
<td><code>--set location=&#34;Central US&#34; testing=true</code></td>
</tr>
</tbody>
</table>

<p>This arguments are parsed using <code>argparse</code> and saving the Terraform path to <code>tfpath</code> and the variables as a <code>dict</code> in <code>vars</code>.
&gt; I based this part on Sam Starkman&#39;s <a href="https://towardsdatascience.com/a-simple-guide-to-command-line-arguments-with-argparse-6824c30ab1c3">article</a> and Laurent Franceschetti&#39;s <a href="https://gist.github.com/fralau/061a4f6c13251367ef1d9a9a99fb3e8d">gist</a></p>

<pre><code class="language-python">import argparse

def parse_var(s):
    items = s.split(&#39;=&#39;)
    key = items[0].strip()
    if len(items) &gt; 1:
        value = &#39;=&#39;.join(items[1:])
    return (key, value)


def parse_vars(items):
    d = {}

    if items:
        for item in items:
            key, value = parse_var(item)
            d[key] = value
    return d

vars = {}
parser = argparse.ArgumentParser(description=&#34;...&#34;)
parser.add_argument(&#34;--set&#34;,
                        metavar=&#34;KEY=VALUE&#34;,
                        nargs=&#39;+&#39;)
parser.add_argument(&#34;--tfpath&#34;,
                    type=str,
                    required=True)
args = parser.parse_args()
vars = parse_vars(args.set)

tfpath = args.tfpath
</code></pre>

<h3 id="executing-terraform">Executing Terraform</h3>

<p>To execute Terraform, I used the <a href="https://github.com/beelit94/python-terraform/blob/master/README.md"><code>python_terraform</code></a> library. I generated the plan, passing <code>vars</code> as an argument value for <code>var</code>, saved it first to <code>plan.tfplan</code> and the read it as a JSON into the variable <code>plan</code>. The code to generate the Terraform plan is really simple:</p>

<pre><code class="language-python">from python_terraform import *
import json

tf = Terraform(working_dir=tfpath)
tf.init()
tf.plan(out=&#34;plan.tfplan&#34;,var=vars)
json_data = tf.show(&#34;plan.tfplan&#34;,json=IsFlagged)

plan = json.loads(json_data[1])
</code></pre>

<h3 id="parsing-the-terraform-plan">Parsing the Terraform plan</h3>

<p>Finally, I needed to parse the plan, especifically <code>resource_changes</code>. Since it contained everything from <code>null</code> and <code>false</code>, all the way to <code>list</code> and <code>dict</code> values, I decided to do a recursive function (<code>flattener</code>) that would iterate through all the resources.</p>

<blockquote><p>The bit where I get the current directory, for the Excel file name, is based on <a href="https://stackoverflow.com/a/10293159">vinithravit&#39;s answer</a> over at StackOverflow.</p></blockquote>

<pre><code class="language-python">import json
import xlsxwriter

def ofname(tfpath=&#34;.&#34;,extension=&#34;.xlsx&#34;):
    os.chdir(tfpath)
    str1=os.getcwd()
    str2=str1.split(&#39;/&#39;)
    n=len(str2)
    name = str2[n-1] + extension
    return name

def flattener(jdata,row,column,worksheet,itemc):
    if isinstance(jdata,dict):
        for k,v in jdata.items():
            if isinstance(v,dict) or isinstance(v,list):
                worksheet.write(row,column,k)
                if isinstance(v,list) and len(v) == 1 :
                    row = flattener(v[0],row,column+1,worksheet,len(v))
                else:
                    row = flattener(v,row,column+1,worksheet,len(v))
            else:
                worksheet.write(row,column,k)
                worksheet.write(row,column+1,v)
                row = row + 1
    else:
        for v in jdata:
            if isinstance(v,dict) or isinstance(v,list):
                row = flattener(v,row,column,worksheet,len(v))
            else:
                worksheet.write(row,column,v)
                row = row + 1
    return row

classed = {}

for rc in plan[&#39;resource_changes&#39;]:
    classed[rc[&#34;type&#34;]] = {}

for rc in plan[&#39;resource_changes&#39;]:
    rc_dict = rc[&#39;change&#39;][&#39;after&#39;]
    rc_dict[&#39;address&#39;] = rc[&#39;address&#39;]
    rc_dict[&#39;type&#39;] = rc[&#39;type&#39;]
    classed[rc[&#34;type&#34;]].update({rc[&#39;address&#39;]: rc_dict})

workbook = xlsxwriter.Workbook(ofname(tfpath))
cell_format = workbook.add_format()
cell_format.set_text_wrap()
cell_format.set_align(&#34;vcenter&#34;)
for type,data in classed.items():
    sheet = type[:31]
    worksheet = workbook.add_worksheet(sheet)
    worksheet.set_column(0,1000,42,cell_format)
    flattener(data,1,0,worksheet,0)
workbook.close()
</code></pre>

<h3 id="final-script">Final Script</h3>

<p>Putting everything together, plus a couple of minor adjustments (like the addition of <code>tfcheck</code> [a small bash script that I wrote to validate Terraform scripts] and <code>rm plan.tfplan</code> for cleaning up), the script ends up as follows:</p>

<pre><code class="language-python">from python_terraform import *
import json
import os
import xlsxwriter
import argparse

def parse_var(s):
    items = s.split(&#39;=&#39;)
    key = items[0].strip()
    if len(items) &gt; 1:
        value = &#39;=&#39;.join(items[1:])
    return (key, value)


def parse_vars(items):
    d = {}

    if items:
        for item in items:
            key, value = parse_var(item)
            d[key] = value
    return d

def ofname(tfpath=&#34;.&#34;,extension=&#34;.xlsx&#34;):
    os.chdir(tfpath)
    str1=os.getcwd()
    str2=str1.split(&#39;/&#39;)
    n=len(str2)
    name = str2[n-1] + extension
    return name

def flattener(jdata,row,column,worksheet,itemc):
    if isinstance(jdata,dict):
        for k,v in jdata.items():
            if isinstance(v,dict) or isinstance(v,list):
                worksheet.write(row,column,k)
                if isinstance(v,list) and len(v) == 1 :
                    row = flattener(v[0],row,column+1,worksheet,len(v))
                else:
                    row = flattener(v,row,column+1,worksheet,len(v))
            else:
                worksheet.write(row,column,k)
                worksheet.write(row,column+1,v)
                row = row + 1
    else:
        for v in jdata:
            if isinstance(v,dict) or isinstance(v,list):
                row = flattener(v,row,column,worksheet,len(v))
            else:
                worksheet.write(row,column,v)
                row = row + 1
    return row

vars = {}
parser = argparse.ArgumentParser(description=&#34;...&#34;)
parser.add_argument(&#34;--set&#34;,
                        metavar=&#34;KEY=VALUE&#34;,
                        nargs=&#39;+&#39;)
parser.add_argument(&#34;--tfpath&#34;,
                    type=str,
                    required=True)
args = parser.parse_args()
vars = parse_vars(args.set)

tfpath = args.tfpath

tf = Terraform(working_dir=tfpath)
tf.init()
tf.plan(out=&#34;plan.tfplan&#34;,var=vars)
json_data = tf.show(&#34;plan.tfplan&#34;,json=IsFlagged)

plan = json.loads(json_data[1])

classed = {}

for rc in plan[&#39;resource_changes&#39;]:
    classed[rc[&#34;type&#34;]] = {}

for rc in plan[&#39;resource_changes&#39;]:
    rc_dict = rc[&#39;change&#39;][&#39;after&#39;]
    rc_dict[&#39;address&#39;] = rc[&#39;address&#39;]
    rc_dict[&#39;type&#39;] = rc[&#39;type&#39;]
    classed[rc[&#34;type&#34;]].update({rc[&#39;address&#39;]: rc_dict})

workbook = xlsxwriter.Workbook(ofname(tfpath))
cell_format = workbook.add_format()
cell_format.set_text_wrap()
cell_format.set_align(&#34;vcenter&#34;)
for type,data in classed.items():
    sheet = type[:31]
    worksheet = workbook.add_worksheet(sheet)
    worksheet.set_column(0,1000,42,cell_format)
    flattener(data,1,0,worksheet,0)
workbook.close()

os.system(&#34;tfcheck&#34;)
os.system(&#34;rm plan.tfplan&#34;)
</code></pre>

<p>I&#39;m no Python expert, by any means, and I&#39;m sure that this script can be improved and optimized.</p>

<h2 id="usage">Usage</h2>

<p>Now, how do we use this script? Pretty easily. Once we&#39;ve created our override file for Terraform, we simply run the script, passing the arguments we require:</p>

<pre><code class="language-bash">python3 main.py --tfpath terraform/ --set location=&#34;Central US&#34; testing=true
</code></pre>

<h2 id="final-thoughts">Final thoughts</h2>

<p>It&#39;s been over a year since my last post... And what a year it has been! My baby daughter was born recently, I got a new job, my grandma died...</p>

<p>Anyway, I got several ideas I&#39;d like to share with you, so I&#39;ll try to post more frequently.</p>

<p>See you soon! (Hopefully)</p>
]]></content:encoded>
      <guid>https://blog.chelo.dev/exporting-terraform-plan-to-excel-with-python</guid>
      <pubDate>Fri, 05 May 2023 21:30:00 +0000</pubDate>
    </item>
  </channel>
</rss>