<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Git &#8211; Johnny Morano&#039;s Tech Articles</title>
	<atom:link href="https://jmorano.moretrix.com/tag/git/feed/" rel="self" type="application/rss+xml" />
	<link>https://jmorano.moretrix.com</link>
	<description>Ramblings of an old-fashioned space cowboy</description>
	<lastBuildDate>Sat, 09 Apr 2022 07:03:25 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.6.2</generator>

<image>
	<url>https://jmorano.moretrix.com/wp-content/uploads/2022/04/cropped-jmorano_emblem-32x32.png</url>
	<title>Git &#8211; Johnny Morano&#039;s Tech Articles</title>
	<link>https://jmorano.moretrix.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Import configuration from Hiera or a Git repository with YAML files into Terraform</title>
		<link>https://jmorano.moretrix.com/2022/04/import-configuration-from-hiera-or-a-git-repository-with-yaml-files-into-terraform/</link>
					<comments>https://jmorano.moretrix.com/2022/04/import-configuration-from-hiera-or-a-git-repository-with-yaml-files-into-terraform/#comments</comments>
		
		<dc:creator><![CDATA[Johnny Morano]]></dc:creator>
		<pubDate>Tue, 05 Apr 2022 11:26:43 +0000</pubDate>
				<category><![CDATA[Automation]]></category>
		<category><![CDATA[Blog]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Terraform]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[Git]]></category>
		<category><![CDATA[Hashicorp]]></category>
		<category><![CDATA[YAML]]></category>
		<guid isPermaLink="false">https://jmorano.moretrix.com/?p=1393</guid>

					<description><![CDATA[De-duplication of configuration information is key when managing large environments which use different types of automation (Terraform, Jenkins,&#8230;]]></description>
										<content:encoded><![CDATA[
<p>De-duplication of configuration information is key when managing large environments which use different types of automation (<a rel="noreferrer noopener" href="https://www.terraform.io/" data-type="URL" data-id="https://www.terraform.io/" target="_blank">Terraform</a>, <a rel="noreferrer noopener" href="https://www.jenkins.io/" data-type="URL" data-id="https://www.jenkins.io/" target="_blank">Jenkins</a>, <a rel="noreferrer noopener" href="https://www.ansible.com/" data-type="URL" data-id="https://www.ansible.com/" target="_blank">Ansible</a>, scripts executed as <a rel="noreferrer noopener" href="https://systemd.io/" data-type="URL" data-id="https://systemd.io/" target="_blank">Systemd</a> timers, <a rel="noreferrer noopener" href="https://puppet.com/" data-type="URL" data-id="https://puppet.com/" target="_blank">Puppet</a>&#8230;). Although many different configuration management tools exist (RDBMS, <a rel="noreferrer noopener" href="https://www.consul.io/" data-type="URL" data-id="https://www.consul.io/" target="_blank">Consul</a>, &#8230;), one of the easiest to use is <a href="https://puppet.com/docs/puppet/7/hiera_intro.html" data-type="URL" data-id="https://puppet.com/docs/puppet/7/hiera_intro.html" target="_blank" rel="noreferrer noopener">Hiera</a> or just a plain normal <a rel="noreferrer noopener" href="https://git-scm.com/" data-type="URL" data-id="https://git-scm.com/" target="_blank">Git</a> repository with <code><a rel="noreferrer noopener" href="https://yaml.org/" data-type="URL" data-id="https://yaml.org/" target="_blank">YAML</a></code> files, in some hierarchical way (which Hiera in theory is).</p>



<p> The YAML configuration hierarchy could be defined as the following file structure:</p>



<ul class="wp-block-list"><li><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">common.yaml</mark></code>: Default settings no matter which role or host. These can be overridden in all the below.</li><li><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">my_environment01.yaml</mark></code>: Environment specific configuration (example: development, staging, production, amsterdam, az01, az04, &#8230;). These can be overridden in all the below.</li><li><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">common/roles/some_server_role.yaml</mark></code>: A server role, or type definition, which contains role specific configuration parameters. The roles could implement an extra hierarchy as for instance:<br /><ul><li><code>debian::databases::postgres</code></li><li><code>debian::databases::postgres::timescale</code></li><li><code>debian::databases::postgres::timescale::prometheus</code></li><li><code>debian::loadbalancer::internal</code></li><li><code>debian::application::request_processor</code><br /><br />The hierarchy steps are divided by<code> :: </code>in the above example, and need to be inherited accordingly, each with their own YAML file.<br />These can be overridden in all the below.</li></ul></li><li><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">my_environment01/roles/some_server_role.yaml</mark></code>: override role configuration parameters per environment.<br />These can even be overridden on host level below.</li><li><code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">my_environment01/hosts/my_hostname01.yaml</mark></code>: set host specific configuration parameters. This file is actually always required and should contain at least the IP address of the node and the server role string.</li></ul>



<p>Let&#8217;s take the following example: The host <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-cyan-blue-color">vmazdbprm01</mark></code> has the role <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color"><code>debian::databases::postgres::timescale::prometheus</code> </mark>and is deployed in the environment in <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-green-cyan-color">my_cool_location03</mark></code>. The configuration management should search for parameters in the following file locations (and first verify if the file path exists):</p>



<ol class="wp-block-list"><li><code>common.yaml</code></li><li><code>my_cool_location01.yaml</code></li><li><code>common/roles/debian.yaml</code></li><li><code>common/roles/debian::databases.yaml</code></li><li><code>common/roles/debian::databases::postgres.yaml</code></li><li><code>common/roles/debian::databases::postgres::timescale.yaml</code></li><li><code>common/roles/debian::databases::postgres::timescale::prometheus.yaml</code></li><li><code>my_cool_location01/roles/debian.yaml</code></li><li><code>my_cool_location01/roles/debian::databases.yaml</code></li><li><code>my_cool_location01/roles/debian::databases::postgres.yaml</code></li><li><code>my_cool_location01/roles/debian::databases::postgres::timescale.yaml</code></li><li><code>my_cool_location01/roles/debian::databases::postgres::timescale::prometheus.yaml</code></li><li><code>my_cool_location01/hosts/vmazdbprm01.yaml</code></li></ol>



<p>This means that any code which wants to implement the above configuration management, needs to verify if the above <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">13 files</mark> exists from top to bottom, and if yes loads the YAML file accordingly.</p>



<p>Terraform is an open-source infrastructure as code software tool that provides a consistent CLI workflow to manage hundreds of cloud services.</p>



<p>Implementing the above <code>YAML</code> hierarchy in Terraform, could be done as follows:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="yaml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">locals {
  host_cfg             = yamldecode(fileexists("cfgmgmt/${var.environment}/hosts/${var.node}.yaml") ? file("cfgmgmt/${var.environment}/hosts/${var.node}.yaml") : "{server_role: debian}")

  roles_list           = split("::", local.host_cfg.server_role)
  all_roles_list       = [ for index in range(length(local.roles_list)): join("::",slice(local.roles_list, 0, index + 1))  ]

  common_cfg           = yamldecode(fileexists("cfgmgmt/common.yaml") ? file("cfgmgmt/common.yaml") : "{}")
  common_role_cfg_list = [ for file in local.all_roles_list:
      yamldecode(fileexists("cfgmgmt/common/roles/${file}.yaml") ? file("cfgmgmt/common/roles/${file}.yaml") : "{}" )]
    
  env_cfg              = yamldecode(fileexists("cfgmgmt/${var.environment}.yaml") ? file("cfgmgmt/${var.environment}.yaml") : "{}")
  env_role_cfg_list    = [ for file in local.all_roles_list:
      yamldecode(fileexists("cfgmgmt/${var.environment}/roles/${file}.yaml") ? file("cfgmgmt/${var.environment}/roles/${file}.yaml") : "{}") ]
        
  common_role_cfg_map  = merge(local.common_role_cfg_list...)
  env_role_cfg_map     = merge(local.env_role_cfg_list...)

  cfg                  = merge(local.common_cfg, local.env_cfg, local.common_role_cfg_map, local.env_role_cfg_map, local.host_cfg)
}</pre>



<p>Let&#8217;s have a look what actually happens in the above code.</p>



<p>All YAML files are stored in a Git/ Hiera repository, accessible in the sub-directory <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">cfgmgmt</mark></code>.</p>



<p>The code declares &#8220;<code>local</code>&#8221; variables by issuing the resource &#8220;<code><a rel="noreferrer noopener" href="https://www.terraform.io/language/values/locals" data-type="URL" data-id="https://www.terraform.io/language/values/locals" target="_blank">locals</a></code>&#8220;, starting from <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">line 1</mark>.</p>



<p><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">Line 2</mark> will check if a file called <code>cfgmgmt/${var.environment}/hosts/${var.node}.yaml</code> exists and if <code>true</code>, loads the <code>YAML</code> content as an map into the local variable <code>host_cfg</code>. If the file doesn&#8217;t exists, a default <code>YAML</code> code will be loaded. In theory, each node/ host must have a file defined as it should have at least data configuration such as:</p>



<ul class="wp-block-list"><li>unique node host name</li><li>IP address</li><li>server role</li><li>(optionally) VLAN/ subnet configuration</li><li>&#8230;</li></ul>



<p><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">Line 4</mark> splits the server role string, stored in <code>local.host_cfg.server_role</code>, into a list, to build the server role hierarchy further below.</p>



<p><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">Line 5 </mark>creates a list of top level server roles which need to be imported too. Example: if the server role was set to <code>debian::databases::postgres::timescale::prometheus</code> , the list <code>all_roles_list</code> will contain the following elements:</p>



<ul class="wp-block-list"><li><code>debian</code></li><li><code>debian::databases</code></li><li><code>debian::databases::postgres</code></li><li><code>debian::databases::postgres::timescale</code></li><li><code>debian::databases::postgres::timescale::prometheus</code></li></ul>



<p><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">Line 7</mark> loads the YAML content of <code>common.yaml</code>, if it exists.</p>



<p><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">Line 8</mark> loops over the <code>all_roles_list</code> elements, created on <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">line 5</mark>, and will load the YAML content of the server roles (if the file exists) into a list element. The result is a list called <code>common_role_cfg_list</code>.</p>



<p><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">Line 11</mark> loads the general environment configuration YAML content (if it exists) into the local variable <code>env_cfg</code>.</p>



<p><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">Line 12 </mark>will do the same thing as<mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color"> line 8</mark>, but for environment specific roles. (for instance: when certain server roles have environment specific configuration parameters).</p>



<p><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">Lines 15</mark> and <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">16</mark> will merge the elements (which in theory are <code><a rel="noreferrer noopener" href="https://www.terraform.io/language/values/variables#map" data-type="URL" data-id="https://www.terraform.io/language/values/variables#map" target="_blank">maps</a></code> of YAML data) of the server role lists into one big map, in the order of the list. This allows that keys can be overridden. The expansion <code>...</code> notation is explained at <a rel="noreferrer noopener" href="https://www.terraform.io/language/expressions/function-calls#expanding-function-arguments" target="_blank">https://www.terraform.io/language/expressions/function-calls#expanding-function-arguments</a>.</p>



<p>Finally on <mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-red-color">line 18</mark>, a local variable called <code>cfg</code> will be created, which merges the values of:</p>



<ul class="wp-block-list"><li><code>local.common_cfg</code></li><li><code>local.env_cfg</code></li><li><code>local.common_role_cfg_map</code></li><li><code>local.env_role_cfg_map</code></li><li><code>local.host_cfg</code></li></ul>



<p>By providing the environment name and the host/ node name to the above code (as <code>var.environment</code> and <code>var.node</code> ), all required configuration parameters can be loaded per node in Terraform, but since we&#8217;ve used a Git repository, this information can be loaded in any kind of automation tool (required is of course that each automation implements the same kind of hierarchy code).</p>
]]></content:encoded>
					
					<wfw:commentRss>https://jmorano.moretrix.com/2022/04/import-configuration-from-hiera-or-a-git-repository-with-yaml-files-into-terraform/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
	</channel>
</rss>
