<?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>Forem: Mickaël Canévet</title>
    <description>The latest articles on Forem by Mickaël Canévet (@mcanevet).</description>
    <link>https://forem.com/mcanevet</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%2F376124%2Fcd04fcc7-0412-4576-99e0-e44b2b664531.jpeg</url>
      <title>Forem: Mickaël Canévet</title>
      <link>https://forem.com/mcanevet</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/mcanevet"/>
    <language>en</language>
    <item>
      <title>Terraform Project Design — A parallel with Puppet</title>
      <dc:creator>Mickaël Canévet</dc:creator>
      <pubDate>Mon, 03 May 2021 11:57:38 +0000</pubDate>
      <link>https://forem.com/camptocamp-ops/terraform-project-design-a-parallel-with-puppet-1f8o</link>
      <guid>https://forem.com/camptocamp-ops/terraform-project-design-a-parallel-with-puppet-1f8o</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Like Puppet, Terraform provides low-level objects written in a programming language that allows you to manage individual resources.&lt;/p&gt;

&lt;p&gt;With &lt;em&gt;Puppet&lt;/em&gt;, these low-level objects, the &lt;a href="https://puppet.com/docs/puppet/7.6/cheatsheet_core_types.html" rel="noopener noreferrer"&gt;Puppet Types and Providers&lt;/a&gt;, written in Ruby, allow you to apply the CRUD paradigm to resources located on managed nodes.&lt;/p&gt;

&lt;p&gt;With &lt;em&gt;Terraform&lt;/em&gt;, these low-level objects, the &lt;a href="https://www.terraform.io/docs/language/resources/index.html" rel="noopener noreferrer"&gt;Terraform Resources&lt;/a&gt;, written in Go, allow you to apply the CRUD paradigm to resources in an API.&lt;/p&gt;

&lt;p&gt;On top of this, both solutions provide a DSL: the &lt;a href="https://puppet.com/docs/puppet/7.6/puppet_language.html" rel="noopener noreferrer"&gt;Puppet DSL&lt;/a&gt; for Puppet and the &lt;a href="https://www.terraform.io/docs/language/syntax/configuration.html" rel="noopener noreferrer"&gt;HashiCorp Configuration Language (HCL)&lt;/a&gt; for Terraform. They are both declarative languages,  allowing you to organize your code for higher re-usability and maintainability.&lt;/p&gt;

&lt;h1&gt;
  
  
  It's all about abstraction and convergence
&lt;/h1&gt;

&lt;p&gt;As in Puppet, HCL is only a wrapper around the low-level resources, the only objects that actually impact your infrastructure.&lt;/p&gt;

&lt;p&gt;These resources ensure the convergence of your infrastructure. All other objects only allow you to organize your code in multiple abstraction layers: &lt;a href="https://puppet.com/docs/puppet/7.6/lang_classes.html" rel="noopener noreferrer"&gt;classes&lt;/a&gt; and &lt;a href="https://puppet.com/docs/puppet/7.6/lang_defined_types.html" rel="noopener noreferrer"&gt;defined types&lt;/a&gt; in Puppet; &lt;a href="https://www.terraform.io/docs/language/modules/syntax.html" rel="noopener noreferrer"&gt;modules&lt;/a&gt; in Terraform.&lt;/p&gt;

&lt;p&gt;In Puppet, you could do something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="k"&gt;node&lt;/span&gt; &lt;span class="s1"&gt;'mynode'&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'foo'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="py"&gt;ensure&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;present&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, if you want code re-usability and ease maintenance, you should add abstraction layers in between the node and the &lt;code&gt;user&lt;/code&gt; resource.&lt;/p&gt;

&lt;p&gt;Since 2012 and &lt;a href="http://craigdunn.org/2012/05/239/" rel="noopener noreferrer"&gt;Craig Dunn's famous blog post about Designing Puppet&lt;/a&gt;, the most described (and probably used) pattern in Puppet code is to use public modules from the Puppet forge, then add 2 levels of abstraction on top of it with &lt;a href="https://puppet.com/docs/pe/2019.8/osp/the_roles_and_profiles_method.html" rel="noopener noreferrer"&gt;Roles and Profiles&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In Terraform, you could also do very simple things without any level of abstraction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_ami"&lt;/span&gt; &lt;span class="s2"&gt;"ubuntu"&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="nx"&gt;filter&lt;/span&gt; &lt;span class="p"&gt;{&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;"ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"&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="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;"099720109477"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# Canonical&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_instance"&lt;/span&gt; &lt;span class="s2"&gt;"web"&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="nx"&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;ubuntu&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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;"t3.micro"&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&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;"HelloWorld"&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;p&gt;However, if you want to maximize re-usability, you probably want to add some abstraction layers, similar to the Roles and Profiles in Puppet. Terraform allows you to group your resources into &lt;em&gt;modules&lt;/em&gt; that can than be instantiated multiple times in your code.&lt;/p&gt;

&lt;p&gt;The Roles and Profiles pattern in Puppet usually implements the following layers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;node → role → profiles → component module → resources&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In Terraform, we do not manage &lt;em&gt;nodes&lt;/em&gt;, so the abstraction stack starts with the &lt;em&gt;workspace&lt;/em&gt; instead:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;workspace → root module → composition module → resource module → resource&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Analogies
&lt;/h1&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%2F0kkplwxlayjo8hpf9f0e.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%2F0kkplwxlayjo8hpf9f0e.png" alt="Puppet vs Terraform abstractions"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Puppet Types vs Terraform Resources
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Terraform&lt;/em&gt; resources are like &lt;em&gt;Puppet&lt;/em&gt; Types: they are the only low-level objects that really do something in your API (or on your Puppet node).&lt;/p&gt;

&lt;h3&gt;
  
  
  Puppet nodes vs Terraform workspaces
&lt;/h3&gt;

&lt;p&gt;With &lt;em&gt;Puppet&lt;/em&gt;, the entry point of your code is the &lt;code&gt;node&lt;/code&gt; object which automatically includes the &lt;code&gt;Main&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Terraform&lt;/em&gt; implicitly creates a &lt;code&gt;default&lt;/code&gt; workspace for your stack, which instantiates the &lt;em&gt;root module&lt;/em&gt; where you can declare your infrastructure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Puppet Roles vs Terraform root modules
&lt;/h3&gt;

&lt;p&gt;The Roles and Profiles design pattern in &lt;em&gt;Puppet&lt;/em&gt; suggests to assign one and only one Role class to your node.&lt;/p&gt;

&lt;p&gt;In &lt;em&gt;Terraform&lt;/em&gt;, you don't really have the choice because you automatically have one and only one root module per workspace.&lt;/p&gt;

&lt;h3&gt;
  
  
  Puppet Profiles vs Terraform composition modules
&lt;/h3&gt;

&lt;p&gt;In &lt;em&gt;Puppet&lt;/em&gt;, you would code your business logic into Profile classes so that you can reuse it, probably with different parameters, in your various Roles.&lt;/p&gt;

&lt;p&gt;In &lt;em&gt;Terraform&lt;/em&gt;, you could code your business logic into &lt;strong&gt;composition modules&lt;/strong&gt; that you will instantiate in your root module.&lt;/p&gt;

&lt;h3&gt;
  
  
  Puppet Forge vs Terraform registry
&lt;/h3&gt;

&lt;p&gt;Just like &lt;em&gt;Puppet&lt;/em&gt; has its &lt;a href="https://forge.puppet.com" rel="noopener noreferrer"&gt;Forge&lt;/a&gt;, Terraform provides a &lt;a href="https://registry.terraform.io" rel="noopener noreferrer"&gt;registry&lt;/a&gt; for anyone to share their resource modules, which represent the first level of abstraction.&lt;/p&gt;

&lt;h1&gt;
  
  
  Examples
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Puppet
&lt;/h2&gt;

&lt;p&gt;Let's provision a node running a Docker engine and a Traefik reverse proxy. The role will include both classes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code Entry Point
&lt;/h3&gt;

&lt;p&gt;The code entry point in Puppet is the &lt;code&gt;manifests/site.pp&lt;/code&gt; file. There are multiple ways to achieve node classification in Puppet, one of which is to store the node's role in its certificate. It will then be exposed as a &lt;a href="https://puppet.com/docs/puppet/7.6/lang_facts_and_builtin_vars.html" rel="noopener noreferrer"&gt;trusted fact&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="c"&gt;# manifests/site.pp
&lt;/span&gt;&lt;span class="nv"&gt;$role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$trusted&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'extensions'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'pp_role'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;node&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;include&lt;/span&gt; &lt;span class="s2"&gt;"roles::&lt;/span&gt;&lt;span class="nv"&gt;${role}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Role class
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="c"&gt;# modules/roles/manifests/docker_traefik_dev.pp
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="s1"&gt;'roles::docker_traefik_dev'&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;include&lt;/span&gt; &lt;span class="nc"&gt;profiles::docker&lt;/span&gt;
  &lt;span class="k"&gt;include&lt;/span&gt; &lt;span class="nc"&gt;profiles::traefik&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Profile classes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight puppet"&gt;&lt;code&gt;&lt;span class="c"&gt;# modules/profiles/manifests/docker.pp
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="s1"&gt;'profiles::docker'&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'docker'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'docker-compose'&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="c"&gt;# modules/profiles/manifests/traefik.pp
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="s1"&gt;'profiles::traefik'&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="nv"&gt;$enable_docker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&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;class&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'traefik'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="py"&gt;enable_docker&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$enable_docker&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Component modules
&lt;/h3&gt;

&lt;p&gt;Here, we are using 3 component modules that could be published on the Puppet Forge:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;docker&lt;/li&gt;
&lt;li&gt;docker-compose&lt;/li&gt;
&lt;li&gt;traefik&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;In this Terraform example, we create an AWS VPC and an EKS cluster.&lt;/p&gt;

&lt;h3&gt;
  
  
  Root module
&lt;/h3&gt;

&lt;p&gt;The Terraform root module is the entry point of your code, which declares your stack. Here, it only includes a composition module.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# main.tf&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"cluster"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"registry.example.com/example-corp/k8s-cluster/aws"&lt;/span&gt;

  &lt;span class="nx"&gt;vpc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&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;"my-vpc"&lt;/span&gt;
    &lt;span class="nx"&gt;cidr&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.0.0/16"&lt;/span&gt;
    &lt;span class="nx"&gt;azs&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"eu-west-1a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"eu-west-1b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"eu-west-1c"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;private_subnets&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"10.0.1.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"10.0.2.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"10.0.3.0/24"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;public_subnets&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"10.0.101.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"10.0.102.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"10.0.103.0/24"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;eks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cluster_name&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-cluster"&lt;/span&gt;
    &lt;span class="nx"&gt;cluster_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1.18"&lt;/span&gt;
    &lt;span class="nx"&gt;worker_groups&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="nx"&gt;instance_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"m5a.large"&lt;/span&gt;
        &lt;span class="nx"&gt;asg_max_size&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Composition module
&lt;/h3&gt;

&lt;p&gt;The composition module is a reusable piece of code that contains your business logic. Here, it creates a VPC and deploys an EKS cluster on every instantiation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# variables.tf&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"vpc"&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;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;name&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;cidr&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;azs&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;private_subnets&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;public_subnets&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&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="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"eks"&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;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;cluster_name&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;cluster_version&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;worker_groups&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;any&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="c1"&gt;# main.tf`&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"vpc"&lt;/span&gt; &lt;span class="p"&gt;{&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;"terraform-aws-modules/vpc/aws"&lt;/span&gt;

  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
  &lt;span class="nx"&gt;cidr&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr&lt;/span&gt;

  &lt;span class="nx"&gt;azs&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;azs&lt;/span&gt;
  &lt;span class="nx"&gt;private_subnets&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_subnets&lt;/span&gt;
  &lt;span class="nx"&gt;public_subnets&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_subnets&lt;/span&gt;

  &lt;span class="nx"&gt;enable_nat_gateway&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;single_nat_gateway&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;one_nat_gateway_per_az&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;private_subnet_tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"kubernetes.io/role/internal-elb"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;public_subnet_tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"kubernetes.io/role/elb"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_eks_cluster"&lt;/span&gt; &lt;span class="s2"&gt;"cluster"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_eks_cluster_auth"&lt;/span&gt; &lt;span class="s2"&gt;"cluster"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"kubernetes"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;host&lt;/span&gt;                   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_eks_cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;
  &lt;span class="nx"&gt;cluster_ca_certificate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;base64decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_eks_cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;certificate_authority&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;token&lt;/span&gt;                  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_eks_cluster_auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;
  &lt;span class="nx"&gt;load_config_file&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;version&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 1.9"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"cluster"&lt;/span&gt; &lt;span class="p"&gt;{&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;"terraform-aws-modules/eks/aws"&lt;/span&gt;
  &lt;span class="nx"&gt;cluster_name&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_name&lt;/span&gt;
  &lt;span class="nx"&gt;cluster_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_version&lt;/span&gt;
  &lt;span class="nx"&gt;subnets&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_subnets&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc_id&lt;/span&gt;
  &lt;span class="nx"&gt;worker_groups&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;worker_groups&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You probably want maximum testing of this composition module, such as linting, syntax validation, integration tests…&lt;/p&gt;

&lt;h3&gt;
  
  
  Resource Module
&lt;/h3&gt;

&lt;p&gt;Here, we are using two public modules available on the Terraform registry (but of course you could load any module from any supported source):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/modules/terraform-aws-modules/vpc/aws/latest" rel="noopener noreferrer"&gt;terraform-aws-modules/vpc/aws&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/modules/terraform-aws-modules/eks/aws/latest" rel="noopener noreferrer"&gt;terraform-aws-modules/eks/aws&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Rules
&lt;/h1&gt;

&lt;p&gt;Common rules for the Puppet's &lt;em&gt;Roles and Profiles&lt;/em&gt; design pattern are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;node&lt;/strong&gt; includes &lt;strong&gt;one role&lt;/strong&gt;, and one only,&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;role&lt;/strong&gt; includes &lt;strong&gt;one or more profiles&lt;/strong&gt; to define the type of server,&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;profile&lt;/strong&gt; includes and manages &lt;strong&gt;component modules&lt;/strong&gt; to define a logical technical stack,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Component modules&lt;/strong&gt; manage low-level resources,&lt;/li&gt;
&lt;li&gt;Modules should only be responsible for managing aspects of the component they are written for.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In Terraform, we could also do some analogies regarding this rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;workspace&lt;/strong&gt; includes &lt;strong&gt;one root module&lt;/strong&gt;, and one only. Here you don't really have the choice because Terraform imposes this to you.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;root module&lt;/strong&gt; includes &lt;strong&gt;one or more composition modules&lt;/strong&gt; to define the type of infrastructure,&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;composition module&lt;/strong&gt; includes and manages &lt;strong&gt;resource modules&lt;/strong&gt; to define a logical technical stack,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resource modules&lt;/strong&gt; manage low-level resources,&lt;/li&gt;
&lt;li&gt;Modules should only be responsible for managing aspects of the component they are written for.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Even though Puppet and Terraform have different purposes, they share similarities and can benefit from the same architecture best practices.&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>puppet</category>
      <category>architecture</category>
      <category>devops</category>
    </item>
    <item>
      <title>Easily deploy an SKS cluster on Exoscale with Terraform</title>
      <dc:creator>Mickaël Canévet</dc:creator>
      <pubDate>Thu, 25 Feb 2021 09:26:01 +0000</pubDate>
      <link>https://forem.com/camptocamp-ops/easily-deploy-an-sks-cluster-on-exoscale-with-terraform-3c3o</link>
      <guid>https://forem.com/camptocamp-ops/easily-deploy-an-sks-cluster-on-exoscale-with-terraform-3c3o</guid>
      <description>&lt;p&gt;Following the &lt;a href="https://www.exoscale.com/syslog/introducing-scalable-kubernetes-service/"&gt;recent announcement of Exoscale's managed Kubernetes service&lt;/a&gt;, we gave it a test run to deploy our standard stack of tools. As usual, we wanted to do it "as Code", so we chose Terraform for the task.&lt;/p&gt;

&lt;p&gt;Since the release of Exoscale's Terraform provider v0.22.0, it is now possible to create SKS clusters as code.&lt;/p&gt;

&lt;p&gt;To deploy a cluster you'll need to create all these resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An &lt;code&gt;exoscale_sks_cluster&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;One or more &lt;code&gt;exoscale_sks_nodepools&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;exoscale_affinity&lt;/code&gt; per node pool to ensure that all nodes in a pools are in the same anti-affinity group in case of outage in an hypervisor,&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;exoscale_security_group&lt;/code&gt; for your node pools,&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;exoscale_security_group_rule&lt;/code&gt; to allow Calico traffic behind your nodes,&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;exoscale_security_group_rule&lt;/code&gt; to allow &lt;code&gt;nodePorts&lt;/code&gt; access from everywhere,&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;exoscale_security_group_rule&lt;/code&gt; to allow access to logs and exec.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To ease the deployment of all these resources, we decided to write a &lt;a href="https://registry.terraform.io/providers/exoscale/exoscale/latest"&gt;Terraform module&lt;/a&gt; that we published &lt;a href="https://registry.terraform.io/modules/camptocamp/sks/exoscale/latest"&gt;on the Terraform registry&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In order to use it, simply copy this HCL code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"sks"&lt;/span&gt; &lt;span class="p"&gt;{&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;"camptocamp/sks/exoscale"&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;"0.3.1"&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;"test"&lt;/span&gt;
  &lt;span class="nx"&gt;zone&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"de-fra-1"&lt;/span&gt;

  &lt;span class="nx"&gt;nodepools&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"router"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&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;"medium"&lt;/span&gt;
      &lt;span class="nx"&gt;size&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="err"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"compute"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&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;"small"&lt;/span&gt;
      &lt;span class="nx"&gt;size&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="err"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"kubeconfig"&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="nx"&gt;module&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sks&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kubeconfig&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;p&gt;Export your API keys:&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;$ &lt;/span&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;EXOSCALE_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;...
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;EXOSCALE_API_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run:&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;$ &lt;/span&gt;terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will deploy an SKS cluster with 2 nodepools (one that we'll dedicate for our Ingress Controller and one to host our applications), one anti-affinity group per nodepool and a security group with proper rules so that everything runs properly (you'll still have to open access to http and https ports if needed).&lt;/p&gt;

&lt;p&gt;You can retrieve the kubeconfig for the kube-admin user using this command:&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;$ &lt;/span&gt;terraform output &lt;span class="nt"&gt;-json&lt;/span&gt; kubeconfig | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ~/.kube/config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;small&gt;NOTE: make sure to not overwrite a previous cluster configuration or prefer working with environment variables with &lt;code&gt;KUBECONFIG=~/path/to/sks-config&lt;/code&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;You should then be able to connect to the cluster:&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;$ &lt;/span&gt;kubectl get pods &lt;span class="nt"&gt;--all-namespaces&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And voilà.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>devops</category>
      <category>terraform</category>
      <category>showdev</category>
    </item>
    <item>
      <title>4 ways to inject secrets into an application</title>
      <dc:creator>Mickaël Canévet</dc:creator>
      <pubDate>Mon, 24 Aug 2020 14:27:40 +0000</pubDate>
      <link>https://forem.com/camptocamp-ops/4-ways-to-inject-secrets-into-an-application-cem</link>
      <guid>https://forem.com/camptocamp-ops/4-ways-to-inject-secrets-into-an-application-cem</guid>
      <description>&lt;p&gt;Most applications require secrets, for example to connect to a database, communicate with another application using tokens or certificates, define an admin password…&lt;/p&gt;

&lt;p&gt;Dealing with this is often a headache. Even when you have a proper secret management tool, it's sometimes a nightmare to inject the secrets into the application where it needs to be used.&lt;/p&gt;

&lt;h1&gt;
  
  
  The 4 ways
&lt;/h1&gt;

&lt;h2&gt;
  
  
  First way: build time
&lt;/h2&gt;

&lt;p&gt;This is probably the worst way to do it.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build a WAR file, or a Docker image or any artifact with a configuration file that contains the secret in plain text.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your deployment tool does not need permissions to decrypt the secrets.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your whole artifact becomes a secret;&lt;/li&gt;
&lt;li&gt;You have to rebuild and redeploy when you want to renew a secret.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Second way: deploy time
&lt;/h2&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Give a Continuous Deployment pipeline access to a secrets store an let it inject the secrets in the application's configuration files;&lt;/li&gt;
&lt;li&gt;Give a Continuous Deployment tool, like ArgoCD, access to a secrets store an let it inject the secrets in the application's configuration files.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You don't have to rebuild when you want to renew a secret.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your deployment tool needs permissions to retrieve and decrypt the secrets;&lt;/li&gt;
&lt;li&gt;You have to redeploy when you want to renew a secret.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Third way: start time
&lt;/h2&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use Kubernetes &lt;a href="https://github.com/kubernetes-sigs/secrets-store-csi-driver"&gt;Secret Store CSI drivers&lt;/a&gt; to inject secrets as volumes or secrets;&lt;/li&gt;
&lt;li&gt;Use HashiCorp &lt;a href="https://www.vaultproject.io/"&gt;Vault&lt;/a&gt; as a secret store and &lt;a href="https://www.vaultproject.io/docs/platform/k8s/injector"&gt;Vault Agent Injector&lt;/a&gt; as an &lt;em&gt;init&lt;/em&gt; container to create configuration files before the application starts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your deployment tool does not need permissions to decrypt the secrets;&lt;/li&gt;
&lt;li&gt;You don't have to redeploy when you want to renew a secret.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need to restart your application when you want to renew a secret.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Forth way: run time
&lt;/h2&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS instance profiles;&lt;/li&gt;
&lt;li&gt;HashiCorp Vault as secret store and Vault Agent Injector in &lt;em&gt;sidecar&lt;/em&gt; mode.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can renew your secret without even having to restart your application, allowing a dynamic secrets mechanism.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You probably need to adapt your application (or not if you already use an SDK that supports it).&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Do you know any more ways to inject secrets in your applications? Let me know in the comments!&lt;/p&gt;

</description>
      <category>devops</category>
      <category>kubernetes</category>
      <category>security</category>
      <category>secrets</category>
    </item>
    <item>
      <title>Use Kustomize to post-render Helm charts in ArgoCD</title>
      <dc:creator>Mickaël Canévet</dc:creator>
      <pubDate>Tue, 18 Aug 2020 13:50:18 +0000</pubDate>
      <link>https://forem.com/camptocamp-ops/use-kustomize-to-post-render-helm-charts-in-argocd-2ml6</link>
      <guid>https://forem.com/camptocamp-ops/use-kustomize-to-post-render-helm-charts-in-argocd-2ml6</guid>
      <description>&lt;p&gt;In an ideal world you wouldn't have to perform multiple steps for the rendering, but unfortunately we don't live in an ideal world...&lt;/p&gt;

&lt;h1&gt;
  
  
  Kustomize
&lt;/h1&gt;

&lt;p&gt;Nowadays, most applications that are meant to be deployed in Kubernetes provide a Helm chart to ease deployment. Unfortunately, sometimes the Helm chart is not flexible enough to do what you want to do, so you have to fork and contribute and hope that your contribution is quickly merged upstream so that you don't have to maintain your fork.&lt;/p&gt;

&lt;p&gt;Instead of pointing to your fork, you could use &lt;a href="https://kustomize.io/"&gt;Kustomize&lt;/a&gt; to apply some post-rendering to your templatized Helm release. This is possible natively since Helm 3.1 using the &lt;a href="https://helm.sh/docs/topics/advanced/"&gt;&lt;code&gt;--post-process&lt;/code&gt; flag&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Integration in ArgoCD
&lt;/h1&gt;

&lt;p&gt;At Camptocamp, we use &lt;a href="https://argoproj.github.io/argo-cd/"&gt;ArgoCD&lt;/a&gt; to manage the deployment of our objects into Kubernetes. Let's see how we can use Kustomize to do post-rendering of Helm charts in ArgoCD:&lt;/p&gt;

&lt;p&gt;At first, declare a new config management plugin into your &lt;code&gt;argocd-cm&lt;/code&gt; configMap (the way to do it depends on the way you deployed ArgoCD):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ConfigMap&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;argocd-cm&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;argocd&lt;/span&gt;
&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;configManagementPlugins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;- name: kustomized-helm&lt;/span&gt;
      &lt;span class="s"&gt;init:&lt;/span&gt;
        &lt;span class="s"&gt;command: ["/bin/sh", "-c"]&lt;/span&gt;
        &lt;span class="s"&gt;args: ["helm dependency build || true"]&lt;/span&gt;
      &lt;span class="s"&gt;generate:&lt;/span&gt;
        &lt;span class="s"&gt;command: ["/bin/sh", "-c"]&lt;/span&gt;
        &lt;span class="s"&gt;args: ["helm template . --name-template $ARGOCD_APP_NAME --namespace $ARGOCD_APP_NAMESPACE --include-crds &amp;gt; all.yaml &amp;amp;&amp;amp; kustomize build"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then add a &lt;code&gt;kustomization.yaml&lt;/code&gt; file next to your application's &lt;code&gt;Chart.yaml&lt;/code&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="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kustomize.config.k8s.io/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Kustomization&lt;/span&gt;

&lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;all.yaml&lt;/span&gt;

&lt;span class="na"&gt;patchesJson6902&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps&lt;/span&gt;
      &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
      &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapplication&lt;/span&gt;
    &lt;span class="na"&gt;patch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|-&lt;/span&gt;
      &lt;span class="s"&gt;- op: remove&lt;/span&gt;
        &lt;span class="s"&gt;path: /spec/template/spec/securityContext&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now configure your &lt;code&gt;Applications&lt;/code&gt; object to use this plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;argoproj.io/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Application&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapplication&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;argocd&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myproject&lt;/span&gt;
  &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapplication&lt;/span&gt;
    &lt;span class="na"&gt;repoURL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.spec.source.repoURL&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
    &lt;span class="na"&gt;targetRevision&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.spec.source.targetRevision&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
    &lt;span class="na"&gt;plugin&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kustomized-helm&lt;/span&gt;
  &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myproject&lt;/span&gt;
    &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.spec.destination.server&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And... voilà!&lt;/p&gt;

&lt;h1&gt;
  
  
  Integration in App of Apps
&lt;/h1&gt;

&lt;p&gt;One thing that I often do is to use &lt;code&gt;spec.source.helm&lt;/code&gt; in my &lt;code&gt;Application&lt;/code&gt; object to pass some values that comes from my &lt;a href="https://argoproj.github.io/argo-cd/operator-manual/cluster-bootstrapping/"&gt;app of apps&lt;/a&gt;. This is not possible using a configuration plugin as the keys &lt;code&gt;helm&lt;/code&gt; and &lt;code&gt;plugin&lt;/code&gt; are mutually exclusive.&lt;/p&gt;

&lt;p&gt;The workaround I found is to use plugin's envs. You have to change your config management plugin configuration to (note the &lt;code&gt;$HELM_ARGS&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="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ConfigMap&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;argocd-cm&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;argocd&lt;/span&gt;
&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;configManagementPlugins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;- name: kustomized-helm&lt;/span&gt;
      &lt;span class="s"&gt;init:&lt;/span&gt;
        &lt;span class="s"&gt;command: ["/bin/sh", "-c"]&lt;/span&gt;
        &lt;span class="s"&gt;args: ["helm dependency build || true"]&lt;/span&gt;
      &lt;span class="s"&gt;generate:&lt;/span&gt;
        &lt;span class="s"&gt;command: ["/bin/sh", "-c"]&lt;/span&gt;
        &lt;span class="s"&gt;args: ["echo \"$HELM_VALUES\" | helm template . --name-template $ARGOCD_APP_NAME --namespace $ARGOCD_APP_NAMESPACE $HELM_ARGS -f - --include-crds &amp;gt; all.yaml &amp;amp;&amp;amp; kustomize build"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll then be able to use this in your &lt;code&gt;Application&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="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;argoproj.io/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Application&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapplication&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;argocd&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myproject&lt;/span&gt;
  &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapplication&lt;/span&gt;
    &lt;span class="na"&gt;repoURL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.spec.source.repoURL&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
    &lt;span class="na"&gt;targetRevision&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.spec.source.targetRevision&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
    &lt;span class="na"&gt;plugin&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kustomized-helm&lt;/span&gt;
      &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HELM_ARGS&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&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;targetRevision={{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;.Values.spec.source.targetRevision&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;

  &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myproject&lt;/span&gt;
    &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.spec.destination.server&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;argoproj.io/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Application&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapplication&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;argocd&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myproject&lt;/span&gt;
  &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapplication&lt;/span&gt;
    &lt;span class="na"&gt;repoURL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.spec.source.repoURL&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
    &lt;span class="na"&gt;targetRevision&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.spec.source.targetRevision&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
    &lt;span class="na"&gt;plugin&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kustomized-helm&lt;/span&gt;
      &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HELM_VALUES&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;targetRevision: {{ .Values.spec.source.targetRevision }}&lt;/span&gt;

  &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myproject&lt;/span&gt;
    &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.spec.destination.server&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>kubernetes</category>
      <category>devops</category>
      <category>tutorial</category>
      <category>argocd</category>
    </item>
  </channel>
</rss>
