<?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: SerDigital64</title>
    <description>The latest articles on DEV Community by SerDigital64 (@serdigital64).</description>
    <link>https://dev.to/serdigital64</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%2F683941%2Fe733b9f6-0c30-430f-8bc8-2546c9bd920f.png</url>
      <title>DEV Community: SerDigital64</title>
      <link>https://dev.to/serdigital64</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/serdigital64"/>
    <language>en</language>
    <item>
      <title>Terraform Scripting Concepts: Part 2 of 2</title>
      <dc:creator>SerDigital64</dc:creator>
      <pubDate>Fri, 31 Dec 2021 01:25:02 +0000</pubDate>
      <link>https://dev.to/serdigital64/terraform-scripting-concepts-part-2-of-2-36nl</link>
      <guid>https://dev.to/serdigital64/terraform-scripting-concepts-part-2-of-2-36nl</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;This is the second part of the tutorial. If not done already, please read the first part: &lt;a href="https://dev.to/serdigital64/terraform-scripting-concepts-part-1-of-2-484f"&gt;Terraform Scripting Concepts: Part 1 of 2&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Using functions
&lt;/h2&gt;

&lt;p&gt;Another particular characteristic of the &lt;strong&gt;Terraform Language&lt;/strong&gt; is that it doesn't support user-defined functions. Instead, it provides groups of built-in functions that can be used when assigning value to variables and attributes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Numeric&lt;/li&gt;
&lt;li&gt;String&lt;/li&gt;
&lt;li&gt;Collection&lt;/li&gt;
&lt;li&gt;Encoding&lt;/li&gt;
&lt;li&gt;Filesystem&lt;/li&gt;
&lt;li&gt;Date and Time&lt;/li&gt;
&lt;li&gt;Hash and Crypto&lt;/li&gt;
&lt;li&gt;Networking&lt;/li&gt;
&lt;li&gt;Type conversion&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use the following template for calling functions: &lt;code&gt;FUNCTION_NAME(PARAMETER1, PARAMETER2, PARAMETERN)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;# Find the maximum value from a list of numbers&lt;/span&gt;
  &lt;span class="nx"&gt;numeric_max&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;# Find the minimum value from a list of numbers&lt;/span&gt;
  &lt;span class="nx"&gt;numeric_min&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;# Conver string to lowercase&lt;/span&gt;
  &lt;span class="nx"&gt;string_lower&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"ServerX.DomainY"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;# Extrat dir from path&lt;/span&gt;
  &lt;span class="nx"&gt;filesystem_dir&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/full/path/to/file"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;# Extrat file name from path&lt;/span&gt;
  &lt;span class="nx"&gt;filesystem_file&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/full/path/to/file"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;# Record timestamp&lt;/span&gt;
  &lt;span class="nx"&gt;date_time_timestamp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Managing infrastructure
&lt;/h2&gt;

&lt;p&gt;Up until now, the script takes no action. In order for &lt;strong&gt;Terraform&lt;/strong&gt; to actually manage the target infrastructure and make changes to reach the required end-state, the script needs providers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Terraform&lt;/strong&gt; defines the following block types for working with the target infrastructure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;terraform&lt;/code&gt;: used to set workspace-wide run-time options.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;required_providers&lt;/code&gt;: used to declare what providers will be used&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;provider&lt;/code&gt;: used to define provider-specific options.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;resource&lt;/code&gt;: used to create a logical representation of the managed resource. Each provider will further specialize the resource with attributes and subtypes.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;data&lt;/code&gt;: optional block that is used to query infrastructure resources managed by the provider. The query result is then made available for other blocks to use as variables.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Declare Providers
&lt;/h3&gt;

&lt;p&gt;Providers are declared by adding a &lt;code&gt;required_providers&lt;/code&gt; nested block to the &lt;code&gt;terraform&lt;/code&gt; block. Each provider will be assigned to an attribute of type map:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Map name: used to reference the provider from other blocks.&lt;/li&gt;
&lt;li&gt;Attribute &lt;code&gt;version&lt;/code&gt;: used to define version requirements.&lt;/li&gt;
&lt;li&gt;Attribute &lt;code&gt;source&lt;/code&gt;: defines from where the provider will be downloaded.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additional provider-specific attributes can be defined by using the &lt;code&gt;provider&lt;/code&gt; block. The block will use the same label as the map name in &lt;code&gt;required_providers&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The following example declares two providers: &lt;code&gt;aws&lt;/code&gt; and &lt;code&gt;azure&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 3.0.0"&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/aws"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;azurerm&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 2.0.0"&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/azurerm"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"azurerm"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;features&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Define Managed Resources
&lt;/h3&gt;

&lt;p&gt;Once the provider has been declared, it will make available additional &lt;code&gt;resource&lt;/code&gt; and &lt;code&gt;data&lt;/code&gt; types. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Provider: AWS

&lt;ul&gt;
&lt;li&gt;Resource Type: &lt;code&gt;aws_instance&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Data Type: &lt;code&gt;aws_ami&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Provider: Azure

&lt;ul&gt;
&lt;li&gt;Resource Type: &lt;code&gt;azurerm_linux_virtual_machine&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Data Type: &lt;code&gt;azurerm_image&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use the following template to define blocks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"RESOURCE_TYPE"&lt;/span&gt; &lt;span class="s2"&gt;"RESOURCE_LABEL"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ATTRIBUTE1&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;VALUE&lt;/span&gt;
  &lt;span class="nx"&gt;ATTRIBUTEN&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;VALUE&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"RESOURCE_TYPE"&lt;/span&gt; &lt;span class="s2"&gt;"RESOURCE_LABEL"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ATTRIBUTE1&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;VALUE&lt;/span&gt;
  &lt;span class="nx"&gt;ATTRIBUTEN&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;VALUE&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The data and resource blocks can be used together to improve automation. In the following example, the &lt;code&gt;data&lt;/code&gt; block is used to query AWS for a particular AMI and then the &lt;code&gt;resource&lt;/code&gt; block will use the resulting AMI ID to create the VM instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_ami"&lt;/span&gt; &lt;span class="s2"&gt;"coreos"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;most_recent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;    &lt;span class="c1"&gt;# Return the latest one if more than one result is available&lt;/span&gt;

  &lt;span class="nx"&gt;filter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;              &lt;span class="c1"&gt;# Look for fedora coreos v35&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"name"&lt;/span&gt;
    &lt;span class="nx"&gt;values&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"fedora-coreos-35**"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;filter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;              &lt;span class="c1"&gt;# Look for HVM virtualization&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"virtualization-type"&lt;/span&gt;
    &lt;span class="nx"&gt;values&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"hvm"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;owners&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"125523088429"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;     &lt;span class="c1"&gt;# CentOS / Fedora account that owns AMIs&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_instance"&lt;/span&gt; &lt;span class="s2"&gt;"aws_test_instance"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ami&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_ami&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;coreos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;    &lt;span class="c1"&gt;# Use the AMI that was found in the data query&lt;/span&gt;
  &lt;span class="nx"&gt;instance_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t2.micro"&lt;/span&gt;      &lt;span class="c1"&gt;# Select the instance size&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;Explore Terraform resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Source code: &lt;a href="https://github.com/hashicorp/terraform"&gt;GitHUB Repository&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Tool documentation: &lt;a href="https://www.terraform.io/docs"&gt;Manual&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Learn advanced features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.terraform.io/language/functions"&gt;Functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.terraform.io/language/expressions"&gt;Expresions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.terraform.io/language/values/variables#custom-validation-rules"&gt;Custom validation rules&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.terraform.io/language/functions"&gt;Built-in Functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.terraform.io/language/modules"&gt;Modules&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Understand provider-specific implementation details:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/"&gt;Providers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Copyright information
&lt;/h2&gt;

&lt;p&gt;This article is licensed under a &lt;a href="http://creativecommons.org/licenses/by/4.0/"&gt;Creative Commons Attribution 4.0 International License&lt;/a&gt;. For copyright information on the product or products mentioned inhere refer to their respective owner.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;p&gt;Opinions presented in this article are personal and belong solely to me, and do not represent people or organizations associated with me in a professional or personal way. All the information on this site is provided "as is" with no guarantee of completeness, accuracy or the results obtained from the use of this information.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Terraform Scripting Concepts: Part 1 of 2</title>
      <dc:creator>SerDigital64</dc:creator>
      <pubDate>Fri, 31 Dec 2021 01:23:54 +0000</pubDate>
      <link>https://dev.to/serdigital64/terraform-scripting-concepts-part-1-of-2-484f</link>
      <guid>https://dev.to/serdigital64/terraform-scripting-concepts-part-1-of-2-484f</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.terraform.io/"&gt;Terraform&lt;/a&gt; is an automation tool focused on infrastructure provisioning. The product is developed by HashiCorp, and the command line version is provided as an Open Source Project available on GitHub.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Terraform&lt;/strong&gt; has the following core components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Terraform CLI&lt;/strong&gt;: stand-alone app that provides the run-time environment for &lt;strong&gt;Terraform Scripts&lt;/strong&gt; and maintains build-state information of &lt;strong&gt;Managed Resources&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terraform Script&lt;/strong&gt;: text file with &lt;strong&gt;Terraform Language&lt;/strong&gt; code used to describe the required end-state of the &lt;strong&gt;Managed Resources&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Managed Resource&lt;/strong&gt;: logical representation of a real infrastructure resource in the target infrastructure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terraform Provider&lt;/strong&gt;: stand-alone apps (plugins) that are used by the &lt;strong&gt;Terraform CLI&lt;/strong&gt; to interact with &lt;strong&gt;Managed Resources&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terraform Workspace&lt;/strong&gt;: working directory where &lt;strong&gt;Terraform Scripts&lt;/strong&gt; are located and where the &lt;strong&gt;Terraform CLI&lt;/strong&gt; stores data files (state, temporary, plugins, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Using Terraform
&lt;/h2&gt;

&lt;h3&gt;
  
  
  CLI Installation
&lt;/h3&gt;

&lt;p&gt;The recommended method for production environments is to add &lt;a href="https://www.terraform.io/downloads"&gt;HashiCorp's package repository&lt;/a&gt; to the OS package manager and install it using native tools.&lt;/p&gt;

&lt;p&gt;For more relaxed environments the binary package provides a simpler alternative that does not require root privilege:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create the installation repository&lt;/span&gt;
&lt;span class="nv"&gt;INSTALL_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/terraform-cli"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;INSTALL_PATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;INSTALL_PATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="c"&gt;# Download the binary package&lt;/span&gt;
curl &lt;span class="nt"&gt;-LO&lt;/span&gt; https://releases.hashicorp.com/terraform/1.1.2/terraform_1.1.2_linux_amd64.zip
&lt;span class="c"&gt;# Uncompress the binary package&lt;/span&gt;
&lt;span class="nv"&gt;ZIP_PACKAGE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'terraform_1.1.2_linux_amd64.zip'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; unzip &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ZIP_PACKAGE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ZIP_PACKAGE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Running scripts
&lt;/h3&gt;

&lt;p&gt;The execution process of &lt;strong&gt;Terraform Scripts&lt;/strong&gt; is divided into stages:&lt;/p&gt;

&lt;p&gt;1) &lt;code&gt;init&lt;/code&gt;: prepares the &lt;strong&gt;Terraform Workspace&lt;/strong&gt;, sets run-time parameters, downloads, and installs providers.&lt;br&gt;
2) &lt;code&gt;plan&lt;/code&gt;: creates/updates state information of managed resources and executes a dry-run to create the change plan. For already existing resources, if the provider is unable to modify attributes on the fly it will propose a destructive change (remove and recreate the resource with the new end-state).&lt;br&gt;
3) &lt;code&gt;apply&lt;/code&gt;: executes the change plan, and updates resource state information. The provider will modify the target infrastructure by creating, updating and removing resources until the end-state is reached.&lt;/p&gt;

&lt;p&gt;To execute the script the &lt;strong&gt;Terraform&lt;/strong&gt; CLI must be already inside the workspace directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;WORKSPACE_PATH
&lt;span class="c"&gt;# Initialize the workspace&lt;/span&gt;
terraform init
&lt;span class="c"&gt;# Prepare and review the action plan&lt;/span&gt;
terraform plan
&lt;span class="c"&gt;# Execute the action plan&lt;/span&gt;
terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that the user running the process doesn't need root privilege, just write permissions to the workspace path.&lt;/p&gt;

&lt;h2&gt;
  
  
  Script structure
&lt;/h2&gt;

&lt;p&gt;The first thing to understand about &lt;strong&gt;Terraform Scripts&lt;/strong&gt; is that the &lt;strong&gt;Terraform Language&lt;/strong&gt; is not for general-purpose development. Based on HCL (&lt;a href="https://github.com/hashicorp/hcl"&gt;Hashicorp Configuration Language&lt;/a&gt;), it's specifically designed to describe infrastructure resources.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Terraform Scripts&lt;/strong&gt; are developed by defining blocks: a complex data structure that describes a configuration object through its attributes.&lt;/p&gt;

&lt;p&gt;Use the following template for declaring blocks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;BLOCK_TYPE&lt;/span&gt; &lt;span class="s2"&gt;"RESOURCE_TYPE"&lt;/span&gt; &lt;span class="s2"&gt;"RESOURCE_LABEL1"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="nx"&gt;ATTRIBUTE1&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;VALUE&lt;/span&gt;
  &lt;span class="nx"&gt;ATTRIBUTE2&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;VALUE&lt;/span&gt;
  &lt;span class="nx"&gt;ATTRIBUTEN&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;VALUE&lt;/span&gt;

  &lt;span class="c1"&gt;# Nested block&lt;/span&gt;
  &lt;span class="nx"&gt;BLOCK_TYPE&lt;/span&gt; &lt;span class="s2"&gt;"RESOURCE_TYPE"&lt;/span&gt; &lt;span class="s2"&gt;"RESOURCE_LABEL2"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;ATTRIBUTE&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;VALUE&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;BLOCK_TYPE&lt;/strong&gt;: defines what type of configuration object is going to be described. Notice that Blocks can have any number of attributes and other blocks (nested).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RESOURCE_TYPE&lt;/strong&gt;: optional field used to select object subtypes. Not all objects will require one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RESOURCE_LABEL&lt;/strong&gt;: optional field to assign a label (ID) to the object.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ATTRIBUTE&lt;/strong&gt;: name of the object's attribute.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VALUE&lt;/strong&gt;: value that will be assigned to the object's attribute.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The following list shows common built-in block types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;variable&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;output&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;local&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;terraform&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;provider&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;resource&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;data&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additional types and subtypes are implemented through &lt;strong&gt;Terraform Providers&lt;/strong&gt;. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest"&gt;&lt;code&gt;aws&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/providers/hashicorp/google/latest"&gt;&lt;code&gt;google&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/providers/hashicorp/azurerm/latest"&gt;&lt;code&gt;azurerm&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/providers/hashicorp/kubernetes/latest"&gt;&lt;code&gt;kubernetes&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Scripts can have comments anywhere:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;//&lt;/code&gt;: single-line comment. Anything at the right is ignored.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;#&lt;/code&gt;: single-line comment. Anything at the right is ignored.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/* */&lt;/code&gt;: multi-line comment. Anything between the delimiters is ignored.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a good practice, after the script is created/updated run the built-in linter and code-formatter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;WORKSPACE_PATH
terraform &lt;span class="nb"&gt;fmt
&lt;/span&gt;terraform validate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using Variables
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Declaration
&lt;/h3&gt;

&lt;p&gt;As everything in &lt;strong&gt;Teraform&lt;/strong&gt;, variables are also a block type. Use the following template to declare variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;VARIABLE_NAME&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;VALUE&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Data Types
&lt;/h3&gt;

&lt;p&gt;Both variables and attributes have types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;string&lt;/code&gt;: a sequence of Unicode characters.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;number&lt;/code&gt;: integer or float numbers.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bool&lt;/code&gt;: boolean values.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;list&lt;/code&gt;: indexed sequence of values. The index is numerical and starts at cero. VALUEs can be of any type.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;map&lt;/code&gt;: group of key + value pairs. VALUEs can be of any type.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;null&lt;/code&gt;: empty value.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Types are inferred from the value (i.e. no explicit data type declaration). For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;test_string&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"abc123"&lt;/span&gt;
  &lt;span class="nx"&gt;test_number_integer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;
  &lt;span class="nx"&gt;test_number_float&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;15.1&lt;/span&gt;
  &lt;span class="nx"&gt;test_bool&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;test_list&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s2"&gt;"value 1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;100&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;test_map&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;key1&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"value 1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;key2&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Parameters
&lt;/h3&gt;

&lt;p&gt;Parameters are variables that are used to provide run-time data to &lt;strong&gt;Terraform Scripts&lt;/strong&gt;. They are declared using the &lt;code&gt;variable&lt;/code&gt; block type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"PARAMETER_NAME"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;# Optional parameter attributes&lt;/span&gt;
  &lt;span class="nx"&gt;ATTRIBUTE&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;VALUE&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They are different from regular variables as they allow attributes to further describe the parameter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;default&lt;/code&gt;: value that is assigned if none is provided.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;description&lt;/code&gt;: text message that is used to describe the parameter.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;type&lt;/code&gt;: expected variable type. This is not used to declare the type, but rather to constrain the provided value.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;validation&lt;/code&gt;: validation block used to perform additional checks.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sensitive&lt;/code&gt;: flag to restrict script output. Used to mask sensitive values such as passwords.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;nullable&lt;/code&gt;: flag to indicate if the value can be null.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"aws_region"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-west-1a"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"target AWS region"&lt;/span&gt;
  &lt;span class="nx"&gt;nullable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="nx"&gt;sensitive&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"xaas_test_api_secret"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"API secret for the Test API"&lt;/span&gt;
  &lt;span class="nx"&gt;nullable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="nx"&gt;sensitive&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Output
&lt;/h3&gt;

&lt;p&gt;In addition to the &lt;code&gt;local&lt;/code&gt; and &lt;code&gt;variable&lt;/code&gt; block types &lt;strong&gt;Terraform&lt;/strong&gt; provides the &lt;code&gt;output&lt;/code&gt; type to define variables that will be exported and made available to external apps and scripts. The following attributes are available:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;value&lt;/code&gt;: the value assigned to the variable.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;description&lt;/code&gt;: describe what the value represents.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sensitive&lt;/code&gt;: flag to restrict script output. Used to mask sensitive values such as passwords.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"sysadmin_key"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"secret-api-key"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"API secret for the new service"&lt;/span&gt;
  &lt;span class="nx"&gt;sensitive&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Retreive variable's value
&lt;/h3&gt;

&lt;p&gt;In general, any block attribute can be referenced from within other blocks by using the following structure: &lt;code&gt;BLOCK_TYPE.ATTRIBUTE&lt;/code&gt; or &lt;code&gt;BLOCK_TYPE.BLOCK_LABEL.ATTRIBUTE&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In addition to the generic format there are special cases to consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Locals: use &lt;code&gt;local&lt;/code&gt; instead of &lt;code&gt;locals&lt;/code&gt;: &lt;code&gt;local.ATTRIBUTE&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Parameters: use the short form &lt;code&gt;var&lt;/code&gt; instead of the full type name &lt;code&gt;variable&lt;/code&gt;: &lt;code&gt;var.ATTRIBUTE&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Outputs: requires the module name where the output is declared: &lt;code&gt;module.MODULE NAME.OUTPUT_NAME&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Variables can also be used in &lt;strong&gt;String Templates&lt;/strong&gt; (simple programming code that can be embedded inside string declarations): &lt;code&gt;"${BLOCK_TYPE.ATTRIBUTE}"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The following example shows the three variable types seen so far:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;api_secret_seed&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"abc123"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"new_service_name"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"new_service_name"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"What name will the new service have"&lt;/span&gt;
  &lt;span class="nx"&gt;nullable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="nx"&gt;sensitive&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"service_secret"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api_secret_seed&lt;/span&gt;&lt;span class="k"&gt;}${&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;new_service_name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"API secret for the new service"&lt;/span&gt;
  &lt;span class="nx"&gt;sensitive&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;Continue reading the second part of the tutorial: &lt;a href="https://dev.to/serdigital64/terraform-scripting-concepts-part-2-of-2-36nl"&gt;Terraform Scripting Concepts: Part 2 of 2&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Copyright information
&lt;/h2&gt;

&lt;p&gt;This article is licensed under a &lt;a href="http://creativecommons.org/licenses/by/4.0/"&gt;Creative Commons Attribution 4.0 International License&lt;/a&gt;. For copyright information on the product or products mentioned inhere refer to their respective owner.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;p&gt;Opinions presented in this article are personal and belong solely to me, and do not represent people or organizations associated with me in a professional or personal way. All the information on this site is provided "as is" with no guarantee of completeness, accuracy or the results obtained from the use of this information.&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Bash Scripting Concepts: Part 2 of 2</title>
      <dc:creator>SerDigital64</dc:creator>
      <pubDate>Wed, 22 Dec 2021 03:07:56 +0000</pubDate>
      <link>https://dev.to/serdigital64/bash-scripting-concepts-part-2-of-2-3j0f</link>
      <guid>https://dev.to/serdigital64/bash-scripting-concepts-part-2-of-2-3j0f</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;This is the second part of the tutorial. If not done already, please read the first part: &lt;a href="https://dev.to/serdigital64/bash-scripting-concepts-part-1-of-2-28fk"&gt;Bash Scripting Concepts: Part 1 of 2&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Working with loops
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Bash&lt;/strong&gt; provides three ways of creating loops:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;for&lt;/code&gt;: loop for a predefined number of times&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;while&lt;/code&gt;: loop while the exit condition is true. The condition is evaluated before executing tasks.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;until&lt;/code&gt;: loop until the exit condition is true. The condition is evaluated after executing tasks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In addition to the loop condition, Bash provides two statements that can be used to control the loop execution flow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;break&lt;/code&gt;: forces the loop to stop.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;continue&lt;/code&gt;: forces the loop to skip remaining tasks and start the next iteration.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Command: &lt;code&gt;while&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;In the following example, the loop iterates 5 times. The loop condition is evaluated after all commands in the code block are executed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;declare&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;span class="nb"&gt;declare&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nv"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt;count &amp;lt;&lt;span class="o"&gt;=&lt;/span&gt; max&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'counter: %s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;count &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Command: &lt;code&gt;until&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Using the same structure as in the &lt;code&gt;while&lt;/code&gt; example, notice that now the loop iterates 4 times only. This is because the loop condition is evaluated before executing the code block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;declare&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;span class="nb"&gt;declare&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nv"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5

&lt;span class="k"&gt;until&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt;count &lt;span class="o"&gt;==&lt;/span&gt; max&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'counter: %s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;count &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Command: &lt;code&gt;for&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;In the case of the &lt;code&gt;for&lt;/code&gt; loop the iteration is predefined. Instead of having a loop condition, the loop variable &lt;code&gt;count&lt;/code&gt; will be assigned each value in the list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;declare&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; count

&lt;span class="k"&gt;for &lt;/span&gt;count &lt;span class="k"&gt;in &lt;/span&gt;1 2 3 4 5 &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'counter: %s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Working with conditionals
&lt;/h2&gt;

&lt;p&gt;Bash provides the following options for implementing conditional execution:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;||&lt;/code&gt;: (logical OR) evaluates the execution of two commands and sets the exit status to zero if any associated exit status is zero.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;: (logical AND) evaluates the execution of two commands and sets the exit status to zero if all associated exit statuses are zero.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;\!&lt;/code&gt;: (logical NOT) evaluates the execution of a command and sets the exit status to zero if the associated exit status is not zero.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;(( ))&lt;/code&gt;: performs logical evaluation on the integer expression and sets the exit status to zero if true&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[[ ]]&lt;/code&gt;: evaluates the literal expression and sets the exit status to zero if true.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;if&lt;/code&gt;: executes a command and if the exit status is zero then performs additional actions.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;case&lt;/code&gt;: compares the provided value against a list of patterns and executes the commands upon match.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As mentioned before, &lt;strong&gt;Bash&lt;/strong&gt; interprets the exit status of commands as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;0&lt;/code&gt;: true&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;gt;0&lt;/code&gt;: false&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the following examples &lt;code&gt;true&lt;/code&gt; and &lt;code&gt;false&lt;/code&gt; are external commands that emulates true and false values (exist status 0 and 1 respectively)&lt;/p&gt;

&lt;h3&gt;
  
  
  Logical OR: &lt;code&gt;||&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;false
printf&lt;/span&gt; &lt;span class="s1"&gt;'evaluation result of (true || false): %s\n'&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true
printf&lt;/span&gt; &lt;span class="s1"&gt;'evaluation result of (true || true): %s\n'&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;false
printf&lt;/span&gt; &lt;span class="s1"&gt;'evaluation result of (false || false): %s\n'&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true
printf&lt;/span&gt; &lt;span class="s1"&gt;'evaluation result of (false || true): %s\n'&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Logical AND: &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;false
printf&lt;/span&gt; &lt;span class="s1"&gt;'evaluation result of (true &amp;amp;&amp;amp; false): %s\n'&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;true
printf&lt;/span&gt; &lt;span class="s1"&gt;'evaluation result of (true &amp;amp;&amp;amp; true): %s\n'&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;false
printf&lt;/span&gt; &lt;span class="s1"&gt;'evaluation result of (false &amp;amp;&amp;amp; false): %s\n'&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;true
printf&lt;/span&gt; &lt;span class="s1"&gt;'evaluation result of (false &amp;amp;&amp;amp; true): %s\n'&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Logical NOT: &lt;code&gt;!&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;true
printf&lt;/span&gt; &lt;span class="s1"&gt;'evaluation result of (! true): %s\n'&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nb"&gt;false
printf&lt;/span&gt; &lt;span class="s1"&gt;'evaluation result of (! false): %s\n'&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Arithmetic Expression Evaluation: &lt;code&gt;(( ))&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;(( ))&lt;/code&gt; form accepts several logical operators. Some of them are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;==&lt;/code&gt;: equal&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;!=&lt;/code&gt;: not equal&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;gt;&lt;/code&gt;: greater than&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;&lt;/code&gt;: less than&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;gt;=&lt;/code&gt;: greater than or equal&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;=&lt;/code&gt;: less than or equal
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;declare&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$RANDOM&lt;/span&gt;
&lt;span class="o"&gt;((&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;test&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 5000 &lt;span class="o"&gt;))&lt;/span&gt;
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'evaluation result of (( %s &amp;gt; 5000 )): %s\n'&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;test&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Expression Evaluation: &lt;code&gt;[[ ]]&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;[[ ]]&lt;/code&gt; form accepts several logical operators and tests. Some of them are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;==&lt;/code&gt;: equal&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;!=&lt;/code&gt;: not equal&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-z&lt;/code&gt;: string is empty&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-n&lt;/code&gt;: string is not empty&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-f&lt;/code&gt;: path is a file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For &lt;code&gt;==&lt;/code&gt; and &lt;code&gt;!=&lt;/code&gt; the special character &lt;code&gt;*&lt;/code&gt; can be used as a wildcard to match zero or more characters to the right.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;declare test&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$RANDOM&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;test&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; 1&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'evaluation result of [[ "%s" == 1* ]]: %s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;test&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Command: &lt;code&gt;if&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;In the following example, arithmetic evaluation is used. Notice that quotes are not required within &lt;code&gt;(( ))&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;declare&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RANDOM&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;test&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; 10000 &lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'test value (%s) is equal or greater than 10000\n'&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;test&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;test&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 5000 &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;test&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &amp;lt; 10000 &lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'test value (%s) is between 5001 and 9999\n'&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;test&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;else
  &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'test value (%s) is less than than 5001\n'&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;test&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Command: &lt;code&gt;case&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;declare&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RANDOM&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;test&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;in
  &lt;/span&gt;1&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;2&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'Random number (%s) starts with 1 or 2\n'&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;test&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
  3&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'Random number (%s) starts with 3\n'&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;test&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
  &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'Random number (%s) does not start with 1,2 or 3\n'&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;test&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="k"&gt;esac&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Redirecting data flows
&lt;/h2&gt;

&lt;p&gt;Bash provides two alternatives for establishing data flows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Redirection

&lt;ul&gt;
&lt;li&gt;Set read source for &lt;strong&gt;STDIN&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Set write destination for &lt;strong&gt;STDOUT&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Set write destination for &lt;strong&gt;STDERR&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Pipelines: integrate two commands by plugging the &lt;strong&gt;STDOUT&lt;/strong&gt; from the first one to the &lt;strong&gt;STDIN&lt;/strong&gt; of the second one&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Redirection
&lt;/h3&gt;

&lt;p&gt;In the following example, two functions will communicate with each other using a common file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;readonly &lt;/span&gt;&lt;span class="nv"&gt;DATA_BRIDGE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;mktemp&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;function &lt;/span&gt;produce_data&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'produce_data(): write data to the temporary file (%s) by redirectin the STDOUT of the printf command\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DATA_BRIDGE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'[sample data]\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DATA_BRIDGE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function &lt;/span&gt;ingest_data&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'ingest_data(): read data from the temporary file (%s) by redirecting the STDIN of the cat command: '&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DATA_BRIDGE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;cat&lt;/span&gt; &amp;lt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DATA_BRIDGE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

produce_data
ingest_data

&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DATA_BRIDGE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pipelines
&lt;/h3&gt;

&lt;p&gt;This example shows an alternative way of integrating both functions using pipelines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="k"&gt;function &lt;/span&gt;produce_data&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'[sample data]\n'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function &lt;/span&gt;ingest_data&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;cat&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'Integrate functions produce_data() and ingest_data() by piping their STDIN and STDOUT: '&lt;/span&gt;
produce_data | ingest_data

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;Discover advanced features by exploring the &lt;a href="https://www.gnu.org/software/bash/manual/html_node/index.html#SEC_Contents"&gt;Bash Reference Manual&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;jobs&lt;/li&gt;
&lt;li&gt;signals&lt;/li&gt;
&lt;li&gt;traps&lt;/li&gt;
&lt;li&gt;parallelism&lt;/li&gt;
&lt;li&gt;error handling&lt;/li&gt;
&lt;li&gt;configuration settings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Organize your code by choosing a coding style. For example: &lt;a href="https://google.github.io/styleguide/shellguide.html"&gt;Google Shell Style Guide&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enhance script's quality by incorporating linter and testing tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.shellcheck.net/"&gt;Shell Check&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/bats-core/bats-core"&gt;Basts-Core&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Copyright information
&lt;/h2&gt;

&lt;p&gt;This article is licensed under a &lt;a href="http://creativecommons.org/licenses/by/4.0/"&gt;Creative Commons Attribution 4.0 International License&lt;/a&gt;. For copyright information on the product or products mentioned inhere refer to their respective owner.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;p&gt;Opinions presented in this article are personal and belong solely to me, and do not represent people or organizations associated with me in a professional or personal way. All the information on this site is provided "as is" with no guarantee of completeness, accuracy or the results obtained from the use of this information.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Bash Scripting Concepts: Part 1 of 2</title>
      <dc:creator>SerDigital64</dc:creator>
      <pubDate>Wed, 22 Dec 2021 03:06:52 +0000</pubDate>
      <link>https://dev.to/serdigital64/bash-scripting-concepts-part-1-of-2-28fk</link>
      <guid>https://dev.to/serdigital64/bash-scripting-concepts-part-1-of-2-28fk</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Bash&lt;/strong&gt; is a tool that provides a text-based user interface for managing operating system resources. It's also the run-time environment for a simple interpreted programming language that can be used to create &lt;strong&gt;scripts&lt;/strong&gt; for automating tasks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bash&lt;/strong&gt; is the evolution of the popular &lt;strong&gt;Born Shell (sh)&lt;/strong&gt;, a de facto standard among Unix-like operating systems. It provides all the features of its predecessor plus a new set of built-ins and configuration settings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Bash
&lt;/h2&gt;

&lt;p&gt;There are three common use cases for &lt;strong&gt;Bash&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;User shell&lt;/strong&gt;: configured at the operating system level, provides the shell for user login, either local or remote.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Script run-time&lt;/strong&gt;: provides the execution run-time for &lt;strong&gt;Bash&lt;/strong&gt; scripts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Temporal shell&lt;/strong&gt;: provides an interactive shell session on top of the user shell. The new session is a sub-process of the actual user or a different user when using privilege delegation tools such as SuDO.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This tutorial will focus on the &lt;strong&gt;scripts run-time&lt;/strong&gt; use case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Script structure
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Bash&lt;/strong&gt; scripts are text files describing a sequence of commands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Commands can be either built-in or external Linux apps.&lt;/li&gt;
&lt;li&gt;Commands are separated from each other by the new-line character (standard line separator for Linux/Unix systems).&lt;/li&gt;
&lt;li&gt;Long lines can be split using the scape character &lt;code&gt;\&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Commands can be grouped on the same line using the semicolon separator &lt;code&gt;;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Commands and arguments are separated using one or more spaces.&lt;/li&gt;
&lt;li&gt;Although not required, it's recommended that the first line of the script contains: &lt;code&gt;#!/bin/bash&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Script files must have execution and read permissions if used directly, or just read-only permissions if called using &lt;strong&gt;Bash&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'echo and printf are built-in commands'&lt;/span&gt;

&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'pwd is an external Linux command that shows the current directory: '&lt;/span&gt;
&lt;span class="nb"&gt;pwd

printf&lt;/span&gt; &lt;span class="s1"&gt;'multiple '&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'commands '&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'on the same line\n'&lt;/span&gt;

&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s %s %s\n'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'single command'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'using multiple'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'lines'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using Variables
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Declaration
&lt;/h3&gt;

&lt;p&gt;Variables in Bash are created using a declaration command and the equal &lt;code&gt;=&lt;/code&gt; symbol to associate the variable name to its value. Notice that there should be no spaces before and after the assignment symbol:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;declare variable_name='variable value'&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Bash provides the following built-in commands for declaring variables:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Usage&lt;/th&gt;
&lt;th&gt;Scope&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;export&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Declare global variables&lt;/td&gt;
&lt;td&gt;Script-wide and sub-processes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;readonly&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Declare readonly variables (constants)&lt;/td&gt;
&lt;td&gt;Script-wide&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;declare&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Declare script level variables&lt;/td&gt;
&lt;td&gt;Script-wide&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;local&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Declare function level variables&lt;/td&gt;
&lt;td&gt;Function-wide&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Consider the following best practices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Declare variables at the beginning of the code block.&lt;/li&gt;
&lt;li&gt;Declare and initialize all the variables that the script will use.&lt;/li&gt;
&lt;li&gt;Readonly and exported variables should be named using all uppercase.&lt;/li&gt;
&lt;li&gt;Use single quotes for static content.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Retreive variable's value
&lt;/h3&gt;

&lt;p&gt;To use the value of a variable, prepend the dollar symbol &lt;code&gt;$&lt;/code&gt; to the name of the variable surrounded by brackets &lt;code&gt;{}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As a best-practice, surround variable expansion with double quotes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;echo "${test_variable}"&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Bash&lt;/strong&gt; also provides processing functions that can be used for altering the value before the expansion.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;declare &lt;/span&gt;&lt;span class="nv"&gt;var1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'word1'&lt;/span&gt;
&lt;span class="nb"&gt;declare &lt;/span&gt;&lt;span class="nv"&gt;var2&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'WORD2'&lt;/span&gt;
&lt;span class="nb"&gt;declare &lt;/span&gt;&lt;span class="nv"&gt;var3&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'AbCdE/12345'&lt;/span&gt;

&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'replace value with its length: "${#var1}" = "%s"\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;var1&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'right strip from pattern: "${var3##*/}" = "%s"\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;var3&lt;/span&gt;&lt;span class="p"&gt;##*/&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'left strip from pattern: "${var3%%/*}" = "%s"\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;var3&lt;/span&gt;&lt;span class="p"&gt;%%/*&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'convert to uppercase: "${var1^^}" = "%s"\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;var1&lt;/span&gt;&lt;span class="p"&gt;^^&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'convert to lowercase: "${var2,,}" = "%s"\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;var2&lt;/span&gt;&lt;span class="p"&gt;,,&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Data Types
&lt;/h3&gt;

&lt;p&gt;Bash supports four data types:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Declaration command&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;declare&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;integer&lt;/td&gt;
&lt;td&gt;declare -i&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;array&lt;/td&gt;
&lt;td&gt;declare -a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;associative array&lt;/td&gt;
&lt;td&gt;declare -A&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Even though there is no explicit boolean data type, &lt;strong&gt;Bash&lt;/strong&gt; interprets the exit status of commands as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;exit status == 0: true&lt;/li&gt;
&lt;li&gt;exit status &amp;gt; 0: false&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Parameters
&lt;/h3&gt;

&lt;p&gt;Parameters are special variables that are automatically created when a function or script is called with arguments.&lt;/p&gt;

&lt;p&gt;The variable name is created by using a single number to represent the position of the word in the argument list:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;bash say_hello.bash 'to' 'my' 'little' 'friend'&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;declare &lt;/span&gt;&lt;span class="nv"&gt;msg1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="c"&gt;# Variable $1: assigned to the first variable&lt;/span&gt;
&lt;span class="nb"&gt;declare &lt;/span&gt;&lt;span class="nv"&gt;msg2&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="c"&gt;# Variable $2: assigned to the second variable&lt;/span&gt;
&lt;span class="nb"&gt;declare &lt;/span&gt;&lt;span class="nv"&gt;msg3&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$3&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="c"&gt;# Variable $3: assigned to the third variable&lt;/span&gt;
&lt;span class="nb"&gt;declare &lt;/span&gt;&lt;span class="nv"&gt;msg4&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$4&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="c"&gt;# Variable $4: assigned to the forth variable&lt;/span&gt;

&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'say hello %s %s %s %s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;msg1&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;msg2&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;msg3&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;msg4&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Shell Expansion
&lt;/h3&gt;

&lt;p&gt;In addition to variables &lt;strong&gt;Bash&lt;/strong&gt; provides additional features for generating dynamic values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;$( )&lt;/code&gt;: Command Expansion: Execute the command in a sub-process and expand its STDOUT.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$(( ))&lt;/code&gt;: Arithmetic Expansion: Evaluate the arithmetic expression and use the resulting value for the expansion.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$(&amp;lt; )&lt;/code&gt;: File Expansion: Read the content of a file and use it for the expansion.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'Command Expansion: "$(echo hello-world)" = "%s"\n\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'hello-world'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'Arithmetic Expansion:  "$(( 2 + 2 ))" = %s\n\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'File Expansion: "$(&amp;lt;/etc/os-release)"\n %s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&amp;lt;/etc/os-release&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Working with processes
&lt;/h2&gt;

&lt;p&gt;Before going into the details, let's review the following key concepts about Unix processes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A process is a running app that is executed from an already running process (parent). For scripts, the parent process is the one running the &lt;strong&gt;Bash&lt;/strong&gt; run-time.&lt;/li&gt;
&lt;li&gt;The Operating System assigns unique integer identifiers to each process (&lt;strong&gt;PID&lt;/strong&gt;: Process ID).&lt;/li&gt;
&lt;li&gt;Processes have 3 data paths:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;STDIN&lt;/strong&gt;: standard input: process can read data from this path.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;STDOUT&lt;/strong&gt;: standard output: process can write data to this path.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;STDERR&lt;/strong&gt;:: standard error: process can write error diagnostic data to this path.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Exit status: numeric value that represents the final execution status of the process. In general:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;0&lt;/code&gt;: successful execution&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;gt;0&lt;/code&gt;: failed execution. The app can assign different numbers to further describe the error.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Bash&lt;/strong&gt; provides the following features for interacting with processes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Variables: show process information

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;$?&lt;/code&gt;: exit status of the last executed command&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$BASHPID&lt;/code&gt;: PID of the current &lt;strong&gt;Bash&lt;/strong&gt; process&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Redirection: redirect the data flow from the STDIN, STDOUT, and STDERR&lt;/li&gt;
&lt;li&gt;Pipelines: integrate two processes by creating a pipe between the STDOUT from one to the STDIN of the other&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additional features are available but no covered in the current tutorial (jobs, signals, traps, parallelism, etc.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing Functions
&lt;/h2&gt;

&lt;p&gt;To declare a function in Bash use the following structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt; &amp;lt;FUNCTION_NAME&amp;gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &amp;lt;COMMANDS&amp;gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &amp;lt;EXIT_STATUS&amp;gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Functions in Bash behave in a similar way to scripts and commands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can be called directly from the script or command line&lt;/li&gt;
&lt;li&gt;Use positional parameters that are automatically assigned to &lt;code&gt;$N&lt;/code&gt; variables&lt;/li&gt;
&lt;li&gt;Have exit status&lt;/li&gt;
&lt;li&gt;Can send data to STDOUT and STDERR&lt;/li&gt;
&lt;li&gt;Can receive data from STDIN&lt;/li&gt;
&lt;li&gt;Can be used in complex command sequences (&lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;, &lt;code&gt;||&lt;/code&gt;, &lt;code&gt;|&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's create a function with all the concepts seen so far:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;readonly &lt;/span&gt;&lt;span class="nv"&gt;TEST_READONLY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'content of this variable is constant and can not be modified afterwords'&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TEST_EXPORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'content of this variable is visible everywhere'&lt;/span&gt;
&lt;span class="nb"&gt;declare &lt;/span&gt;&lt;span class="nv"&gt;test_declare&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'this variable is declared at the script level'&lt;/span&gt;
&lt;span class="nb"&gt;declare&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nv"&gt;function_result&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0

&lt;span class="k"&gt;function &lt;/span&gt;test_function&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;test_parameter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;test_parameter_default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;2&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;default&lt;/span&gt;&lt;span class="p"&gt;-value&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;test_local&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'this variable is available only inside this function'&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;test_static&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;  &lt;span class="c"&gt;# initialize a static variable&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;test_dynamic&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="c"&gt;# initialize a dynamic variable&lt;/span&gt;

  &lt;span class="nv"&gt;test_static&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'this variable has static content'&lt;/span&gt;
  &lt;span class="nv"&gt;test_dynamic&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'this dynamic variable is assigned at execution time'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'Show variable content from inside a function:\n\n'&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'  first parameter: [%s]\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;test_parameter&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'  second parameter, default value: [%s]\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;test_parameter_default&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'  local static variable: [%s]\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;test_static&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'  local dynamic variable: [%s]\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;test_dynamic&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'  exported variable: [%s]\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TEST_EXPORT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'  readonly variable: [%s]\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TEST_READONLY&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'  declared variable: [%s]\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;test_declare&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;5

&lt;span class="o"&gt;}&lt;/span&gt;

test_function &lt;span class="s1"&gt;'this value is assigned to the first parameter'&lt;/span&gt;
&lt;span class="nv"&gt;function_result&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt;

&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'\nShow the same variables but outside the function:\n\n'&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'  local variable: [%s]\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;test_local&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'  local static variable: [%s]\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;test_static&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'  local dynamic variable: [%s]\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;test_dynamic&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'  exported variable: [%s]\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TEST_EXPORT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'  readonly variable: [%s]\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TEST_READONLY&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'  declared variable: [%s]\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;test_declare&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'\nShow the exit status (return value) of the function: [%s]\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;function_result&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'\nShow current PID of the Bash run-time: [%s]\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BASHPID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;Continue reading the second part of the tutorial: &lt;a href="https://dev.to/serdigital64/bash-scripting-concepts-part-2-of-2-3j0f"&gt;Bash Scripting Concepts: Part 2 of 2&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Copyright information
&lt;/h2&gt;

&lt;p&gt;This article is licensed under a &lt;a href="http://creativecommons.org/licenses/by/4.0/"&gt;Creative Commons Attribution 4.0 International License&lt;/a&gt;. For copyright information on the product or products mentioned inhere refer to their respective owner.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;p&gt;Opinions presented in this article are personal and belong solely to me, and do not represent people or organizations associated with me in a professional or personal way. All the information on this site is provided "as is" with no guarantee of completeness, accuracy or the results obtained from the use of this information.&lt;/p&gt;

</description>
      <category>bash</category>
    </item>
    <item>
      <title>Implementing infrastructure-as-code with Ansible and GIT</title>
      <dc:creator>SerDigital64</dc:creator>
      <pubDate>Fri, 01 Oct 2021 22:17:04 +0000</pubDate>
      <link>https://dev.to/serdigital64/implementing-infrastructure-as-code-with-ansible-and-git-48ng</link>
      <guid>https://dev.to/serdigital64/implementing-infrastructure-as-code-with-ansible-and-git-48ng</guid>
      <description>&lt;h2&gt;
  
  
  What is "infrastructure-as-code"?
&lt;/h2&gt;

&lt;p&gt;To keep the concept simple, think of your infrastructure as a picture (end-state) and the main characteristics (configuration) that you would use to describe it.&lt;/p&gt;

&lt;p&gt;Using this information you should be able to reproduce the picture any time you need, and the result should always be the same as the original.&lt;/p&gt;

&lt;p&gt;There are several tools and methods to implement this approach but the most important thing to consider is that you also need to adapt your management procedures to switch from the traditional imperative model to the infrastructure-as-code declarative model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges
&lt;/h2&gt;

&lt;p&gt;To successfully implement infrastructure-as-code special attention must be paid to the selection of supporting tools and the definition of the conceptual model that will represent the managed environment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automation tool: takes control of the infrastructure configuration and performs the necessary actions to reach the desired end-state.&lt;/li&gt;
&lt;li&gt;Code Repository and Versioning: stores the infrastructure-model and automation scripts to manage the infrastructure and tracks changes.&lt;/li&gt;
&lt;li&gt;Infrastructure-model: conceptual data model that describes the desired end-state of the infrastructure.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Implementing infrastructure-as-code
&lt;/h2&gt;

&lt;p&gt;Let's take the following example scenario to demonstrate the implementation procedure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Environment: Home Office&lt;/li&gt;
&lt;li&gt;Ansible Control Node: small PC or VM installed with Centos 8.4&lt;/li&gt;
&lt;li&gt;Ansible Managed Nodes: notebooks with Ubuntu and workstations with Centos and Fedora&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.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%2F0xmvc0pim74ce01aoenw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F0xmvc0pim74ce01aoenw.png" alt="Home Office Site"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before starting the implementation procedure, make sure the following requirements are meet:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Control Node:

&lt;ul&gt;
&lt;li&gt;OpenSSH client&lt;/li&gt;
&lt;li&gt;Sudo&lt;/li&gt;
&lt;li&gt;Python3&lt;/li&gt;
&lt;li&gt;GIT&lt;/li&gt;
&lt;li&gt;Regular user with SUDO configured for password less root privilege&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Managed Nodes:

&lt;ul&gt;
&lt;li&gt;Fresh OS install (standard setup)&lt;/li&gt;
&lt;li&gt;OpenSSH server&lt;/li&gt;
&lt;li&gt;Sudo&lt;/li&gt;
&lt;li&gt;Python3&lt;/li&gt;
&lt;li&gt;Regular user with SUDO configured for password less root privilege&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Define the Infrastructure Model
&lt;/h3&gt;

&lt;p&gt;The infrastructure-model will have the following data structures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Site&lt;/strong&gt;: Represents a group of &lt;strong&gt;Nodes&lt;/strong&gt; that are managed by the same &lt;strong&gt;Control Node&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Node&lt;/strong&gt;: Compute node that is capable of hosting software components and that is fully managed by the Control Node.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Component&lt;/strong&gt;: Individual software product that is installed in a &lt;strong&gt;Node&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service&lt;/strong&gt;: Group of &lt;strong&gt;Components&lt;/strong&gt; configured in one or more &lt;strong&gt;Nodes&lt;/strong&gt; to serve a particular function.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The data structures will be implemented using Ansible inventories, host_vars, and group_vars:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Inventory&lt;/strong&gt;: the &lt;code&gt;hosts.ini&lt;/code&gt; file will be used to declare all the &lt;strong&gt;Nodes&lt;/strong&gt; for the target &lt;strong&gt;Site&lt;/strong&gt;. &lt;strong&gt;Nodes&lt;/strong&gt; will be grouped based on the &lt;strong&gt;Service&lt;/strong&gt; they provide or use.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GroupVars&lt;/strong&gt;: individual YAML files for &lt;strong&gt;Components&lt;/strong&gt; and &lt;strong&gt;Services&lt;/strong&gt; will be created for each &lt;strong&gt;Node&lt;/strong&gt; group declared in the &lt;strong&gt;Inventory&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HostVars&lt;/strong&gt;: individual YAML files will be used for cases where the &lt;strong&gt;Node&lt;/strong&gt; requires further customization.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Create the Code Repository
&lt;/h3&gt;

&lt;p&gt;Create a dedicated Linux account. This is to isolate content from regular users and facilitate activity auditing. Modify the shell variable &lt;code&gt;PROJECT_OWNER&lt;/code&gt; to change the default name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;PROJECT_OWNER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'sitectl'&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;useradd &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROJECT_OWNER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;su - &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROJECT_OWNER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the project directory structure that will contain the infrastructure-model and automation scripts. Refer to the &lt;a href="https://docs.ansible.com/ansible/2.8/user_guide/playbooks_best_practices.html#directory-layout" rel="noopener noreferrer"&gt;Ansible Best Practices&lt;/a&gt; document to further learn about the directory structure.&lt;br&gt;
Modify the shell variable &lt;code&gt;PROJECT_PATH&lt;/code&gt; to change the default project location.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PROJECT_OWNER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'sitectl'&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PROJECT_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/home/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/manager"&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROJECT_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROJECT_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'collections'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'files'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'inventories'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'inventories/group_vars'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'inventories/host_vars'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'playbooks'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'vars'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'etc'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'filter_plugins'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'library'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'module_utils'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'roles'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'var'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'templates'&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a simple shell script to set environment variables for Ansible. Refer to the &lt;a href="https://docs.ansible.com/ansible/latest/reference_appendices/config.html" rel="noopener noreferrer"&gt;Ansible Configuration&lt;/a&gt; documentation to understand what are the &lt;code&gt;ANSIBLE_*&lt;/code&gt; shell variables doing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'load_environment.sh'&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;EEOF&lt;/span&gt;&lt;span class="sh"&gt;
#!/bin/bash

declare -x PROJECT_OWNER='&lt;/span&gt;&lt;span class="nv"&gt;$PROJECT_OWNER&lt;/span&gt;&lt;span class="sh"&gt;'
declare -x PROJECT_PATH='&lt;/span&gt;&lt;span class="nv"&gt;$PROJECT_PATH&lt;/span&gt;&lt;span class="sh"&gt;'
declare -x PROJECT_END_STATE='&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_PATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;/inventories'

declare -x ANSIBLE_INVENTORY="&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="sh"&gt;{PROJECT_END_STATE}/hosts.ini"
declare -x ANSIBLE_PRIVATE_KEY_FILE="/home/&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="sh"&gt;{PROJECT_OWNER}/.ssh/id_rsa"
declare -x ANSIBLE_COLLECTIONS_PATHS="&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="sh"&gt;{PROJECT_PATH}/collections"
declare -x ANSIBLE_ROLES_PATH="&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="sh"&gt;{PROJECT_PATH}/roles"
declare -x ANSIBLE_GALAXY_CACHE_DIR="&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="sh"&gt;{PROJECT_PATH}/var"
declare -x ANSIBLE_LOG_PATH="&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="sh"&gt;{PROJECT_PATH}/var/ansible.log"
declare -x ANSIBLE_PYTHON_INTERPRETER='/usr/bin/python3.9'
declare -x ANSIBLE_PLAYBOOK_DIR="&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="sh"&gt;{PROJECT_PATH}/playbooks"

PATH='/home/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;/.local/bin:/usr/bin:/usr/sbin'
&lt;/span&gt;&lt;span class="no"&gt;
EEOF

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the Code Repository using GIT. Configure the &lt;code&gt;.gitignore&lt;/code&gt; file to avoid tracking changes in the ansible-galaxy install location target (&lt;code&gt;collections/&lt;/code&gt;) and in the repository for temporary and volatile files (&lt;code&gt;/var&lt;/code&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source &lt;/span&gt;load_environment.sh
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'.gitignore'&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;EEOF&lt;/span&gt;&lt;span class="sh"&gt;
collections/
var/
&lt;/span&gt;&lt;span class="no"&gt;
EEOF
&lt;/span&gt;git config user.email &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_OWNER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;@localhost.localdomain"&lt;/span&gt;
git config user.name &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROJECT_OWNER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
git init
git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Initial commit"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configure Ansible
&lt;/h3&gt;

&lt;p&gt;Install the latest Ansible engine to the control node user. This method keeps the environment isolated, minimizes os-distribution dependencies, and facilitates module upgrades.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ANSIBLE_PYTHON_INTERPRETER&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;ansible

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prepare remote access to Ansible Managed Nodes using OpenSSH keys. Modify the shell variable &lt;code&gt;PROJECT_MANAGED_NODES&lt;/code&gt; to represent your environment and set the &lt;code&gt;PROJECT_MANAGED_USER&lt;/code&gt; variable to the remote user name with password-less root privilege.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;PROJECT_MANAGED_NODES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'host1 host2 host3 host4'&lt;/span&gt;
&lt;span class="nv"&gt;PROJECT_MANAGED_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'sysadmin'&lt;/span&gt;
ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; rsa &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ANSIBLE_PRIVATE_KEY_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-N&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;x &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$PROJECT_MANAGED_NODES&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;ssh-copy-id &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ANSIBLE_PRIVATE_KEY_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_MANAGED_USER&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;@&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the initial Ansible inventory registering the following &lt;strong&gt;Node&lt;/strong&gt; groups:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;[control_node]&lt;/code&gt;: defines the Ansible Node.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[managed_nodes]&lt;/code&gt;: defines Ansible Managed nodes for the target site.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[office_nodes]&lt;/code&gt;: defines &lt;strong&gt;Nodes&lt;/strong&gt; that will consume the &lt;strong&gt;office-service&lt;/strong&gt;. The service provides users with common productivity applications. For this example, we'll use the image editor GIMP.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ANSIBLE_INVENTORY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;EEOF&lt;/span&gt;&lt;span class="sh"&gt;
[control_node]
localhost

[managed_nodes]
&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;x &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$PROJECT_MANAGED_NODES&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$x&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;

[office_nodes]
&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;x &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$PROJECT_MANAGED_NODES&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$x&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;
EEOF

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create end-state configuration repositories. This is where the infrastructure-model will be implemented.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;group_vars/&lt;/code&gt;: one directory per host group&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;group_vars/host_group_x/&lt;/code&gt;: one or more YAML files representing the components that will be available for all hosts in the group&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;host_vars/&lt;/code&gt;: one directory per host&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;host_vars/hostx/&lt;/code&gt;: one or more YAML files representing the components that will be available for the host
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;x &lt;span class="k"&gt;in &lt;/span&gt;control_node managed_nodes office_nodes&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_END_STATE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/group_vars/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done
for &lt;/span&gt;x &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$PROJECT_MANAGED_NODES&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_END_STATE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/host_vars/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Define component end-states
&lt;/h3&gt;

&lt;p&gt;Now that the data repository for the infrastructure-model is created it can be populated with end-state targets. Notice that you can also add behaviour definitions to keep variable data separated from the code.&lt;/p&gt;

&lt;p&gt;Define how is the Ansible engine going to connect to the managed nodes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROJECT_END_STATE&lt;/span&gt;&lt;span class="s2"&gt;/group_vars/managed_nodes/ansible.yml"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;EEOF&lt;/span&gt;&lt;span class="sh"&gt;
---
ansible_user: "&lt;/span&gt;&lt;span class="nv"&gt;$PROJECT_MANAGED_USER&lt;/span&gt;&lt;span class="sh"&gt;"
ansible_become_method: "sudo"
...
&lt;/span&gt;&lt;span class="no"&gt;
EEOF

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Define the attributes for the &lt;code&gt;Linux Users&lt;/code&gt; component. This definition will be applied to all hosts in the group &lt;code&gt;managed_nodes&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROJECT_END_STATE&lt;/span&gt;&lt;span class="s2"&gt;/group_vars/managed_nodes/users.yml"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;EEOF&lt;/span&gt;&lt;span class="sh"&gt;
---
managed_nodes_users:
  - name: "user1"
    description: "Regular User 1"
    uid: "10100"
  - name: "user2"
    description: "Regular User 2"
    uid: "10101"
...
&lt;/span&gt;&lt;span class="no"&gt;
EEOF

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Define the attributes for the &lt;code&gt;Linux Package&lt;/code&gt; component. This definition will be applied to all hosts in the group &lt;code&gt;office_nodes&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PROJECT_END_STATE&lt;/span&gt;&lt;span class="s2"&gt;/group_vars/office_nodes/packages.yml"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;EEOF&lt;/span&gt;&lt;span class="sh"&gt;
---
office_nodes_packages:
  flatpak:
    - "flatpak"
  gimp:
    - "org.gimp.GIMP"
...
&lt;/span&gt;&lt;span class="no"&gt;
EEOF

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Bring the site to the target end-state
&lt;/h3&gt;

&lt;p&gt;At this point end-state, and behaviour definitions are set. Now it's time to write the code that will apply it to the target hosts.&lt;/p&gt;

&lt;p&gt;Create the Ansible Playbook that will configure the &lt;code&gt;managed_nodes&lt;/code&gt; host group:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ANSIBLE_PLAYBOOK_DIR&lt;/span&gt;&lt;span class="s2"&gt;/managed_nodes.yml"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;EEOF&lt;/span&gt;&lt;span class="sh"&gt;
---
- name: "Manage Ansible Nodes"
  hosts: "managed_nodes"
  gather_facts: false

  tasks:
    - name: "Create Regular User Accounts"
      become: true
      ansible.builtin.user:
        create_home: true
        state: "present"
        name: "{{ item['name'] }}"
        comment: "{{ item['description'] | default( omit ) }}"
        uid: "{{ item['uid'] | default( omit ) }}"
      loop: "{{ managed_nodes_users }}"
...
&lt;/span&gt;&lt;span class="no"&gt;
EEOF

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the Ansible Playbook that will configure the &lt;code&gt;office_nodes&lt;/code&gt; host group:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ANSIBLE_PLAYBOOK_DIR&lt;/span&gt;&lt;span class="s2"&gt;/office_nodes.yml"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;EEOF&lt;/span&gt;&lt;span class="sh"&gt;
---
- name: "Manage Office Nodes"
  hosts: "office_nodes"
  gather_facts: false

  pre_tasks:

    - name: "Install FlatPak tools"
      become: true
      ansible.builtin.package:
        name: "{{ office_nodes_packages['flatpak'] }}"
        state: "present"

    - name: "Prepare FlatPak repository"
      become: true
      ansible.builtin.command:
        argv:
          - "/usr/bin/flatpak"
          - "--system"
          - "remote-add"
          - "flatpak"
          - "https://flathub.org/repo/flathub.flatpakrepo"
      register: result
      changed_when:
        - result['rc'] == 0

  tasks:
    - name: "Install GIMP from FlatHub"
      become: true
      community.general.flatpak:
        name: "{{ office_nodes_packages['gimp'] }}"
        state: "present"
...
&lt;/span&gt;&lt;span class="no"&gt;
EEOF

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Execute the playbooks to apply the end-state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ansible-playbook playbooks/managed_nodes.yml
ansible-playbook playbooks/office_nodes.yml

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save the changes to the repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add inventories
git add playbooks
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"add office_node and managed_node plays"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Now the base structure is up and running more content can be added, either from Ansible Galaxy or developed in-house.&lt;/p&gt;

&lt;p&gt;In addition to the automation engine and code repository you should evaluate incorporating:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code linters: &lt;a href="https://ansible-lint.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;Ansible Lint&lt;/a&gt; and &lt;a href="https://yamllint.readthedocs.io/en/stable/" rel="noopener noreferrer"&gt;YAMLlint&lt;/a&gt; can help to keep code consistent and standardized.&lt;/li&gt;
&lt;li&gt;Testing: &lt;a href="https://molecule.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;Ansible Molecule&lt;/a&gt; can be used to build and run test environments for testing in-house roles&lt;/li&gt;
&lt;li&gt;Provisioning: &lt;a href="https://www.terraform.io/docs/index.html" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; can be used to automate the creation of standardized VMs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Explore the &lt;a href="https://readthedocs.org/projects/aplatform64/" rel="noopener noreferrer"&gt;A:Platform64&lt;/a&gt; project that facilitates the implementation of infrastructure-as-code by automating most of the tasks described in this tutorial.&lt;/p&gt;

&lt;h2&gt;
  
  
  Copyright information
&lt;/h2&gt;

&lt;p&gt;This article is licensed under a &lt;a href="http://creativecommons.org/licenses/by/4.0/" rel="noopener noreferrer"&gt;Creative Commons Attribution 4.0 International License&lt;/a&gt;. For copyright information on the product or products mentioned inhere refer to their respective owner.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;p&gt;Opinions presented in this article are personal and belong solely to me, and do not represent people or organizations associated with me in a professional or personal way. All the information on this site is provided "as is" with no guarantee of completeness, accuracy or the results obtained from the use of this information.&lt;/p&gt;

</description>
      <category>ansible</category>
      <category>linux</category>
      <category>automation</category>
      <category>devops</category>
    </item>
    <item>
      <title>Ansible Concepts</title>
      <dc:creator>SerDigital64</dc:creator>
      <pubDate>Sat, 17 Jul 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/serdigital64/ansible-concepts-for-sysadmins-1c8p</link>
      <guid>https://dev.to/serdigital64/ansible-concepts-for-sysadmins-1c8p</guid>
      <description>&lt;h2&gt;
  
  
  Ansible Overview
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Ansible&lt;/strong&gt; is an automation engine that executes tasks described in &lt;strong&gt;YAML&lt;/strong&gt; + &lt;strong&gt;Jinja2&lt;/strong&gt; to bring the target system to a known state.&lt;/p&gt;

&lt;p&gt;At its core, &lt;strong&gt;Ansible&lt;/strong&gt; is a python module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;(&lt;/span&gt;ansible&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;sysadmin@cnt84 ~]&lt;span class="nv"&gt;$ &lt;/span&gt;pip list | egrep &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="s1"&gt;'ansible|jinja|yaml|markup'&lt;/span&gt;
ansible      4.3.0
ansible-core 2.11.3
Jinja2       3.0.1
MarkupSafe   2.0.1
PyYAML       5.4.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The engine along with other helpers are available as stand-alone programs that can be run directly from the command line:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ansible&lt;/code&gt; : used to run individual tasks.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ansible-playbook&lt;/code&gt; : used to execute a batch of tasks in multiple targets.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ansible-config&lt;/code&gt; : used to define engine configuration settings.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ansible-doc&lt;/code&gt; : used to show documentation of several ansible components.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ansible-galaxy&lt;/code&gt; : used to interact with the ansible packaging system.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Ansible Tasks
&lt;/h2&gt;

&lt;p&gt;The minimum execution unit in &lt;strong&gt;Ansible&lt;/strong&gt; is a task. Each task will focus on managing a particular component of the target host.&lt;/p&gt;

&lt;p&gt;The main difference between an &lt;strong&gt;Ansible Task&lt;/strong&gt; and traditional shell script commands is that the task defines the &lt;em&gt;what&lt;/em&gt; (target state) and not the &lt;em&gt;how&lt;/em&gt; (action).&lt;/p&gt;

&lt;p&gt;For example, to set file permissions using a &lt;strong&gt;Bash&lt;/strong&gt; script you need to define &lt;em&gt;how&lt;/em&gt; it will be done:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Example: Set file permissions"&lt;/span&gt;
/usr/bin/chmod &lt;span class="s1"&gt;'0644'&lt;/span&gt; &lt;span class="s1"&gt;'/tmp/test.file'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To achieve the same result using &lt;strong&gt;Ansible Tasks&lt;/strong&gt; you define &lt;em&gt;what&lt;/em&gt; will be the end-state of the component:&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="s"&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Example:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Set&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;file&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;permissions"&lt;/span&gt;
  &lt;span class="na"&gt;ansible.builtin.file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0644"&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/tmp/test.file"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here the component is the Linux file &lt;code&gt;/tmp/test.file&lt;/code&gt; managed by the &lt;code&gt;ansible.builtin.file&lt;/code&gt; &lt;strong&gt;Ansible Module&lt;/strong&gt; and the end-state is defined by the &lt;code&gt;mode&lt;/code&gt; attribute.&lt;/p&gt;

&lt;p&gt;See below common shell script actions and their equivalent &lt;strong&gt;Ansible Tasks&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Bash&lt;/th&gt;
&lt;th&gt;Ansible&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Action: Copy FileA to DestinationB&lt;/td&gt;
&lt;td&gt;Target State: FileA must be present in DestinationB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Action: Set FileA Owner to UserX&lt;/td&gt;
&lt;td&gt;Target State: UserX must own FileA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Action: Start the ServiceX&lt;/td&gt;
&lt;td&gt;Target State: ServiceX must be in started state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Action: Install PackageX&lt;/td&gt;
&lt;td&gt;Target State: PackageX must be installed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Ansible Modules
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Ansible Modules&lt;/strong&gt; are used to represent components in &lt;strong&gt;Ansible Tasks&lt;/strong&gt; and are intended to hide the complexity of how the component is manipulated to achieve the desired end-state.&lt;/p&gt;

&lt;p&gt;A set of modules are already included in &lt;strong&gt;Ansible&lt;/strong&gt; for managing common components. For example:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Module Name&lt;/th&gt;
&lt;th&gt;End-State&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Linux File&lt;/td&gt;
&lt;td&gt;copy&lt;/td&gt;
&lt;td&gt;Source File is present in the Target Path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linux File&lt;/td&gt;
&lt;td&gt;file&lt;/td&gt;
&lt;td&gt;File attributes are set (ownership, permissions, etc)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linux File&lt;/td&gt;
&lt;td&gt;lineinfile&lt;/td&gt;
&lt;td&gt;The text line is included (or not) in the target file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linux OS Package&lt;/td&gt;
&lt;td&gt;package&lt;/td&gt;
&lt;td&gt;The OS package is present (or not) in the target host&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linux OS Service&lt;/td&gt;
&lt;td&gt;service&lt;/td&gt;
&lt;td&gt;The OS service is started (or not) in the target host&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OS User&lt;/td&gt;
&lt;td&gt;user&lt;/td&gt;
&lt;td&gt;OS User is created (or not) in the target host&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenSSH&lt;/td&gt;
&lt;td&gt;authorized_keys&lt;/td&gt;
&lt;td&gt;OpenSSH key is present (or not) in the authorized_keys file&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Additional modules developed by product owners or the OSS community are available at the &lt;em&gt;Ansible Galaxy&lt;/em&gt; site and can be installed using the &lt;code&gt;ansible-galaxy&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;For example, to add the &lt;code&gt;openssh_keypair&lt;/code&gt; &lt;strong&gt;Ansible Module&lt;/strong&gt; included in the community provided &lt;code&gt;community.crypto.openssh_keypair&lt;/code&gt; &lt;strong&gt;Ansible Collection&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;(&lt;/span&gt;ansible&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;sysadmin@cnt84 ~]&lt;span class="nv"&gt;$ &lt;/span&gt;ansible-galaxy collection &lt;span class="nb"&gt;install &lt;/span&gt;community.crypto
Starting galaxy collection &lt;span class="nb"&gt;install &lt;/span&gt;process
Process &lt;span class="nb"&gt;install &lt;/span&gt;dependency map
Starting collection &lt;span class="nb"&gt;install &lt;/span&gt;process
Downloading https://galaxy.ansible.com/download/community-crypto-1.7.1.tar.gz to /home/sysadmin/.ansible/tmp/ansible-local-321219he000qkm/tmpd12y1_sb/community-crypto-1.7.1-xrp950jz
Installing &lt;span class="s1"&gt;'community.crypto:1.7.1'&lt;/span&gt; to &lt;span class="s1"&gt;'/home/sysadmin/.ansible/collections/ansible_collections/community/crypto'&lt;/span&gt;
community.crypto:1.7.1 was installed successfully

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Ansible Playbooks
&lt;/h2&gt;

&lt;p&gt;Similar to what scripts are for traditional shells like &lt;em&gt;bash&lt;/em&gt;, &lt;strong&gt;Ansible Playbooks&lt;/strong&gt; are &lt;em&gt;YAML&lt;/em&gt; files used to create automation jobs.&lt;/p&gt;

&lt;p&gt;The minimum &lt;strong&gt;Ansible Playbook&lt;/strong&gt; contains one or more &lt;strong&gt;Ansible Tasks&lt;/strong&gt; and the explicit declaration of the target host where the end-state will be set.&lt;/p&gt;

&lt;p&gt;The following example playbook sets &lt;code&gt;test-server21&lt;/code&gt; as the target host where the end-state for the package component will be set. In this case, the desired end-state is to have the package &lt;code&gt;lsof&lt;/code&gt; installed.&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;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test-server21&lt;/span&gt;
  &lt;span class="na"&gt;tasks&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Deploy&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;LSOF&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;tool"&lt;/span&gt;
      &lt;span class="na"&gt;ansible.builtin.package&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="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lsof"&lt;/span&gt;
      &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;present"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Ansible Playbooks&lt;/strong&gt; can contain additional features to facilitate the creation of complex automation jobs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Job management: defines how tasks are going to be executed based on the number of target hosts (sequentially, in parallel, etc.)&lt;/li&gt;
&lt;li&gt;Error handling: provides features for recovering from failed tasks.&lt;/li&gt;
&lt;li&gt;Event Handlers: defines special tasks that are only executed when certain conditions are met. For example, the application X configuration reload handler is executed when the configuration update task sets a new parameter value&lt;/li&gt;
&lt;li&gt;External Variable Definition: allows the inclusion of &lt;strong&gt;YAML&lt;/strong&gt; files that contains variables definitions only.&lt;/li&gt;
&lt;li&gt;Roles: allows the inclusion of roles. Roles are similar to &lt;strong&gt;Ansible Modules&lt;/strong&gt; but implemented using &lt;strong&gt;Ansible Tasks&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To run &lt;strong&gt;Ansible Playbooks&lt;/strong&gt; use the command &lt;code&gt;ansible-playbook&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;(&lt;/span&gt;ansible&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;sysadmin@cnt84 ~]&lt;span class="nv"&gt;$ &lt;/span&gt;ansible-playbook hello_world.yml

PLAY &lt;span class="o"&gt;[&lt;/span&gt;localhost] &lt;span class="k"&gt;*****************************************************************************************************&lt;/span&gt;

TASK &lt;span class="o"&gt;[&lt;/span&gt;Gathering Facts] &lt;span class="k"&gt;***********************************************************************************************&lt;/span&gt;
ok: &lt;span class="o"&gt;[&lt;/span&gt;localhost]

TASK &lt;span class="o"&gt;[&lt;/span&gt;Hello World] &lt;span class="k"&gt;***************************************************************************************************&lt;/span&gt;
ok: &lt;span class="o"&gt;[&lt;/span&gt;localhost] &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"msg"&lt;/span&gt;: &lt;span class="s2"&gt;"Hello World"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

PLAY RECAP &lt;span class="k"&gt;***********************************************************************************************************&lt;/span&gt;
localhost                  : &lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2    &lt;span class="nv"&gt;changed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0    &lt;span class="nv"&gt;unreachable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0    &lt;span class="nv"&gt;failed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0    &lt;span class="nv"&gt;skipped&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0    &lt;span class="nv"&gt;rescued&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0    &lt;span class="nv"&gt;ignored&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  YAML and Jinja
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Ansible&lt;/strong&gt; doesn't have a language on its own for defining tasks.&lt;br&gt;
Instead, it uses the data definition language &lt;strong&gt;YAML&lt;/strong&gt; for declaring desired end-state and the template engine &lt;strong&gt;Jinja2&lt;/strong&gt; for embedding simple programming functions into &lt;strong&gt;YAML&lt;/strong&gt; files.&lt;/p&gt;

&lt;p&gt;For example, let's define the &lt;code&gt;file_path&lt;/code&gt; variable in a stand-alone &lt;strong&gt;YAML&lt;/strong&gt; file:&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="s"&gt;--------&lt;/span&gt;
&lt;span class="na"&gt;file_path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/tmp/test.file"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can use the &lt;code&gt;file_path&lt;/code&gt; variable to set the &lt;code&gt;path&lt;/code&gt; attribute.&lt;br&gt;
To use the variable a &lt;strong&gt;Jinja2&lt;/strong&gt; template must be added to the &lt;strong&gt;YAML&lt;/strong&gt; declaration using the expression container &lt;code&gt;"{{ }}"&lt;/code&gt;:&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="s"&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Example:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Include&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;variable&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;from&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;file"&lt;/span&gt;
  &lt;span class="na"&gt;include_vars&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;example_var_definition.yml"&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Example:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Set&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;file&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;permissions"&lt;/span&gt;
  &lt;span class="na"&gt;ansible.builtin.file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0644"&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;file_path&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Ansible Infrastructure
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Ansible&lt;/strong&gt; defines two types of infrastructure components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Control Node&lt;/em&gt;: central compute node where the automation engine is installed and from where &lt;strong&gt;Ansible Tasks&lt;/strong&gt; are executed.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Managed Node&lt;/em&gt;: target node that &lt;strong&gt;Ansible Tasks&lt;/strong&gt; will manage to reach the defined end-state. Nodes can be regular compute nodes (RedHat Linux Enterprise, Ubuntu, etc.) or non-compute nodes such as storage devices, network devices, appliances, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Communication between &lt;em&gt;Control Nodes&lt;/em&gt; and &lt;em&gt;Managed Nodes&lt;/em&gt; is implemented using &lt;strong&gt;Connection Plugins&lt;/strong&gt;. The default plugin for Linux nodes is &lt;em&gt;ssh&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Use the command &lt;code&gt;ansible-doc -t connection -l&lt;/code&gt; to show available plugins:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;(&lt;/span&gt;ansible&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;sysadmin@cnt84 ~]&lt;span class="nv"&gt;$ &lt;/span&gt;ansible-doc &lt;span class="nt"&gt;-t&lt;/span&gt; connection &lt;span class="nt"&gt;-l&lt;/span&gt; | egrep &lt;span class="s1"&gt;'^ssh'&lt;/span&gt;
ssh                            connect via ssh client binary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For tasks that requires privileged access to manage the component &lt;strong&gt;Ansible&lt;/strong&gt; provides &lt;strong&gt;Become Plugins&lt;/strong&gt;. The default plugin for Linux based nodes is &lt;em&gt;sudo&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Use the command &lt;code&gt;ansible-doc -t become -l&lt;/code&gt; to show available plugins:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;(&lt;/span&gt;ansible&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;sysadmin@cnt84 ~]&lt;span class="nv"&gt;$ &lt;/span&gt;ansible-doc &lt;span class="nt"&gt;-t&lt;/span&gt; become &lt;span class="nt"&gt;-l&lt;/span&gt; | egrep &lt;span class="s1"&gt;'^sudo|^su'&lt;/span&gt;
su                           Substitute User
&lt;span class="nb"&gt;sudo                         &lt;/span&gt;Substitute User DO
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Content Organization
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Ansible&lt;/strong&gt; doesn't enforce a strict directory structure for content organization. Instead, it provides configuration parameters that can be used to define locations based on the resource type.&lt;/p&gt;

&lt;p&gt;The following is a basic directory structure that can be used for simple to medium size deployments:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Path&lt;/th&gt;
&lt;th&gt;Content&lt;/th&gt;
&lt;th&gt;Ansible Parameter&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;etc/&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Ansible&lt;/strong&gt; configuration files&lt;/td&gt;
&lt;td&gt;ANSIBLE_CONFIG&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;files/&lt;/td&gt;
&lt;td&gt;Site wide data files&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;inventories/&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Ansible Playbooks&lt;/strong&gt; inventory files, host_vars and group_vars&lt;/td&gt;
&lt;td&gt;ANSIBLE_INVENTORY&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;collections/&lt;/td&gt;
&lt;td&gt;Collections installed from Ansible-Galaxy&lt;/td&gt;
&lt;td&gt;ANSIBLE_COLLECTIONS_PATHS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;roles/&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Ansible Roles&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ANSIBLE_ROLES_PATH&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;playbooks/&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Ansible Playbooks&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ANSIBLE_PLAYBOOK_DIR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;logs/&lt;/td&gt;
&lt;td&gt;Execution logs&lt;/td&gt;
&lt;td&gt;ANSIBLE_LOG_PATH&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Once resources are organized you should consider managing the content using a version control system like GIT. This is key to implement the &lt;em&gt;infrastructure as code&lt;/em&gt; strategy.&lt;/p&gt;

&lt;h2&gt;
  
  
  References &amp;amp; Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Ansible Community Documentation: &lt;a href="https://docs.ansible.com/ansible_community.html"&gt;https://docs.ansible.com/ansible_community.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Ansible Galaxy: &lt;a href="https://galaxy.ansible.com/"&gt;https://galaxy.ansible.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Jinja2: &lt;a href="https://jinja.palletsprojects.com/en/3.0.x/"&gt;https://jinja.palletsprojects.com/en/3.0.x&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;YAML: &lt;a href="https://yaml.org/spec/"&gt;https://yaml.org/spec&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GIT: &lt;a href="https://git-scm.com/"&gt;https://git-scm.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Infrastructure-as-code Tutorial: &lt;a href="https://serdigital64.github.io/post/ansible/implementing-infrastructure-as-code-with-ansible-and-git/"&gt;https://serdigital64.github.io&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A:Platform64 Automation Platform: &lt;a href="https://serdigital64.github.io/post/projects/project-aplatform64/"&gt;https://serdigital64.github.io&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Copyright information
&lt;/h2&gt;

&lt;p&gt;This article is licensed under a &lt;a href="http://creativecommons.org/licenses/by/4.0/"&gt;Creative Commons Attribution 4.0 International License&lt;/a&gt;. For copyright information on the product or products mentioned inhere refer to their respective owner.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;p&gt;Opinions presented in this article are personal and belong solely to me, and do not represent people or organizations associated with me in a professional or personal way. All the information on this site is provided "as is" with no guarantee of completeness, accuracy or the results obtained from the use of this information.&lt;/p&gt;

</description>
      <category>ansible</category>
      <category>linux</category>
      <category>automation</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
