<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title>RDerik</title><subtitle>Simplify</subtitle><id>https://rderik.com/blog</id><link href="https://rderik.com/blog"/><link href="https://rderik.com/feed.xml" rel="self"/><link href="https://rderik.com/images/RD.svg" rel="icon" sizes="any" type="image/svg+xml"/><updated>2023-10-09T17:16:00+01:00</updated><author><name>Derik Ramirez</name></author><entry><title>Set up AWS Cognito with Terraform and Go</title><link rel="alternate" href="https://rderik.com/blog/set-up-aws-cognito-with-terraform-and-go/"/><id>https://rderik.com/blog/set-up-aws-cognito-with-terraform-and-go/</id><published>2023-10-09T17:16:00+01:00</published><updated>2023-10-09T17:16:00+01:00</updated><author><name>Derik Ramirez</name></author><content type="html"><![CDATA[<p>Choosing AWS Cognito for your user authentication and authorization needs is an excellent option. Cognito provides a lot of capabilities, and with all the flexibility comes some complexity. It is hard to wrap your head around how to set it up, you probably have questions like:</p>
<ul>
<li>should I use a User Pool or an Identity Pool?</li>
<li>If I create a User Pool, do I need to use a federated Identity Provider?</li>
<li>When the documentation says that Cognito can be used as an OIDC what does it mean?</li>
</ul>
<p>The goal of this article is to shed some light on this topics and help you set Cognito for your project. We&rsquo;ll use go for the examples, but should be able to understand the ideas behind the code an dapply them to a project using other languages.</p>
<p>Let&rsquo;s start by discussing some basic topics, before we start creating terraform templates and writing go code.</p>
<h2 id="aws-cognito-basic-concepts">AWS Cognito basic concepts</h2>
<p>AWS Cognito provides a complete solution for authentication and authorization. Cognito can be used in different scenarios, for example:</p>
<ul>
<li>You need a complete solution to mange, sign-up, sign-in, etcetera, in your app</li>
<li>Cognito can also serve as an identity provider that can return Oauth 2.0 acces tokens, this tokens contain metadata of the authenticated user so you can add authorization logic to your code based on this information</li>
<li>You need access to AWS resources. With AWS Cognito you can return AWS credentials (Specified using IAM) to access those resources</li>
<li>Cognito can also serve as an intermediate Service Provider to other Identity providers. For example, Facebook, Google, etc.</li>
</ul>
<h3 id="user-pools">User Pools</h3>
<p>User pools are, as the name suggests, a repository of users you want to keep track off. The user pool can be an independent directory, that means that all the users live in the User Pool. The user pool can also source the users from third-party providers. That means that the User user pool will be an intermediate service provider. Cognito user pools can be used as an OIDC (OpenID Connect) Identity Provider.</p>
<p>The simplest form is to use the user pool as an independent directory. Everything is handled inside AWS Cognito, which makes things simpler. When using the user pools as an intermidate SP to third parties, help translating all the external tokens returned by the third-party IdPs to Cognito Tokens, so everything is standardised into one format and this could simplify your code.</p>
<h4 id="app-client">App Client</h4>
<p>In Cognito creating the User Pool is only half the batlte, you still need to interact with it. While you can in certain cases just interact with the User Pool programatically using the AWS SDK, it is far easier to interact with the User Pool via an app client. The app client provides features like the following:</p>
<ul>
<li>Authentication Flows</li>
<li>Token management. For example, token expiration.</li>
<li>OAuth scope management</li>
<li>Callback and logout URLs for the authentication workflow</li>
<li>CORS configuration</li>
<li>Security features, like Secrets.</li>
<li>Hosted UI that you can customize</li>
<li>Manage identity providers for the app client</li>
</ul>
<p>In general you&rsquo;ll need to set up an app client for your application to communicate with Cognito for user authentication, token retrieval and expirtation, etcetera.</p>
<h3 id="identity-pools">Identity Pools</h3>
<p>The identity pools are used in combination with authenticated users from User Pools, or it can use federated identities from external IdPs. When an entity has been authenticated, then we can provide AWS Credentials to access AWS resources. For example, we have an app that shows the content of a file in private S3 bucket. We can create an IAM role that grants access to the S3 bucket content. Instead of adding someone to your AWS organisation, you could use an Identity Pool to get temporary AWS credentials to assume that IAM role and be able to access the file form the private S3 bucket.</p>
<h3 id="identity-providers-idps">Identity Providers (IdPs)</h3>
<p>A user pool can serve as an Identity Provider, meaning that it handles the verification of your users and can provide your app with information about them. When you use an external IdP, you can autenticate and get information about your users from a service that you don&rsquo;t manage, it is also know as federated identities. You have probably seen sign in with Google, Facebook, Amazon, etcetera, they serve as Identity Providers. Their users are already registered in their service and their identity is being federated from them. You can also use an Active Directory as your identity provider, or any other service that can communicate using SAML, OAuth2.0 or OpenID Connect(OIDC).</p>
<h2 id="creating-the-user-pool-with-terraform">Creating the user pool with terraform</h2>
<p>First, we need to create our user pool. That is the easy part, the only required field is the name of the user pool:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-terraform" data-lang="terraform"><span class="line"><span class="cl"><span class="kr">resource</span> <span class="s2">&#34;aws_cognito_user_pool&#34;</span> <span class="s2">&#34;user_pool&#34;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">name</span> <span class="o">=</span> <span class="nb">var</span><span class="p">.</span><span class="nx">user_pool_name</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="nx">alias_attributes</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;email&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Adding the <code>alias_attributes</code> to include the email, means that the user would be able to login using its username or the email.</p>
<p>Now we need to set up our App client:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-terraform" data-lang="terraform"><span class="line"><span class="cl"><span class="kr">resource</span> <span class="s2">&#34;aws_cognito_user_pool_client&#34;</span> <span class="s2">&#34;user_pool_client&#34;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">name</span>            <span class="o">=</span> <span class="nb">var</span><span class="p">.</span><span class="nx">user_pool_client_name</span>
</span></span><span class="line"><span class="cl">  <span class="nx">user_pool_id</span>    <span class="o">=</span> <span class="nx">aws_cognito_user_pool</span><span class="p">.</span><span class="nx">user_pool</span><span class="p">.</span><span class="nx">id</span>
</span></span><span class="line"><span class="cl">  <span class="nx">generate_secret</span> <span class="o">=</span> <span class="nb">var</span><span class="p">.</span><span class="nx">generate_client_secret</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>We are not going to be using custom domains, so we just need a prefix for our domain:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-terraform" data-lang="terraform"><span class="line"><span class="cl"><span class="kr">resource</span> <span class="s2">&#34;aws_cognito_user_pool_domain&#34;</span> <span class="s2">&#34;custom_domain&#34;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">domain</span>       <span class="o">=</span> <span class="nb">var</span><span class="p">.</span><span class="nx">custom_domain</span>
</span></span><span class="line"><span class="cl">  <span class="nx">user_pool_id</span> <span class="o">=</span> <span class="nx">aws_cognito_user_pool</span><span class="p">.</span><span class="nx">user_pool</span><span class="p">.</span><span class="nx">id</span><span class="c1">
</span></span></span><span class="line"><span class="cl"><span class="c1">  # If we were to use a custom domain we would need the certificate to be managed
</span></span></span><span class="line"><span class="cl"><span class="c1">  # by AWS Certificate Manager, in us-east-1
</span></span></span><span class="line"><span class="cl"><span class="c1">  # certificate_arn = aws_acm_certificate.cert.arn
</span></span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div>]]></content></entry><entry><title>Directory Structure for Terraform Projects</title><link rel="alternate" href="https://rderik.com/blog/directory-structure-for-terraform-projects/"/><id>https://rderik.com/blog/directory-structure-for-terraform-projects/</id><published>2023-10-01T11:37:00+01:00</published><updated>2023-10-01T11:37:00+01:00</updated><author><name>Derik Ramirez</name></author><content type="html"><![CDATA[<p>Terraform doesn&rsquo;t concern itself with the directory structure of our project. It cares about state. We, as the users of the project, are the ones who benefit from a clean and easy-to-understand directory structure.</p>
<p>In this post, we&rsquo;ll explore basic directory structures used for Terraform projects.</p>
<p><strong>Note:</strong> If you want a more in-depth discussion about the state and directory structure relationship, you might like my guide <a href="/guides/meditations_directory_structure_tf_projects/">Meditations on Directory Structure for Terraform Projects</a>.</p>
<div class="rd-toc">
  <nav id="TableOfContents">
  <ul>
    <li><a href="#common-ways-to-structure-your-directories-and-files-for-terraform-projects">Common ways to structure your directories and files for Terraform projects</a>
      <ul>
        <li><a href="#per-project-boundary">Per-project boundary</a></li>
        <li><a href="#per-service-boundary">Per-service boundary</a></li>
        <li><a href="#using-workspaces">Using <code>workspaces</code></a></li>
      </ul>
    </li>
    <li><a href="#final-thoughts">Final Thoughts</a></li>
  </ul>
</nav>
</div>

<!-- vim-markdown-toc Redcarpet -->
<!-- vim-markdown-toc -->
<h2 id="common-ways-to-structure-your-directories-and-files-for-terraform-projects">Common ways to structure your directories and files for Terraform projects</h2>
<p>There are multiple ways you can structure your code, Terraform is very flexible about it, so there is no single answer to that question. But let&rsquo;s look at a few commong patterns.</p>
<!-- vim-markdown-toc Redcarpet -->
<!-- vim-markdown-toc -->
<h3 id="per-project-boundary">Per-project boundary</h3>
<p>A common pattern is structuring your code using the environment as the boundary for separating resources. The directory structure looks like the following:</p>
<pre tabindex="0"><code>── basic_project
    ├── environments
    │   ├── dev
    │   │   ├── main.tf
    │   │   ├── outputs.tf
    │   │   └── variables.tf
    │   ├── prod
    │   │   ├── main.tf
    │   │   ├── outputs.tf
    │   │   └── variables.tf
    │   └── staging
    │       ├── main.tf
    │       ├── outputs.tf
    │       └── variables.tf
    ├── modules
    └── shared
</code></pre><p>This structure is clean and easy to understand at a glance. If you work in a specific environment, any change you make won&rsquo;t affect other environments. It has a couple of drawbacks. For example, we could easily add different resources to each environment, creating drift between the environments. It is good practice to keep your environments as similar as possible, so running tests in a lower environment will reflect the effects in the same way as running them in a production environment.</p>
<p>In a clean directory structure, the code inside the environment directories (i.e. <code>dev</code>, <code>staging</code> and <code>prod</code>) are primarily to reference modules. The idea is to reduce code duplication and use the modules as the source of truth of how resources should be configured and with sensible default behaviours.</p>
<!-- vim-markdown-toc Redcarpet -->
<!-- vim-markdown-toc -->
<h3 id="per-service-boundary">Per-service boundary</h3>
<p>When the number of resources is high, some teams break down the directory structure into smaller areas of interest. For example, split the directory structure by service. The file structure would look like the following:</p>
<pre tabindex="0"><code>── service_A
    └── environments
        ├── dev
        │   ├── main.tf
        │   ├── outputs.tf
        │   └── variables.tf
        ├── prod
        │   ├── main.tf
        │   ├── outputs.tf
        │   └── variables.tf
        └── staging
            ├── main.tf
            ├── outputs.tf
            └── variables.tf
</code></pre><p>If you are observant, you&rsquo;ll notice that the directory structure is basically the same as the previous one. Breaking down into smaller areas is similar to breaking it down by project. The idea stays the same: create a boundary to hold certain resources.</p>
<p>Depending on the number of resources, the per-project structure might start causing your <code>plan</code> and <code>apply</code> commands to take a lot of time. The reason is that Terraform has to query the AWS API to check the state of the resources it is tracking. So, a larger number of resources will lead to more API calls.</p>
<p>There are other reasons why we might need to segregate the state:</p>
<ul>
<li>High Number of resources, we already mentioned this</li>
<li>Using different AWS accounts per environment</li>
<li>Breaking state per region. Projects sometimes segregate their state per region to:
<ul>
<li>Reduce blast radius if a region is down</li>
<li>Deployment strategies that would prefer to only deploy to certain regions for testing, i.e. canary deployments</li>
</ul>
</li>
</ul>
<!-- vim-markdown-toc Redcarpet -->
<!-- vim-markdown-toc -->
<h3 id="using-workspaces">Using <code>workspaces</code></h3>
<p>Another common alternative for the per-project directory structure is using <code>workspaces</code>. This structure maintains the same code for all environments, reducing the possibility of differences between environments.</p>
<pre tabindex="0"><code>── single_code_multiple_var
    ├── infra
    │   ├── main.tf
    │   ├── provider.tf
    │   ├── outputs.tf
    │   └── variables.tf
    ├─── environments
    │   ├── env.dev.tfvars
    │   ├── env.staging.tfvars
    │   └── env.prod.tfvars
    └── modules
</code></pre><p>There are still cases where we would like different behaviours per environment. For example, using smaller instance types for lower-level environments and larger instance types for production. To reflect that difference, we use different values in <code>tfvars</code> files that will capture the differences. This directory structure and workflow are OK if you are OK with having the same backend for all the workspaces:</p>
<pre tabindex="0"><code>terraform {
  backend &#34;s3&#34; { &lt;--- we can&#39;t use variable interpolation, so we can&#39;t do the following
    region         = &#34;${local.env[var.enviroment]}&#34;
    bucket         = &#34;rderik-tfstate-${var.environment}&#34;
    key            = &#34;${var.environment}/tfstate/terraform.tfstate&#34;
    dynamodb_table = &#34;rderik-tfstate-${var.environment}&#34;
    encrypt        = true
  }
}
</code></pre><p>If, for some reason, we need to use a different backend per environment, then we have to solve the problem with the <code>backend</code>, which has its own set of peculiarities.</p>
<!-- vim-markdown-toc Redcarpet -->
<!-- vim-markdown-toc -->
<h2 id="final-thoughts">Final Thoughts</h2>
<p>The directory structure that we end up using for our project is highly dependent on how we want to manage the state of our project. Consider if you want to keep one state per project, service, region, etc. The key to deciding which directory structure to use in your project is for you to understand the relationship between:</p>
<ul>
<li>State</li>
<li>Directory structure</li>
<li>Backend</li>
<li>Module versions</li>
</ul>
<p>Once you clearly understand how you want to handle those aspects of your project, it&rsquo;ll be easier to choose the directory structure that will make your life easier. But don&rsquo;t worry too much about it. It is OK to start with one structure and later understand that you have outgrown that structure and migrate out of it. Believing you&rsquo;ll have all the answers from the beginning is a common mistake.</p>
<p>Software projects are iterative. You keep growing and you keep adapting that is the nature of our work.</p>
]]></content></entry><entry><title>Setting up access to a private repository in ArgoCD with SSM Parameter Store and External Secrets Operator</title><link rel="alternate" href="https://rderik.com/blog/setting-up-access-to-a-private-repository-in-argocd-with-ssm-parameter-store-and-external-secrets-operator/"/><id>https://rderik.com/blog/setting-up-access-to-a-private-repository-in-argocd-with-ssm-parameter-store-and-external-secrets-operator/</id><published>2022-11-26T11:53:00Z</published><updated>2022-11-26T11:53:00Z</updated><author><name>Derik Ramirez</name></author><content type="html"><![CDATA[<p>ArgoCD&rsquo;s documentation is quite good. I just feel there is one key question that is often left unanswered. How do I get my private SSH key into ArgoCD in a declarative way that doesn&rsquo;t require hard coding the key into a secret YAML file?</p>
<p>In this post, we are going to use the <a href="https://external-secrets.io/">External Secrets Operator (ESO)</a> to get the private SSH key from AWS SSM Parameter Store and inject it into ArgoCD using a Kubernetes Secret.</p>
<p>If you already have ArgoCD setup, skip directly to the section <a href="#setting-up-argocd-for-private-repositories">Setting up ArgoCD for private repositories</a>. If not, follow along.</p>
<p>Ok, let&rsquo;s get started.</p>
<div class="rd-toc">
  <nav id="TableOfContents">
  <ul>
    <li><a href="#installing-argocd">Installing ArgoCD</a></li>
    <li><a href="#create-the-argocd-project-and-application">Create the ArgoCD Project and application</a></li>
    <li><a href="#setting-up-the-private-repository-access">Setting up the private repository access</a></li>
    <li><a href="#setting-up-argocd-for-private-repositories">Setting up ArgoCD for private repositories</a></li>
    <li><a href="#setting-up-external-secrets-operator">Setting up External Secrets Operator</a></li>
    <li><a href="#creating-the-secret">Creating the secret</a></li>
    <li><a href="#final-thoughts">Final thoughts</a></li>
    <li><a href="#references">References</a></li>
  </ul>
</nav>
</div>

<div style="clear:both"></div>
<hr />
<article class="thumbnails">
  <div>
    <a target="_blank" href="https://rderik.gumroad.com/l/bash-beyond-basics"><img src="/images/shop/thumbnail_bash_beyond_basics_v1_0_0.jpg" width="300px"></a>
    <div class="description">
      <h1><a target="_blank" href="https://rderik.gumroad.com/l/bash-beyond-basics">Bash Beyond Basics</a>
        <span>Increase your efficiency and understanding of the shell</span></h1>
      <p>
      If you are interested in this topic you might enjoy my course Bash Byond Basics.
      This course helps you level up your bash skills. This is not a course on shell-scripting, is a course on improving your efficiency by showing you the features of bash that are seldom discussed and often ignored.
      </p>
      <p>
      Every day you spend many hours working in the shell, every little improvement in your worklflows will pay dividends many fold!
      </p>
      <a href="https://rderik.gumroad.com/l/bash-beyond-basics">Learn more</a>
    </div>
  </div>
</article>
<div style="clear:both"></div>
<hr />
<!-- vim-markdown-toc Redcarpet -->
<!-- vim-markdown-toc -->
<h2 id="installing-argocd">Installing ArgoCD</h2>
<p>First, we need to install ArgoCD in our Kubernetes cluster. We will use Helm to do this, but we will do it in a declarative way so we can use our code as the source of truth. Let&rsquo;s start by creating the directory and the required files for our Helm chart.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir argo-cd
</span></span><span class="line"><span class="cl">touch argo-cd/<span class="o">{</span>Chart,requirements<span class="o">}</span>.yaml
</span></span></code></pre></td></tr></table>
</div>
</div><p>The content for the <code>Chart.yaml</code> file is:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">argo-cd</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="m">0.1.0</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>The content for <code>requirements.yaml</code> is:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">dependencies</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">argo-cd</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="m">5.13.6</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">repository</span><span class="p">:</span><span class="w"> </span><span class="l">https://argoproj.github.io/argo-helm</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>Now we can install the chart:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> argo-cd/
</span></span><span class="line"><span class="cl">helm dependency update
</span></span><span class="line"><span class="cl">helm install argo-cd . -n argocd  --create-namespace
</span></span></code></pre></td></tr></table>
</div>
</div><p>That should get ArgoCD set up in our Kubernetes Cluster. We need a repository with some Kubernetes templates. If you don&rsquo;t have one, you can use the one I created for this post. It is a public repository. You can find it <a href="https://github.com/rderik/slartybartfast">here</a>. Fork it, and work with it. Later you can make it private or create a different private repository.</p>
<!-- vim-markdown-toc Redcarpet -->
<!-- vim-markdown-toc -->
<h2 id="create-the-argocd-project-and-application">Create the ArgoCD Project and application</h2>
<p>We will test that everything works correctly with ArgoCD using a public repository. Once ArgoCD runs correctly, we can focus on using a private repo. You can skip this section if you don&rsquo;t want to run these tests. Let&rsquo;s now create a new ArgoCD project and application.</p>
<p>The project:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">argoproj.io/v1alpha1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">AppProject</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">server-proj</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">argocd</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c"># Finaliser that ensures that the project is not deleted until any application does not reference it</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">finalizers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">resources-finalizer.argocd.argoproj.io</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c"># Project description</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">description</span><span class="p">:</span><span class="w"> </span><span class="l">Server Project</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">destinations</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c"># Update your namespace</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">my-namespace</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">server</span><span class="p">:</span><span class="w"> </span><span class="l">https://kubernetes.default.svc</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">sourceRepos</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c"># Make sure to add your repository here</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">https://github.com/rderik/slartybartfast.git</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">clusterResourceWhitelist</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">group</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;*&#39;</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>The application:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">argoproj.io/v1alpha1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Application</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">server</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">argocd</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">destination</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">my-namespace</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">server</span><span class="p">:</span><span class="w"> </span><span class="l">https://kubernetes.default.svc</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">project</span><span class="p">:</span><span class="w"> </span><span class="l">server-proj</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">source</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c"># Change the repository to your repository</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">repoURL</span><span class="p">:</span><span class="w"> </span><span class="l">https://github.com/rderik/slartybartfast.git</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c"># point the path to the path where your Kubernetes templates are</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">server</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">syncPolicy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">automated</span><span class="p">:</span><span class="w"> </span><span class="c"># automated sync by default retries failed attempts 5 times with following delays between attempts ( 5s, 10s, 20s, 40s, 80s ); retry controlled using `retry` field.</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">prune</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w"> </span><span class="c"># Specifies if resources should be pruned during auto-syncing ( false by default ).</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">selfHeal</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w"> </span><span class="c"># Specifies if partial app sync should be executed when resources are changed only in target Kubernetes cluster and no git change detected ( false by default ).</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">allowEmpty</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w"> </span><span class="c"># Allows deleting all application resources during automatic syncing ( false by default ).</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>Ok, we can create the project and the application:</p>
<pre tabindex="0"><code>kubectl apply -f project.yaml
kubectl apply -f application.yaml
</code></pre><p>Finally, we can view our application in the ArgoCD dashboard by doing a port forwarding to the argocd-server service:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl port-forward svc/argocd-server -n argocd 8080:443
</span></span></code></pre></td></tr></table>
</div>
</div><p>I run most of my tests in a VM, so when I do port forwarding, I need the port to be bound to all interfaces:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl port-forward svc/argo-cd-argocd-server -n argocd 8080:443 --address<span class="o">=</span>0.0.0.0
</span></span></code></pre></td></tr></table>
</div>
</div><p>We can now visit the ArgoCD dashboard at <a href="https://localhost:8080">https://localhost:8080</a> and log in with the default username and password. The default username is <code>admin</code>. To obtain the default password, you need to read a secret created by default during installation.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl get secret argocd-initial-admin-secret -n argocd -o <span class="nv">jsonpath</span><span class="o">=</span><span class="s2">&#34;{.data.password}&#34;</span> <span class="p">|</span> base64 -d
</span></span></code></pre></td></tr></table>
</div>
</div><p>Log in, and you should see your application being created!</p>
<p>If we do a port forward to the newly created pod, we can see the Nginx welcome page:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl port-forward <span class="k">$(</span>kubectl get pods -n my-namespace --no-headers <span class="p">|</span> awk <span class="s1">&#39;{print $1}&#39;</span><span class="k">)</span> 8081:80 -n my-namespace
</span></span></code></pre></td></tr></table>
</div>
</div><p>In another terminal:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl localhost:8081
</span></span></code></pre></td></tr></table>
</div>
</div><p>Ok, now we have a working ArgoCD installation. We can focus on setting up the private repository access.</p>
<!-- vim-markdown-toc Redcarpet -->
<!-- vim-markdown-toc -->
<h2 id="setting-up-the-private-repository-access">Setting up the private repository access</h2>
<p>We need to create an SSH key pair. We will use the key pair to access the private repository.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">$ ssh-keygen -t rsa -b <span class="m">4096</span> -C <span class="s2">&#34;key for private repository&#34;</span> -f id_rsa_private_repo
</span></span></code></pre></td></tr></table>
</div>
</div><p>Now we need to add the public key to the repository. In my case, I&rsquo;m using GitHub, so I need to add the public key to the repository. If you also use GitHub,  go to the repository Settings &gt; Deploy Keys, and add the PUBLIC key. We only need read-only, so there is no need to add write permissions.</p>
<p>Let&rsquo;s create a secure string parameter in AWS SSM parameter store that contains the PEM-encoded private key. We will use this as a secret to add the private key to ArgoCD.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">aws ssm put-parameter <span class="se">\
</span></span></span><span class="line"><span class="cl">  --name <span class="s2">&#34;/argo-cd/github-repo/ssh-key&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  --type SecureString <span class="se">\
</span></span></span><span class="line"><span class="cl">  --value <span class="s2">&#34;</span><span class="k">$(</span>cat id_rsa_private_repo<span class="k">)</span><span class="s2">&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Finally, now we can look at setting up ArgoCD for our private repository.</p>
<!-- vim-markdown-toc Redcarpet -->
<!-- vim-markdown-toc -->
<h2 id="setting-up-argocd-for-private-repositories">Setting up ArgoCD for private repositories</h2>
<p>We need to create a secret that will contain the private key. We will use the private key to access the private repository.</p>
<p><a href="https://argo-cd.readthedocs.io/en/stable/operator-manual/declarative-setup/#ssh-repositories">ArgoCD&rsquo;s documentation</a> explains that we need to create a secret with the private key so ArgoCD can access the repository. The secret looks like the following:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">apiVersion: v1
</span></span><span class="line"><span class="cl">kind: Secret
</span></span><span class="line"><span class="cl">metadata:
</span></span><span class="line"><span class="cl">  name: private-repo
</span></span><span class="line"><span class="cl">  namespace: argocd
</span></span><span class="line"><span class="cl">  labels:
</span></span><span class="line"><span class="cl">    argocd.argoproj.io/secret-type: repository
</span></span><span class="line"><span class="cl">stringData:
</span></span><span class="line"><span class="cl">  type: git
</span></span><span class="line"><span class="cl">  url: git@github.com:argoproj/my-private-repository
</span></span><span class="line"><span class="cl">  sshPrivateKey: <span class="p">|</span>
</span></span><span class="line"><span class="cl">    -----BEGIN OPENSSH PRIVATE KEY-----
</span></span><span class="line"><span class="cl">    ...
</span></span><span class="line"><span class="cl">    -----END OPENSSH PRIVATE KEY-----
</span></span></code></pre></td></tr></table>
</div>
</div><p>This approach has the issue that we would have to write our private key in plain text in the secret template. We don&rsquo;t want to do that. So we have a few options to avoid that. We can create a template and documentation that explains how to fill up this file and then manually apply it. That works, but it is a little bit cumbersome. We&rsquo;ll use <a href="https://external-secrets.io/">External Secrets Operator</a> and avoid doing that.</p>
<!-- vim-markdown-toc Redcarpet -->
<!-- vim-markdown-toc -->
<h2 id="setting-up-external-secrets-operator">Setting up External Secrets Operator</h2>
<p>As we did before for the other dependencies, we will set up the External Secrets Operator in a declarative way using Helm.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir external-secrets-operator/
</span></span><span class="line"><span class="cl">touch external-secrets-operator/<span class="o">{</span>Chart,requirements<span class="o">}</span>.yaml
</span></span></code></pre></td></tr></table>
</div>
</div><p>The content for the <code>Chart.yaml</code> file is:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">external-secrets-operator</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="m">0.1.0</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>The content for <code>requirements.yaml</code> is:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">dependencies</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">external-secrets</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="m">0.6.1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">repository</span><span class="p">:</span><span class="w"> </span><span class="l">https://charts.external-secrets.io</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>Now we can install the chart:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> external-secrets-operator
</span></span><span class="line"><span class="cl">helm dependency update
</span></span><span class="line"><span class="cl">helm install external-secrets-operator . -n external-secrets-operator  --create-namespace
</span></span></code></pre></td></tr></table>
</div>
</div><p>Perfect, we have everything set up. Now we can create the secret that will contain the private key.</p>
<!-- vim-markdown-toc Redcarpet -->
<!-- vim-markdown-toc -->
<h2 id="creating-the-secret">Creating the secret</h2>
<p>Make sure that your IAM role has access to the SSM parameter store. The role will need a policy similar to the following:</p>
<pre tabindex="0"><code>resource &#34;aws_iam_policy&#34; &#34;ssm_parameter_policy&#34; {
  name = &#34;cluster-ssm-parameter-policy&#34;

  policy = jsonencode({
    &#34;Version&#34; : &#34;2012-10-17&#34;,
    &#34;Statement&#34; : [
      {
        &#34;Action&#34; : [
          &#34;ssm:DescribeParameters&#34;,
          &#34;ssm:GetParameter&#34;,
          &#34;ssm:GetParameters&#34;,
          &#34;ssm:GetParametersByPath&#34;
        ],
        &#34;Effect&#34; : &#34;Allow&#34;,
        &#34;Resource&#34; : [
          &#34;arn:aws:ssm:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:/argo-cd/*&#34;
        ]
      },
    ]
  })
}
</code></pre><p>With that out of the way, let&rsquo;s define the Secret Store:</p>
<pre tabindex="0"><code>apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: my-aws-secret-store
  # we need the secret to exist in the same namespace as ArgoCD
  namespace: argocd
spec:
  provider:
    aws:
      service: ParameterStore
      # define a specific role to limit access
      # to certain secrets
      region: us-east-1
</code></pre><p>We are going to get the AWS credentials from the current environment. If you wish to use a secret and key stored in a Kubernetes secret, you could add the section:</p>
<pre tabindex="0"><code>      # Auth defines the information necessary to authenticate against AWS by
      # getting the accessKeyID and secretAccessKey from an already created Kubernetes Secret
      auth:
        secretRef:
          accessKeyID:
            name: awssm-secret
            key: access-key
          secretAccessKey:
            name: awssm-secret
            key: secret-access-key
</code></pre><p>Reference: <a href="https://external-secrets.io/v0.6.1/api/secretstore/">https://external-secrets.io/v0.6.1/api/secretstore/</a></p>
<p>Ok, now we can create the external secret. If we take a look at the documentation, the secret should look like the following:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Secret</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">private-repo</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">argocd</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">argocd.argoproj.io/secret-type</span><span class="p">:</span><span class="w"> </span><span class="l">repository</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">stringData</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">git</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="l">git@github.com:argoproj/my-private-repository</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">sshPrivateKey</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">    -----BEGIN OPENSSH PRIVATE KEY-----
</span></span></span><span class="line"><span class="cl"><span class="sd">    ...
</span></span></span><span class="line"><span class="cl"><span class="sd">    -----END OPENSSH PRIVATE KEY-----</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>To generate a secret with that structure, we will use the <code>template</code> feature of the External Secrets Operator to create the secret. The external secret will look like the following:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">external-secrets.io/v1beta1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">ExternalSecret</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">private-repo-ssh-key</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">argocd</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c"># SecretStoreRef defines which SecretStore to use when fetching the secret data</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">secretStoreRef</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">my-aws-secret-store</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">SecretStore </span><span class="w"> </span><span class="c"># or ClusterSecretStore</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c"># Specify a blueprint for the resulting Kind=Secret</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">target</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">my-aws-secret-repo-ssh-key</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">template</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">argocd.argoproj.io/secret-type</span><span class="p">:</span><span class="w"> </span><span class="l">repository</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="c"># Use inline templates to construct your desired config file that contains your secret</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">data</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">git</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="l">git@github.com:rderik/slartypriv.git</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">sshPrivateKey</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">          {{ .sshPrivateKey | toString }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">data</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">secretKey</span><span class="p">:</span><span class="w"> </span><span class="l">sshPrivateKey</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">remoteRef</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">/argo-cd/github-repo/ssh-key</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>Remember to update the <code>url</code> and your SSM parameter key to match your repositories. Remember you are going to be using ssh and not <code>https</code>.</p>
<p>With that out of the way, we can apply the two templates:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl apply -f secret-store.yaml
</span></span><span class="line"><span class="cl">kubectl apply -f external-secret.yaml
</span></span></code></pre></td></tr></table>
</div>
</div><p>If we get any errors and need to review what is going on, we can use the following command:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl describe SecretStore my-aws-secret-store -n argocd
</span></span><span class="line"><span class="cl">kubectl describe ExternalSecret private-repo-ssh-key -n argocd
</span></span></code></pre></td></tr></table>
</div>
</div><p>And if we want to check if the secret created in Kubernetes matches our SSH key:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl get secret my-aws-secret-repo-ssh-key <span class="se">\
</span></span></span><span class="line"><span class="cl">  -n argocd -o <span class="nv">jsonpath</span><span class="o">=</span><span class="s2">&#34;{.data.sshPrivateKey}&#34;</span> <span class="p">|</span> base64 -d
</span></span></code></pre></td></tr></table>
</div>
</div><p>That&rsquo;s it. Now we can use our private repository in ArgoCD.</p>
<p>I created a private repository with the following <code>url</code>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git@github.com:rderik/slartypriv.git
</span></span></code></pre></td></tr></table>
</div>
</div><p>And the project in ArgoCD looks like the following:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">argoproj.io/v1alpha1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">AppProject</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">server-proj-private</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">argocd</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c"># Finaliser that ensures that the project is not deleted until any application does not reference it</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">finalizers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">resources-finalizer.argocd.argoproj.io</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="c"># Project description</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">description</span><span class="p">:</span><span class="w"> </span><span class="l">Server Project</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">destinations</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c"># Update your namespace</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">my-namespace-private</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">server</span><span class="p">:</span><span class="w"> </span><span class="l">https://kubernetes.default.svc</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">sourceRepos</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c"># Make sure to add your repository here</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">git@github.com:rderik/slartypriv.git</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">clusterResourceWhitelist</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">group</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;*&#39;</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>The application looks like the following:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">argoproj.io/v1alpha1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Application</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">server-private</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">argocd</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">destination</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">my-namespace-private</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">server</span><span class="p">:</span><span class="w"> </span><span class="l">https://kubernetes.default.svc</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">project</span><span class="p">:</span><span class="w"> </span><span class="l">server-proj-private</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">source</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c"># Change the repository to your repository</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">repoURL</span><span class="p">:</span><span class="w"> </span><span class="l">git@github.com:rderik/slartypriv.git</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c"># point the path to the path where your Kubernetes templates are</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">server</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">syncPolicy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">automated</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">prune</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">selfHeal</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">allowEmpty</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>Notice that the <code>sourceRepos</code> and the <code>repoURL</code> have changed from <code>https</code> to ssh, so update your repository URLs correctly to use ssh. If we apply the project and the application, we should see it working without a problem in the ArgoCD dashboard.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl apply -f project-private.yaml
</span></span><span class="line"><span class="cl">kubectl apply -f application-private.yaml
</span></span></code></pre></td></tr></table>
</div>
</div><p>And That&rsquo;s it! We now have a private repository in ArgoCD.</p>
<!-- vim-markdown-toc Redcarpet -->
<!-- vim-markdown-toc -->
<h2 id="final-thoughts">Final thoughts</h2>
<p>There are many moving parts when we are working with Kubernetes and ArgoCD, and we need to pay a lot of attention to all the small details. I got stuck in the past debugging for a long time until I realised that the <code>repoURL</code> was still using <code>https</code> instead of ssh. And I got stuck another time until I realised I had a typo on the Secret template.</p>
<p>Anyways, I hope this is useful.</p>
<!-- vim-markdown-toc Redcarpet -->
<!-- vim-markdown-toc -->
<h2 id="references">References</h2>
<ul>
<li><a href="https://external-secrets.io/">External Secrets Operator (ESO)</a></li>
<li><a href="https://external-secrets.io/v0.6.1/provider/aws-parameter-store/">ESO - AWS Parameter Store Provider</a></li>
<li><a href="https://external-secrets.io/v0.6.1/api/externalsecret/">ESO - ExternalSecret</a></li>
<li><a href="https://external-secrets.io/v0.6.1/api/secretstore/">ESO - SecretStore</a></li>
<li><a href="https://external-secrets.io/v0.6.1/guides/templating/">ESO - advanced templating</a></li>
<li><a href="https://argo-cd.readthedocs.io/en/stable/operator-manual/declarative-setup">ArgoCD - declarative setup</a></li>
</ul>
]]></content></entry><entry><title>Using IAM Roles for Kubernetes service accounts in AWS EKS using Terraform</title><link rel="alternate" href="https://rderik.com/blog/using-iam-roles-for-kubernetes-service-accounts-in-aws-eks-using-terraform/"/><id>https://rderik.com/blog/using-iam-roles-for-kubernetes-service-accounts-in-aws-eks-using-terraform/</id><published>2022-11-19T11:23:00Z</published><updated>2022-11-19T11:23:00Z</updated><author><name>Derik Ramirez</name></author><content type="html"><![CDATA[<p>Part of the design principles of the security pillar in the AWS Well architected framework is &ldquo;Implement a strong identity foundation&rdquo;, that is:</p>
<p>&ldquo;Implement the principle of least privilege and enforce separation of duties with appropriate authorization for each interaction with your AWS resources. Centralize identity management, and aim to eliminate reliance on long-term static credentials.&rdquo;</p>
<p><a href="https://docs.aws.amazon.com/wellarchitected/latest/security-pillar/design-principles.html">Well Architected Framework - Security Pillar - Design principles</a></p>
<p>When we start using Kubernetes in AWS EKS, we might take some shortcuts during the learning phase and add all the policies to a role that we directly assign to our nodes. The issue is that we give much more privileges than we require just for practicality. And it can also become a habit. The way we learn something sometimes becomes our default pattern. So let&rsquo;s break that habit and explore how to set IAM Roles to Kubernetes service accounts in AWS EKS.</p>
<p>Note: If you are interested in learning more about how to set up the directory structure for your Terraform project, you might find my guide, <a href="https://rderik.com/guides/meditations_directory_structure_tf_projects">Meditations on Directory Structure for Terraform Projects</a>, useful.</p>
<div class="rd-toc">
  <nav id="TableOfContents">
  <ul>
    <li><a href="#what-are-iam-roles-for-kubernetes-service-accounts">What are IAM Roles for Kubernetes service accounts?</a></li>
    <li><a href="#set-up-oidc-provider">Set up OIDC provider</a></li>
    <li><a href="#creating-the-iam-role">Creating the IAM role</a></li>
    <li><a href="#create-the-ssm-parameter">Create the SSM parameter</a></li>
    <li><a href="#create-the-kubernetes-configuration">Create the Kubernetes configuration</a></li>
    <li><a href="#test-that-everything-is-working">Test that everything is working</a></li>
    <li><a href="#final-thoughts">Final thoughts</a></li>
    <li><a href="#references">References</a></li>
  </ul>
</nav>
</div>

<div style="clear:both"></div>
<hr />
<article class="thumbnails">
  <div>
    <a target="_blank" href="https://rderik.gumroad.com/l/bash-beyond-basics"><img src="/images/shop/thumbnail_bash_beyond_basics_v1_0_0.jpg" width="300px"></a>
    <div class="description">
      <h1><a target="_blank" href="https://rderik.gumroad.com/l/bash-beyond-basics">Bash Beyond Basics</a>
        <span>Increase your efficiency and understanding of the shell</span></h1>
      <p>
      If you are interested in this topic you might enjoy my course Bash Byond Basics.
      This course helps you level up your bash skills. This is not a course on shell-scripting, is a course on improving your efficiency by showing you the features of bash that are seldom discussed and often ignored.
      </p>
      <p>
      Every day you spend many hours working in the shell, every little improvement in your worklflows will pay dividends many fold!
      </p>
      <a href="https://rderik.gumroad.com/l/bash-beyond-basics">Learn more</a>
    </div>
  </div>
</article>
<div style="clear:both"></div>
<hr />
<!-- vim-markdown-toc Redcarpet -->
<!-- vim-markdown-toc -->
<h2 id="what-are-iam-roles-for-kubernetes-service-accounts">What are IAM Roles for Kubernetes service accounts?</h2>
<p>The IAM Roles for Kubernetes service accounts allow us to associate an IAM role with a Kubernetes service account. This feature is available through the Amazon EKS Pod Identity Webhook. The IAM role for a service account is then used to provide AWS credentials to the pod or resource using the service account. These credentials are automatically rotated before they expire.</p>
<p>If you have used WebIdentity federation with AWS Cognito, you will be familiar with the concept of IAM roles for service accounts.</p>
<p>Ok, so what do we need?</p>
<ul>
<li>OIDC Issuer - Already set up if you are running in EKS</li>
<li>OIDC provider</li>
<li>The IAM role that we want to associate with the service account</li>
<li>The Kubernetes service account that we want to associate with the IAM role</li>
</ul>
<!-- vim-markdown-toc Redcarpet -->
<!-- vim-markdown-toc -->
<h2 id="set-up-oidc-provider">Set up OIDC provider</h2>
<p>The first step is to set up the OIDC provider. OIDC, if you are unfamiliar, is an authentication protocol similar to SAML, but it is based on JSON Web Tokens (JWT). AWS also uses it to authenticate users in AWS Cognito.</p>
<p>There are two parts to using OIDC an Issuer and a Provider. The issuer is the entity that issues the token, and the provider is the entity that validates the token. In our case, the issuer is handled by AWS, and Kubernetes handle the provider. When we create the EKS cluster, we already have an Issuer endpoint. If you want to use your OIDC issuer, you can provide that to EKS. But in this example, we&rsquo;ll stick to the issuer already in the EKS cluster.</p>
<p>To get the URL of the issuer, run the following command:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">aws eks describe-cluster --name temp-cluster --query <span class="s2">&#34;cluster.identity.oidc.issuer&#34;</span> --output text
</span></span></code></pre></td></tr></table>
</div>
</div><p>You should see something like the following:</p>
<pre tabindex="0"><code>https://oidc.eks.us-east-1.amazonaws.com/id/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
</code></pre><p>Let&rsquo;s check first if an OIDC provider is already created for our cluster. Run the following command:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">aws iam list-open-id-connect-providers
</span></span></code></pre></td></tr></table>
</div>
</div><p>If you get an empty list, then you need to create the OIDC provider. Let&rsquo;s define it in terraform:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-hcl" data-lang="hcl"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">locals</span> {
</span></span><span class="line"><span class="cl"><span class="n">  oidc_issuer</span> <span class="o">=</span> <span class="s2">&#34;https://oidc.eks.us-east-1.amazonaws.com/id/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&#34;</span><span class="c1">
</span></span></span><span class="line"><span class="cl"><span class="c1">  # We&#39;ll use the cluster_namespace and the service_account_name later
</span></span></span><span class="line"><span class="cl"><span class="n">  cluster_namespace</span> <span class="o">=</span> <span class="s2">&#34;my-namespace&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">  service_account_name</span> <span class="o">=</span> <span class="s2">&#34;my-service-account&#34;</span>
</span></span><span class="line"><span class="cl">}<span class="c1">
</span></span></span><span class="line"><span class="cl"><span class="c1">
</span></span></span><span class="line"><span class="cl"><span class="c1"># Set open IDC provider
</span></span></span><span class="line"><span class="cl"><span class="k">data</span> <span class="s2">&#34;tls_certificate&#34; &#34;cluster&#34;</span> {<span class="c1">
</span></span></span><span class="line"><span class="cl"><span class="c1">  # If we created the EKS cluster in terraform, we would do something like the following
</span></span></span><span class="line"><span class="cl"><span class="c1">  # url = aws_eks_cluster.main.identity[0].oidc[0].issuer
</span></span></span><span class="line"><span class="cl"><span class="n">  url</span> <span class="o">=</span> <span class="k">local</span><span class="p">.</span><span class="k">oidc_issuer</span>
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_iam_openid_connect_provider&#34; &#34;cluster&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  client_id_list</span>  <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;sts.amazonaws.com&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="n">  thumbprint_list</span> <span class="o">=</span> <span class="p">[</span><span class="k">data</span><span class="p">.</span><span class="k">tls_certificate</span><span class="p">.</span><span class="k">cluster</span><span class="p">.</span><span class="k">certificates</span><span class="p">.</span><span class="m">0</span><span class="p">.</span><span class="k">sha1_fingerprint</span><span class="p">]</span><span class="c1">
</span></span></span><span class="line"><span class="cl"><span class="c1">  # If we created the EKS cluster in terraform, we would do something like the following
</span></span></span><span class="line"><span class="cl"><span class="c1">  # url             = aws_eks_cluster.main.identity[0].oidc[0].issuer
</span></span></span><span class="line"><span class="cl"><span class="n">  url</span> <span class="o">=</span> <span class="k">local</span><span class="p">.</span><span class="k">oidc_issuer</span>
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></td></tr></table>
</div>
</div><p>Now, if we run the command to list the OIDC providers, we should see the one we just created:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">aws iam list-open-id-connect-providers
</span></span></code></pre></td></tr></table>
</div>
</div><p>With the OIDC provider created, we can now create the IAM role and the Kubernetes service account.</p>
<!-- vim-markdown-toc Redcarpet -->
<!-- vim-markdown-toc -->
<h2 id="creating-the-iam-role">Creating the IAM role</h2>
<p>We will create a new IAM role, and we will call it <code>eks-test-role</code>. We want the pod to read a parameter from the SSM parameter store, so we will also create a policy for that and attach it to the same role.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-hcl" data-lang="hcl"><span class="line"><span class="cl"><span class="k">data</span> <span class="s2">&#34;aws_region&#34; &#34;current&#34;</span> {}
</span></span><span class="line"><span class="cl"><span class="k">data</span> <span class="s2">&#34;aws_caller_identity&#34; &#34;current&#34;</span> {}<span class="c1">
</span></span></span><span class="line"><span class="cl"><span class="c1">
</span></span></span><span class="line"><span class="cl"><span class="c1"># SSM Parameter Store Service Role
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_iam_role&#34; &#34;pod_sa&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  name</span> <span class="o">=</span> <span class="s2">&#34;eks-test-role&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">  assume_role_policy</span> <span class="o">=</span> <span class="k">data</span><span class="p">.</span><span class="k">aws_iam_policy_document</span><span class="p">.</span><span class="k">pod_sa_assume_role_policy</span><span class="p">.</span><span class="k">json</span>
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">data</span> <span class="s2">&#34;aws_iam_policy_document&#34; &#34;pod_sa_assume_role_policy&#34;</span> {
</span></span><span class="line"><span class="cl">  <span class="k">statement</span> {
</span></span><span class="line"><span class="cl"><span class="n">    effect</span>  <span class="o">=</span> <span class="s2">&#34;Allow&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">    actions</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;sts:AssumeRoleWithWebIdentity&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">principals</span> {
</span></span><span class="line"><span class="cl"><span class="n">      type</span>        <span class="o">=</span> <span class="s2">&#34;Federated&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      identifiers</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;arn:aws:iam::${data.aws_caller_identity.current.account_id}:oidc-provider/${replace(local.oidc_issuer, &#34;https://&#34;, &#34;&#34;)}&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    }
</span></span><span class="line"><span class="cl">    <span class="k">condition</span> {
</span></span><span class="line"><span class="cl"><span class="n">      test</span>     <span class="o">=</span> <span class="s2">&#34;StringEquals&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      variable</span> <span class="o">=</span> <span class="s2">&#34;${replace(local.oidc_issuer, &#34;https://&#34;, &#34;&#34;)}:aud&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">      values</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;sts.amazonaws.com&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">condition</span> {
</span></span><span class="line"><span class="cl"><span class="n">      test</span>     <span class="o">=</span> <span class="s2">&#34;StringEquals&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      variable</span> <span class="o">=</span> <span class="s2">&#34;${replace(local.oidc_issuer, &#34;https://&#34;, &#34;&#34;)}:sub&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">      values</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;system:serviceaccount:${local.cluster_namespace}:${local.service_account_name}&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    }
</span></span><span class="line"><span class="cl">  }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></td></tr></table>
</div>
</div><p>We will create an additional policy allowing the pod to read any parameter in the parameter store. We are going to call it <code>cluster-ssm-parameter-policy</code>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-hcl" data-lang="hcl"><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_iam_role_policy_attachment&#34; &#34;sa_role_ssm_parameter_policy&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  policy_arn</span> <span class="o">=</span> <span class="k">aws_iam_policy</span><span class="p">.</span><span class="k">ssm_parameter_policy</span><span class="p">.</span><span class="k">arn</span>
</span></span><span class="line"><span class="cl"><span class="n">  role</span>       <span class="o">=</span> <span class="k">aws_iam_role</span><span class="p">.</span><span class="k">pod_sa</span><span class="p">.</span><span class="k">name</span>
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_iam_policy&#34; &#34;ssm_parameter_policy&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  name</span> <span class="o">=</span> <span class="s2">&#34;cluster-ssm-parameter-policy&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">  policy</span> <span class="o">=</span> <span class="k">jsonencode</span><span class="p">(</span>{
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;Version&#34; : &#34;2012-10-17&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;Statement&#34;</span> <span class="err">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">      {
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Action&#34;</span> <span class="err">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;ssm:DescribeParameters&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;ssm:GetParameter&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;ssm:GetParameters&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;ssm:GetParametersByPath&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Effect&#34; : &#34;Allow&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;Resource&#34;</span> <span class="err">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">          <span class="s2">&#34;arn:aws:ssm:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:parameter/*&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">]</span>
</span></span><span class="line"><span class="cl">      }<span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl">  }<span class="p">)</span>
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></td></tr></table>
</div>
</div><p>We will create a go program that will serve as a basic HTTP server. The server&rsquo;s sole function is to echo the parameter value. For Kubernetes to be able to pull the image from ECR, we need to create an ECR repository. Let&rsquo;s do that. We are going to call it <code>test-ecr</code>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-hcl" data-lang="hcl"><span class="line"><span class="cl"><span class="c1"># ECR
</span></span></span><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_ecr_repository&#34; &#34;main&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  name</span> <span class="o">=</span> <span class="s2">&#34;test-ecr&#34;</span>
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_ssm_parameter&#34; &#34;ecr&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  name</span>  <span class="o">=</span> <span class="s2">&#34;/ecr/ecr-url&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">  type</span>  <span class="o">=</span> <span class="s2">&#34;String&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">  value</span> <span class="o">=</span> <span class="k">aws_ecr_repository</span><span class="p">.</span><span class="k">main</span><span class="p">.</span><span class="k">repository_url</span>
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_ecr_lifecycle_policy&#34; &#34;main&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  repository</span> <span class="o">=</span> <span class="k">aws_ecr_repository</span><span class="p">.</span><span class="k">main</span><span class="p">.</span><span class="k">name</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">  policy</span> <span class="o">=</span> <span class="err">&lt;&lt;</span><span class="k">EOF</span>
</span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;rules&#34;</span><span class="err">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        {
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;rulePriority&#34;</span><span class="err">:</span> <span class="m">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;description&#34;: &#34;Keep last 10 images&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;selection&#34;</span><span class="err">:</span> {
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;tagStatus&#34;: &#34;tagged&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;tagPrefixList&#34;: [&#34;v&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;countType&#34;: &#34;imageCountMoreThan&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;countNumber&#34;</span><span class="err">:</span> <span class="m">10</span>
</span></span><span class="line"><span class="cl">            }<span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="s2">&#34;action&#34;</span><span class="err">:</span> {
</span></span><span class="line"><span class="cl">                <span class="s2">&#34;type&#34;: &#34;expire&#34;</span>
</span></span><span class="line"><span class="cl">            }
</span></span><span class="line"><span class="cl">        }
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl"><span class="k">EOF</span>
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></td></tr></table>
</div>
</div><p>The following is a quick and dirty go program that will read the parameter from the parameter store and echo it:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="s">&#34;fmt&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="s">&#34;log&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="s">&#34;net/http&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="s">&#34;github.com/aws/aws-sdk-go/aws&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="s">&#34;github.com/aws/aws-sdk-go/aws/session&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="s">&#34;github.com/aws/aws-sdk-go/service/ssm&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nx">http</span><span class="p">.</span><span class="nf">HandleFunc</span><span class="p">(</span><span class="s">&#34;/&#34;</span><span class="p">,</span><span class="w"> </span><span class="kd">func</span><span class="p">(</span><span class="nx">w</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span><span class="w"> </span><span class="nx">r</span><span class="w"> </span><span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// Create an SSM client</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">sess</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">session</span><span class="p">.</span><span class="nf">NewSession</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">aws</span><span class="p">.</span><span class="nx">Config</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nx">Region</span><span class="p">:</span><span class="w"> </span><span class="nx">aws</span><span class="p">.</span><span class="nf">String</span><span class="p">(</span><span class="s">&#34;us-east-1&#34;</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nb">panic</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">svc</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">ssm</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="nx">sess</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// Get the parameter</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">param</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">svc</span><span class="p">.</span><span class="nf">GetParameter</span><span class="p">(</span><span class="o">&amp;</span><span class="nx">ssm</span><span class="p">.</span><span class="nx">GetParameterInput</span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nx">Name</span><span class="p">:</span><span class="w"> </span><span class="nx">aws</span><span class="p">.</span><span class="nf">String</span><span class="p">(</span><span class="s">&#34;/my/parameter&#34;</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintf</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;Error:&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintf</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="p">.</span><span class="nf">Error</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="c1">// Print the value</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintf</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;Greeting: &#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintf</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span><span class="w"> </span><span class="o">*</span><span class="nx">param</span><span class="p">.</span><span class="nx">Parameter</span><span class="p">.</span><span class="nx">Value</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Starting server in port 8080&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nf">ListenAndServe</span><span class="p">(</span><span class="s">&#34;:8080&#34;</span><span class="p">,</span><span class="w"> </span><span class="kc">nil</span><span class="p">);</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nx">log</span><span class="p">.</span><span class="nf">Fatal</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>We will also need a Dockerfile to build the image:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="k">FROM</span><span class="w"> </span><span class="s">golang:1.19-alpine</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">WORKDIR</span><span class="w"> </span><span class="s">/usr/src/app</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">COPY</span> go.mod go.sum ./<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">RUN</span> go mod download <span class="o">&amp;&amp;</span> go mod verify<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">COPY</span> main.go .<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">RUN</span> go build -v -o /usr/local/bin/app ./...<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">EXPOSE</span><span class="w"> </span><span class="s">8080</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="k">CMD</span> <span class="p">[</span> <span class="s2">&#34;app&#34;</span> <span class="p">]</span><span class="err">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>We can now build the image and push it to ECR. Let&rsquo;s create a file called <code>deploy.sh</code> and add the following content:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/bin/bash
</span></span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">AWS_ECR_URL</span><span class="o">=</span><span class="k">$(</span>aws ecr describe-repositories <span class="p">|</span> jq -r <span class="s1">&#39;.repositories[0].repositoryUri&#39;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">docker build -f Dockerfile . -t <span class="si">${</span><span class="nv">AWS_ECR_URL</span><span class="si">}</span>:latest
</span></span><span class="line"><span class="cl">aws ecr get-login-password <span class="p">|</span> docker login --username AWS --password-stdin <span class="k">$(</span><span class="nb">echo</span> <span class="nv">$AWS_ECR_URL</span> <span class="p">|</span> sed -r <span class="s2">&#34;s/\/.*//g&#34;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">docker push <span class="si">${</span><span class="nv">AWS_ECR_URL</span><span class="si">}</span>:latest
</span></span></code></pre></td></tr></table>
</div>
</div><p>We can now run the script:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">chmod +x deploy.sh
</span></span><span class="line"><span class="cl">./deploy.sh
</span></span></code></pre></td></tr></table>
</div>
</div><p>And we should have our Image ready to deploy in Kubernetes.</p>
<!-- vim-markdown-toc Redcarpet -->
<!-- vim-markdown-toc -->
<h2 id="create-the-ssm-parameter">Create the SSM parameter</h2>
<p>We are going to create an SSM parameter that the pod will read. We are going to call it <code>/my/parameter</code>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-hcl" data-lang="hcl"><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_ssm_parameter&#34; &#34;my_parameter&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  name</span>  <span class="o">=</span> <span class="s2">&#34;/my/parameter&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">  type</span>  <span class="o">=</span> <span class="s2">&#34;String&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">  value</span> <span class="o">=</span> <span class="s2">&#34;Hola Mundo&#34;</span>
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></td></tr></table>
</div>
</div><!-- vim-markdown-toc Redcarpet -->
<!-- vim-markdown-toc -->
<h2 id="create-the-kubernetes-configuration">Create the Kubernetes configuration</h2>
<p>We will create the Kubernetes <code>namespace.yml</code>, <code>deployment.yml</code>, <code>service.yml</code>, and <code>service-account.yml</code>.</p>
<p>The <code>namespace.yml</code> file will create the namespace that we are going to use:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Namespace</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">my-namespace</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>The <code>service-account.yml</code> file will create the service account that we are going to use:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">ServiceAccount</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">my-service-account</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">my-namespace</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">annotations</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">eks.amazonaws.com/role-arn</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;arn:aws:iam::&lt;AWS_ACCOUNT_ID&gt;:role/&lt;ROLE_NAME&gt;&#34;</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>Replace <code>&lt;AWS_ACCOUNT_ID&gt;</code> and <code>&lt;ROLE_NAME&gt;</code> with the values you created in the previous steps.</p>
<p>The <code>service.yml</code> file will create the service that we are going to use:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Service</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">sserver</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">my-namespace</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="m">80</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">targetPort</span><span class="p">:</span><span class="w"> </span><span class="m">8080</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">protocol</span><span class="p">:</span><span class="w"> </span><span class="l">TCP</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">NodePort</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">selector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">sserver</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>The <code>deployment.yml</code> file will create the deployment that we are going to use:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">apps/v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Deployment</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">sserver</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">my-namespace</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">selector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">matchLabels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">sserver</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">replicas</span><span class="p">:</span><span class="w"> </span><span class="m">1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">template</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">sserver</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">serviceAccountName</span><span class="p">:</span><span class="w"> </span><span class="l">my-service-account</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">containers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">sserver</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">&lt;ERC_URL&gt;:latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">containerPort</span><span class="p">:</span><span class="w"> </span><span class="m">8080</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>Replace <code>&lt;ERC_URL&gt;</code> with the value of the ECR we created in the previous steps.</p>
<p>Ok, now let&rsquo;s deploy everything:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl apply -f namespace.yml
</span></span><span class="line"><span class="cl">kubectl apply -f service-account.yml  
</span></span><span class="line"><span class="cl">kubectl apply -f service.yml
</span></span><span class="line"><span class="cl">kubectl apply -f deployment.yml
</span></span></code></pre></td></tr></table>
</div>
</div><p>And we should be ready to test.</p>
<!-- vim-markdown-toc Redcarpet -->
<!-- vim-markdown-toc -->
<h2 id="test-that-everything-is-working">Test that everything is working</h2>
<p>We can now test if our pod uses the service account we assigned.</p>
<p>We can port forward to our local host and hit our endpoint to see if we get the parameter&rsquo;s value. Assuming we only have one pod running in my namespace, we can do the following:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl port-forward <span class="k">$(</span>kubectl get pods -n my-namespace --no-headers <span class="p">|</span> awk <span class="s1">&#39;{print $1}&#39;</span><span class="k">)</span> 8080:8080 -n my-namespace
</span></span></code></pre></td></tr></table>
</div>
</div><p>In a different terminal, we can do a curl to the endpoint:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl localhost:8080
</span></span></code></pre></td></tr></table>
</div>
</div><p>And we should see:</p>
<pre tabindex="0"><code>Greeting: Hola Mundo
</code></pre><!-- vim-markdown-toc Redcarpet -->
<!-- vim-markdown-toc -->
<h2 id="final-thoughts">Final thoughts</h2>
<p>Setting up the OIDC might be the most confusing part if we haven&rsquo;t worked with it before. But remember you need an Issuer, which is already provided if you use AWS EKS. You can link to an external issuer in the same way. We also need a provider accessible to our cluster, as we did.</p>
<p>After the OIDC part is completed, we only need to create our IAM roles as we usually do and build the association by creating a service account and assigning the role to it.</p>
<p>I hope this helps you to understand how to use IAM roles for service accounts in Kubernetes.</p>
<!-- vim-markdown-toc Redcarpet -->
<!-- vim-markdown-toc -->
<h2 id="references">References</h2>
<p><a href="https://docs.aws.amazon.com/eks/latest/userguide/service-accounts.html">AWS Documentation - Kubernetes service accounts</a>
<a href="https://docs.aws.amazon.com/wellarchitected/latest/framework/welcome.html">AWS Documentation - Well architected framework</a>
<a href="https://developer.okta.com/blog/2019/10/21/illustrated-guide-to-oauth-and-oidc">Illustrated guide to OAuth and OpenID Connect</a></p>
]]></content></entry><entry><title>Setting up an EKS Kubernetes cluster for learning</title><link rel="alternate" href="https://rderik.com/blog/setting-up-a-eks-kubernetes-cluster-for-learning/"/><id>https://rderik.com/blog/setting-up-a-eks-kubernetes-cluster-for-learning/</id><published>2022-11-12T11:22:00Z</published><updated>2022-11-12T11:22:00Z</updated><author><name>Derik Ramirez</name></author><content type="html"><![CDATA[<p>The fastest way to learn something is through practice. Most of my work is on AWS, so running a local Kubernetes cluster is not the best option. I want to test Kubernetes integration with other AWS services. To run experiments, I create a cluster using <code>eksctl</code>, run my tests and then destroy it.</p>
<p>In this post, I will assume that you already have an AWS account and have installed the <code>aws</code> cli, <code>eksctl</code> and <code>kubectl</code> on your machine.</p>
<p>This post will be short. It will only serve as a blueprint to get you started. I&rsquo;ll be using the <code>us-east-1</code> region, but you can use any region you want.</p>
<p>If you don&rsquo;t know which region to use, you could try to use <a href="https://www.cloudping.info/">https://www.cloudping.info/</a> to check the latency between your machine and the AWS regions (It also works for other providers). There are many other factors that you should consider, like cost differences for resources in different regions, etcetera, but for this quick test, you could use latency.</p>
<h2 id="ssh-keys">SSH keys</h2>
<p>We need ssh keys to access the cluster nodes. We will use the <code>aws</code> CLI to create a key pair and register it in the AWS Key Management Service (KMS).</p>
<pre tabindex="0"><code>aws ec2 create-key-pair --key-name temp-key --query &#39;KeyMaterial&#39; --output text &gt; temp-key.pem
</code></pre><p>Reference: <a href="https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ec2/create-key-pair.html">AWS Documentation - create key pair</a></p>
<p>If you are unfamiliar with SSH keys, maybe read the first two sections of my article <a href="https://rderik.com/blog/understanding-ssh-keys-and-using-keychain-to-manage-passphrase-on-macos/">Understanding SSH Keys and using Keychain to manage passphrase on macOS</a>.</p>
<p>The previous command will generate the private key and store it in <code>temp-key.pem</code>. To get the public key, you can run the following:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ssh-keygen -y -f temp-key.pem &gt; temp-key.pub
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="create-the-cluster">Create the cluster</h2>
<p>I prefer to keep my infrastructure as code. Even if we will be using <code>eksctl</code> in an imperative way, it is nice to reference back to where I have the initial settings. For that, we will be using a configuration file. Create a new file. I&rsquo;ll name it <code>cluster.yml</code> with the following content:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">eksctl.io/v1alpha5</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">ClusterConfig</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">temp-cluster</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">region</span><span class="p">:</span><span class="w"> </span><span class="l">us-east-1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">nodeGroups</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ng-1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c"># the t3.medium supports 3 ENIs, so keep that in mind on how many pods you can put in the Node</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">instanceType</span><span class="p">:</span><span class="w"> </span><span class="l">t3.medium</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">desiredCapacity</span><span class="p">:</span><span class="w"> </span><span class="m">2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">ssh</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">publicKeyPath</span><span class="p">:</span><span class="w"> </span><span class="l">temp-key.pub</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>Now we can apply the configuration:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">eksctl create cluster -f cluster.yml
</span></span></code></pre></td></tr></table>
</div>
</div><p>The creation process takes a while, about 20 mins.</p>
<h2 id="connect-to-the-cluster">Connect to the cluster</h2>
<p>Once the cluster is created, we want to set up <code>kubectl</code> to manage the cluster. We can do that by running the following:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">aws eks --region us-east-1 update-kubeconfig --name temp-cluster
</span></span></code></pre></td></tr></table>
</div>
</div><p>And finally, we can start working on the newly created cluster. Let&rsquo;s check the services:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl get svc
</span></span></code></pre></td></tr></table>
</div>
</div><p>You should see something like the following:</p>
<pre tabindex="0"><code>NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.100.0.1   &lt;none&gt;        443/TCP   10m
</code></pre><p>Perfect! We have a cluster up and running.</p>
<p>Now you can run your tests and destroy the cluster when you are done.</p>
<h2 id="modifying-the-node-group">Modifying the Node Group</h2>
<p>If you need to modify the Node Group, you can do that by editing the <code>cluster.yml</code> file and running:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">eksctl update cluster -f cluster.yml
</span></span></code></pre></td></tr></table>
</div>
</div><p>Remember that not all aspects of the node group can be modified, for example, the instance type. If you need to change the instance type, you must create a new node group and delete the old one.</p>
<h2 id="destroy-the-cluster">Destroy the cluster</h2>
<p>To clean up the cluster, we can run the following:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">eksctl delete cluster -f cluster.yml
</span></span></code></pre></td></tr></table>
</div>
</div><p>And that&rsquo;s it! Happy hacking!</p>
<h2 id="final-thoughts">Final thoughts</h2>
<p>This was a quick and simple post to learn how to create your Kubernetes cluster and start learning. The instructions here are straightforward. The interesting part is when you start modifying your cluster and node group configuration.</p>
<p>Anyways, I hope it was helpful.</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://www.cloudping.info/">https://www.cloudping.info/</a> to check the latency between your machine and the AWS regions.</li>
<li><a href="https://awscli.amazonaws.com/v2/documentation/api/latest/reference/ec2/create-key-pair.html">AWS Documentation - create key pair</a></li>
<li><a href="https://rderik.com/blog/understanding-ssh-keys-and-using-keychain-to-manage-passphrase-on-macos/">Understanding SSH Keys and using Keychain to manage passphrase on macOS</a></li>
<li><a href="https://eksctl.io/usage/schema/">eksctl documentation - the schema with all the options for your <code>cluster.yml</code></a></li>
<li><a href="https://docs.aws.amazon.com/eks/latest/userguide/delete-cluster.html">AWS Documentation - deleting an Amazon EKS cluster</a></li>
</ul>
]]></content></entry><entry><title>Setting up a Kubernetes Cluster in Amazon EKS using Terraform</title><link rel="alternate" href="https://rderik.com/blog/setting-up-a-kubernetes-cluster-in-amazon-eks-using-terraform/"/><id>https://rderik.com/blog/setting-up-a-kubernetes-cluster-in-amazon-eks-using-terraform/</id><published>2022-11-05T11:28:00+01:00</published><updated>2022-11-05T11:28:00+01:00</updated><author><name>Derik Ramirez</name></author><content type="html"><![CDATA[<p>If you check the <a href="https://docs.aws.amazon.com/eks/latest/userguide/create-cluster.html">AWS documentation</a>, they use <code>eksctl</code> to create the EKS cluster. <code>eksctl</code> uses CloudFormation, and even if in the end, I could fetch the template, it feels like <code>eksctl</code> is an imperative way of creating an EKS Cluster. I prefer to keep track of all of my infrastructure as code, and using <code>eksctl</code> leaves an essential part of the infrastructure out of the codebase, the cluster itself.</p>
<p>I&rsquo;ll describe how to create a Kubernetes cluster in Amazon EKS using Terraform in this article.</p>
<p>Note: If you are interested in learning more about how to set up the directory structure for your Terraform project, you might find my guide, <a href="https://rderik.com/guides/meditations_directory_structure_tf_projects">Meditations on Directory Structure for Terraform Projects</a>, useful.</p>
<div class="rd-toc">
  <nav id="TableOfContents">
  <ul>
    <li><a href="#create-the-eks-cluster">Create the EKS cluster</a>
      <ul>
        <li><a href="#iam-role">IAM role</a></li>
      </ul>
    </li>
    <li><a href="#vpc-configuration-and-subnets">VPC configuration and subnets</a></li>
    <li><a href="#eks-cluster-security-group">EKS Cluster security group</a></li>
    <li><a href="#creating-the-worker-nodes">Creating the worker nodes</a></li>
    <li><a href="#creating-the-eks-cluster">Creating the EKS cluster:</a></li>
    <li><a href="#configuriong-kubectl-to-connect-to-the-cluster">Configuriong <code>kubectl</code> to connect to the cluster</a></li>
    <li><a href="#final-thoughts">Final thoughts</a></li>
    <li><a href="#references">References</a></li>
  </ul>
</nav>
</div>

<!-- vim-markdown-toc Redcarpet -->
<!-- vim-markdown-toc -->
<h2 id="create-the-eks-cluster">Create the EKS cluster</h2>
<p>The <a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_cluster"><code>eks_cluster</code></a> resource is the one that creates the EKS cluster. It is a simple resource. Its required fields are <code>name</code>, <code>role_arn</code> and <code>vpc_config</code>.</p>
<ul>
<li><code>name</code> is the name of the cluster</li>
<li><code>role_arn</code> is the ARN of the IAM role that the cluster will use</li>
<li><code>vpc_config</code> includes the VPC ID and the subnets that the cluster will use</li>
</ul>
<p>With that information, we should be able to create the cluster. In reality, if we set up the <code>eks_cluster</code> resource, we&rsquo;ll have a Kubernetes cluster, but we still need to set up the infrastructure for the worker nodes. EKS manages the control plane, so once we&rsquo;ve created the EKS cluster, AWS will handle the control plane for us, so we don&rsquo;t have to worry about that. But we are still responsible for setting up the worker nodes. We&rsquo;ll do that later. Let&rsquo;s start by exploring what each of the fields for the <code>eks_cluster</code> requires (except name because naming is the hardest part of programming and is out of this article&rsquo;s scope).</p>
<!-- vim-markdown-toc Redcarpet -->
<!-- vim-markdown-toc -->
<h3 id="iam-role">IAM role</h3>
<p>A Kubernetes cluster needs to run on Nodes. Those nodes are EC2 instances, and they need to have permission to run the Kubernetes components. Those permissions are defined in an IAM role, and that role is attached to the EC2 instances that run the Kubernetes components.</p>
<p>The IAM role should allow the cluster to create those nodes (EC2 instances), create load balancers (Ingresses in k8s), manage auto scaling groups, etcetera. So we need to grant permissions to the EKS cluster to create, modify and update all the resources it needs. The good thing is that Amazon already made a policy we can use that handles the basics, <code>arn:aws:iam::aws:policy/AmazonEKSClusterPolicy</code>. You can see how the policy is defined if you log into your AWS console and search for it in the IAM section or by using the following command using the <code>aws</code> cli:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">aws iam get-policy --policy-arn arn:aws:iam::aws:policy/AmazonEKSClusterPolicy
</span></span></code></pre></td></tr></table>
</div>
</div><p>From there, you&rsquo;ll be able to see the <code>DefaultVersionId</code>. To view the default version of the policy, you can use the following command:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">aws iam get-policy-version --policy-arn arn:aws:iam::aws:policy/AmazonEKSClusterPolicy --version-id v5
</span></span></code></pre></td></tr></table>
</div>
</div><p>Ok, so let&rsquo;s create the IAM role and attach the policy. We can do that using the <a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role"><code>aws_iam_role</code></a> and <a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment"><code>aws_iam_role_policy_attachment</code></a> resources.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-hcl" data-lang="hcl"><span class="line"><span class="cl"><span class="c1"># EKS Cluster IAM Role
</span></span></span><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_iam_role&#34; &#34;cluster&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  name</span> <span class="o">=</span> <span class="s2">&#34;eks-cluster-role&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">  assume_role_policy</span> <span class="o">=</span> <span class="k">data</span><span class="p">.</span><span class="k">aws_iam_policy_document</span><span class="p">.</span><span class="k">eks_assume_role_policy</span><span class="p">.</span><span class="k">json</span>
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">data</span> <span class="s2">&#34;aws_iam_policy_document&#34; &#34;eks_assume_role_policy&#34;</span> {
</span></span><span class="line"><span class="cl">  <span class="k">statement</span> {
</span></span><span class="line"><span class="cl"><span class="n">    actions</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;sts:AssumeRole&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">principals</span> {
</span></span><span class="line"><span class="cl"><span class="n">      type</span>        <span class="o">=</span> <span class="s2">&#34;Service&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      identifiers</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;eks.amazonaws.com&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    }
</span></span><span class="line"><span class="cl">  }
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_iam_role_policy_attachment&#34; &#34;cluster_AmazonEKSClusterPolicy&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  policy_arn</span> <span class="o">=</span> <span class="s2">&#34;arn:aws:iam::aws:policy/AmazonEKSClusterPolicy&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">  role</span>       <span class="o">=</span> <span class="k">aws_iam_role</span><span class="p">.</span><span class="k">cluster</span><span class="p">.</span><span class="k">name</span>
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></td></tr></table>
</div>
</div><p>With the IAM role out of the way, let&rsquo;s now look at the <code>vpc_config</code>.</p>
<!-- vim-markdown-toc Redcarpet -->
<!-- vim-markdown-toc -->
<h2 id="vpc-configuration-and-subnets">VPC configuration and subnets</h2>
<p>The VPC configuration has more moving parts. There are specific requirements that the VPC needs to meet for the cluster to work properly. The requirements are specified in the <a href="https://docs.aws.amazon.com/eks/latest/userguide/network_reqs.html">AWS Documentation - Amazon EKS VPC and subnet requirements and considerations</a>. I won&rsquo;t rewrite what is already explained in the documentation, but there are a few items worth noting:</p>
<ul>
<li>The VPC must have at least two subnets that are in different availability zones</li>
<li>The VPC must have DNS hostname and DNS resolution support.</li>
</ul>
<p>Those are the main ones. Other considerations include having enough IP addresses, etcetera, so check the documentation.</p>
<p>Let&rsquo;s define the VPC and the subnets. We&rsquo;ll use the <a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc"><code>aws_vpc</code></a> and <a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet"><code>aws_subnet</code></a> resources.</p>
<p>I won&rsquo;t go into detail about how to plan the CIDR blocks and how to do your subnets, but I&rsquo;ll show you the schema I&rsquo;ll use:</p>
<pre tabindex="0"><code>VPC CIDR:
10.0.0.0/18
  public_subnets = {
    a = &#34;10.0.0.0/22&#34;
    b = &#34;10.0.4.0/22&#34;
  }

  private_subnets = {
    a = &#34;10.0.8.0/22&#34;
    b = &#34;10.0.12.0/22&#34;
  }
</code></pre><p>If you are looking for a subnet calculator, you can use mine: <a href="https://rdicidr.rderik.com/">https://rdicidr.rderik.com/</a>. The observant reader will notice that I like palindromes, and RDICIDR is one! it stands for RDerik Interactive CIDR.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">  1
</span><span class="lnt">  2
</span><span class="lnt">  3
</span><span class="lnt">  4
</span><span class="lnt">  5
</span><span class="lnt">  6
</span><span class="lnt">  7
</span><span class="lnt">  8
</span><span class="lnt">  9
</span><span class="lnt"> 10
</span><span class="lnt"> 11
</span><span class="lnt"> 12
</span><span class="lnt"> 13
</span><span class="lnt"> 14
</span><span class="lnt"> 15
</span><span class="lnt"> 16
</span><span class="lnt"> 17
</span><span class="lnt"> 18
</span><span class="lnt"> 19
</span><span class="lnt"> 20
</span><span class="lnt"> 21
</span><span class="lnt"> 22
</span><span class="lnt"> 23
</span><span class="lnt"> 24
</span><span class="lnt"> 25
</span><span class="lnt"> 26
</span><span class="lnt"> 27
</span><span class="lnt"> 28
</span><span class="lnt"> 29
</span><span class="lnt"> 30
</span><span class="lnt"> 31
</span><span class="lnt"> 32
</span><span class="lnt"> 33
</span><span class="lnt"> 34
</span><span class="lnt"> 35
</span><span class="lnt"> 36
</span><span class="lnt"> 37
</span><span class="lnt"> 38
</span><span class="lnt"> 39
</span><span class="lnt"> 40
</span><span class="lnt"> 41
</span><span class="lnt"> 42
</span><span class="lnt"> 43
</span><span class="lnt"> 44
</span><span class="lnt"> 45
</span><span class="lnt"> 46
</span><span class="lnt"> 47
</span><span class="lnt"> 48
</span><span class="lnt"> 49
</span><span class="lnt"> 50
</span><span class="lnt"> 51
</span><span class="lnt"> 52
</span><span class="lnt"> 53
</span><span class="lnt"> 54
</span><span class="lnt"> 55
</span><span class="lnt"> 56
</span><span class="lnt"> 57
</span><span class="lnt"> 58
</span><span class="lnt"> 59
</span><span class="lnt"> 60
</span><span class="lnt"> 61
</span><span class="lnt"> 62
</span><span class="lnt"> 63
</span><span class="lnt"> 64
</span><span class="lnt"> 65
</span><span class="lnt"> 66
</span><span class="lnt"> 67
</span><span class="lnt"> 68
</span><span class="lnt"> 69
</span><span class="lnt"> 70
</span><span class="lnt"> 71
</span><span class="lnt"> 72
</span><span class="lnt"> 73
</span><span class="lnt"> 74
</span><span class="lnt"> 75
</span><span class="lnt"> 76
</span><span class="lnt"> 77
</span><span class="lnt"> 78
</span><span class="lnt"> 79
</span><span class="lnt"> 80
</span><span class="lnt"> 81
</span><span class="lnt"> 82
</span><span class="lnt"> 83
</span><span class="lnt"> 84
</span><span class="lnt"> 85
</span><span class="lnt"> 86
</span><span class="lnt"> 87
</span><span class="lnt"> 88
</span><span class="lnt"> 89
</span><span class="lnt"> 90
</span><span class="lnt"> 91
</span><span class="lnt"> 92
</span><span class="lnt"> 93
</span><span class="lnt"> 94
</span><span class="lnt"> 95
</span><span class="lnt"> 96
</span><span class="lnt"> 97
</span><span class="lnt"> 98
</span><span class="lnt"> 99
</span><span class="lnt">100
</span><span class="lnt">101
</span><span class="lnt">102
</span><span class="lnt">103
</span><span class="lnt">104
</span><span class="lnt">105
</span><span class="lnt">106
</span><span class="lnt">107
</span><span class="lnt">108
</span><span class="lnt">109
</span><span class="lnt">110
</span><span class="lnt">111
</span><span class="lnt">112
</span><span class="lnt">113
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-hcl" data-lang="hcl"><span class="line"><span class="cl"><span class="k">data</span> <span class="s2">&#34;aws_region&#34; &#34;current&#34;</span> {}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_vpc&#34; &#34;main&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  cidr_block</span> <span class="o">=</span> <span class="s2">&#34;10.0.0.0/18&#34;</span><span class="c1">
</span></span></span><span class="line"><span class="cl"><span class="c1">
</span></span></span><span class="line"><span class="cl"><span class="c1">  # EKS requirements
</span></span></span><span class="line"><span class="cl"><span class="c1">  # The VPC must have DNS hostname and DNS resolution support. Otherwise, nodes can&#39;t register to your cluster.
</span></span></span><span class="line"><span class="cl"><span class="c1">  # https://docs.aws.amazon.com/eks/latest/userguide/network_reqs.html
</span></span></span><span class="line"><span class="cl"><span class="n">  enable_dns_hostnames</span> <span class="o">=</span> <span class="kt">true</span>
</span></span><span class="line"><span class="cl"><span class="n">  enable_dns_support</span>   <span class="o">=</span> <span class="kt">true</span>
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_internet_gateway&#34; &#34;main&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  vpc_id</span> <span class="o">=</span> <span class="k">aws_vpc</span><span class="p">.</span><span class="k">main</span><span class="p">.</span><span class="k">id</span>
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_default_route_table&#34; &#34;public&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  default_route_table_id</span> <span class="o">=</span> <span class="k">aws_vpc</span><span class="p">.</span><span class="k">main</span><span class="p">.</span><span class="k">default_route_table_id</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">  route</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    {
</span></span><span class="line"><span class="cl"><span class="n">      cidr_block</span> <span class="o">=</span> <span class="s2">&#34;0.0.0.0/0&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      gateway_id</span> <span class="o">=</span> <span class="k">aws_internet_gateway</span><span class="p">.</span><span class="k">main</span><span class="p">.</span><span class="k">id</span><span class="c1">
</span></span></span><span class="line"><span class="cl"><span class="c1">
</span></span></span><span class="line"><span class="cl"><span class="c1">      # these seem to be required due to an AWS provider bug
</span></span></span><span class="line"><span class="cl"><span class="n">      carrier_gateway_id</span>         <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      destination_prefix_list_id</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      egress_only_gateway_id</span>     <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      instance_id</span>                <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      ipv6_cidr_block</span>            <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      local_gateway_id</span>           <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      nat_gateway_id</span>             <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      network_interface_id</span>       <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      transit_gateway_id</span>         <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      vpc_endpoint_id</span>            <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      vpc_endpoint_id</span>            <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      vpc_peering_connection_id</span>  <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    }
</span></span><span class="line"><span class="cl">  <span class="p">]</span>
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_subnet&#34; &#34;public&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  for_each</span> <span class="o">=</span> {
</span></span><span class="line"><span class="cl"><span class="n">    a</span> <span class="o">=</span> <span class="s2">&#34;10.0.0.0/22&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">    b</span> <span class="o">=</span> <span class="s2">&#34;10.0.4.0/22&#34;</span>
</span></span><span class="line"><span class="cl">  }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">  vpc_id</span>                  <span class="o">=</span> <span class="k">aws_vpc</span><span class="p">.</span><span class="k">main</span><span class="p">.</span><span class="k">id</span>
</span></span><span class="line"><span class="cl"><span class="n">  availability_zone</span>       <span class="o">=</span> <span class="s2">&#34;${data.aws_region.current.name}${each.key}&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">  cidr_block</span>              <span class="o">=</span> <span class="k">each</span><span class="p">.</span><span class="k">value</span>
</span></span><span class="line"><span class="cl"><span class="n">  map_public_ip_on_launch</span> <span class="o">=</span> <span class="kt">true</span>
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_route_table_association&#34; &#34;public&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  for_each</span>       <span class="o">=</span> <span class="k">aws_subnet</span><span class="p">.</span><span class="k">public</span>
</span></span><span class="line"><span class="cl"><span class="n">  subnet_id</span>      <span class="o">=</span> <span class="k">each</span><span class="p">.</span><span class="k">value</span><span class="p">.</span><span class="k">id</span>
</span></span><span class="line"><span class="cl"><span class="n">  route_table_id</span> <span class="o">=</span> <span class="k">aws_vpc</span><span class="p">.</span><span class="k">main</span><span class="p">.</span><span class="k">default_route_table_id</span>
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_eip&#34; &#34;main&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  for_each</span> <span class="o">=</span> <span class="k">aws_subnet</span><span class="p">.</span><span class="k">public</span>
</span></span><span class="line"><span class="cl"><span class="n">  vpc</span>      <span class="o">=</span> <span class="kt">true</span>
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_nat_gateway&#34; &#34;main&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  for_each</span>      <span class="o">=</span> <span class="k">aws_subnet</span><span class="p">.</span><span class="k">public</span>
</span></span><span class="line"><span class="cl"><span class="n">  allocation_id</span> <span class="o">=</span> <span class="k">aws_eip</span><span class="p">.</span><span class="k">main</span><span class="p">[</span><span class="k">each</span><span class="p">.</span><span class="k">key</span><span class="p">].</span><span class="k">id</span>
</span></span><span class="line"><span class="cl"><span class="n">  subnet_id</span>     <span class="o">=</span> <span class="k">each</span><span class="p">.</span><span class="k">value</span><span class="p">.</span><span class="k">id</span>
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_subnet&#34; &#34;private&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  for_each</span> <span class="o">=</span> {
</span></span><span class="line"><span class="cl"><span class="n">    a</span> <span class="o">=</span> <span class="s2">&#34;10.0.8.0/22&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">    b</span> <span class="o">=</span> <span class="s2">&#34;10.0.12.0/22&#34;</span>
</span></span><span class="line"><span class="cl">  }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">  vpc_id</span>            <span class="o">=</span> <span class="k">aws_vpc</span><span class="p">.</span><span class="k">main</span><span class="p">.</span><span class="k">id</span>
</span></span><span class="line"><span class="cl"><span class="n">  availability_zone</span> <span class="o">=</span> <span class="s2">&#34;${data.aws_region.current.name}${each.key}&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">  cidr_block</span>        <span class="o">=</span> <span class="k">each</span><span class="p">.</span><span class="k">value</span>
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_route_table&#34; &#34;private&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  for_each</span> <span class="o">=</span> <span class="k">aws_nat_gateway</span><span class="p">.</span><span class="k">main</span>
</span></span><span class="line"><span class="cl"><span class="n">  vpc_id</span>   <span class="o">=</span> <span class="k">aws_vpc</span><span class="p">.</span><span class="k">main</span><span class="p">.</span><span class="k">id</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">  route</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    {
</span></span><span class="line"><span class="cl"><span class="n">      cidr_block</span>     <span class="o">=</span> <span class="s2">&#34;0.0.0.0/0&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      nat_gateway_id</span> <span class="o">=</span> <span class="k">each</span><span class="p">.</span><span class="k">value</span><span class="p">.</span><span class="k">id</span><span class="c1">
</span></span></span><span class="line"><span class="cl"><span class="c1">
</span></span></span><span class="line"><span class="cl"><span class="c1">      # these seem to be required due to an AWS provider bug
</span></span></span><span class="line"><span class="cl"><span class="n">      carrier_gateway_id</span>         <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      destination_prefix_list_id</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      egress_only_gateway_id</span>     <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      gateway_id</span>                 <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      instance_id</span>                <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      ipv6_cidr_block</span>            <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      local_gateway_id</span>           <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      network_interface_id</span>       <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      transit_gateway_id</span>         <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      vpc_endpoint_id</span>            <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      vpc_peering_connection_id</span>  <span class="o">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    }
</span></span><span class="line"><span class="cl">  <span class="p">]</span>
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_route_table_association&#34; &#34;private&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  for_each</span>       <span class="o">=</span> <span class="k">aws_subnet</span><span class="p">.</span><span class="k">private</span>
</span></span><span class="line"><span class="cl"><span class="n">  subnet_id</span>      <span class="o">=</span> <span class="k">each</span><span class="p">.</span><span class="k">value</span><span class="p">.</span><span class="k">id</span>
</span></span><span class="line"><span class="cl"><span class="n">  route_table_id</span> <span class="o">=</span> <span class="k">aws_route_table</span><span class="p">.</span><span class="k">private</span><span class="p">[</span><span class="k">each</span><span class="p">.</span><span class="k">key</span><span class="p">].</span><span class="k">id</span>
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></td></tr></table>
</div>
</div><p>We now have a VPC with two public and two private subnets. The public subnets have a route to the internet through an internet gateway, and the private subnets have a route to the internet through a NAT gateway.</p>
<!-- vim-markdown-toc Redcarpet -->
<!-- vim-markdown-toc -->
<h2 id="eks-cluster-security-group">EKS Cluster security group</h2>
<p>For the VPC configuration, we will need to provide the security groups for the EKS cluster. We&rsquo;ll use the <a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group"><code>aws_security_group</code></a> resource. You will notice that in the following security group definition, we are referencing <code>aws_securitygroup.eks_nodes.id</code>, which hasn&rsquo;t been created yet. We&rsquo;ll create it later when we define the nodes. At the moment, assume that it&rsquo;ll be defined later.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-hcl" data-lang="hcl"><span class="line"><span class="cl"><span class="c1"># EKS Cluster Security Group
</span></span></span><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_security_group&#34; &#34;eks_cluster&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  name</span>        <span class="o">=</span> <span class="s2">&#34;eks-cluster-sg&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">  description</span> <span class="o">=</span> <span class="s2">&#34;Cluster communication with worker nodes&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">  vpc_id</span>      <span class="o">=</span> <span class="k">aws_vpc</span><span class="p">.</span><span class="k">main</span><span class="p">.</span><span class="k">id</span>
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_security_group_rule&#34; &#34;cluster_inbound&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  description</span>              <span class="o">=</span> <span class="s2">&#34;Allow worker nodes to communicate with the cluster API Server&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">  from_port</span>                <span class="o">=</span> <span class="m">443</span>
</span></span><span class="line"><span class="cl"><span class="n">  protocol</span>                 <span class="o">=</span> <span class="s2">&#34;tcp&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">  security_group_id</span>        <span class="o">=</span> <span class="k">aws_security_group</span><span class="p">.</span><span class="k">eks_cluster</span><span class="p">.</span><span class="k">id</span>
</span></span><span class="line"><span class="cl"><span class="n">  source_security_group_id</span> <span class="o">=</span> <span class="k">aws_security_group</span><span class="p">.</span><span class="k">eks_nodes</span><span class="p">.</span><span class="k">id</span>
</span></span><span class="line"><span class="cl"><span class="n">  to_port</span>                  <span class="o">=</span> <span class="m">443</span>
</span></span><span class="line"><span class="cl"><span class="n">  type</span>                     <span class="o">=</span> <span class="s2">&#34;ingress&#34;</span>
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_security_group_rule&#34; &#34;cluster_outbound&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  description</span>              <span class="o">=</span> <span class="s2">&#34;Allow cluster API Server to communicate with the worker nodes&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">  from_port</span>                <span class="o">=</span> <span class="m">1024</span>
</span></span><span class="line"><span class="cl"><span class="n">  protocol</span>                 <span class="o">=</span> <span class="s2">&#34;tcp&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">  security_group_id</span>        <span class="o">=</span> <span class="k">aws_security_group</span><span class="p">.</span><span class="k">eks_cluster</span><span class="p">.</span><span class="k">id</span>
</span></span><span class="line"><span class="cl"><span class="n">  source_security_group_id</span> <span class="o">=</span> <span class="k">aws_security_group</span><span class="p">.</span><span class="k">eks_nodes</span><span class="p">.</span><span class="k">id</span>
</span></span><span class="line"><span class="cl"><span class="n">  to_port</span>                  <span class="o">=</span> <span class="m">65535</span>
</span></span><span class="line"><span class="cl"><span class="n">  type</span>                     <span class="o">=</span> <span class="s2">&#34;egress&#34;</span>
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></td></tr></table>
</div>
</div><p>Now let&rsquo;s look at creating the nodes where the Kubernetes cluster will run.</p>
<!-- vim-markdown-toc Redcarpet -->
<!-- vim-markdown-toc -->
<h2 id="creating-the-worker-nodes">Creating the worker nodes</h2>
<p>We have a few different ways to set up the worker nodes. We could use two approaches:</p>
<ul>
<li>Self-managed nodes - We would need to manage the nodes ourselves using  Auto Scaling Groups and all that it entails.</li>
<li>Managed node groups - &ldquo;Amazon EKS managed node groups automate the provisioning and lifecycle management of nodes (Amazon EC2 instances) for Amazon EKS Kubernetes clusters.&rdquo; <a href="https://docs.aws.amazon.com/eks/latest/userguide/eks-compute.html">Amazon EKS nodes</a></li>
</ul>
<p>We&rsquo;ll use Managed node groups. The reason is that it&rsquo;s easier to manage and the recommended way to do it.</p>
<p>Let&rsquo;s start by creating the IAM role that the nodes will use. We&rsquo;ll call it <code>eks-node-role</code> and attach the <code>AmazonEKSWorkerNodePolicy</code> and <code>AmazonEKS_CNI_Policy</code> policies to it.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-hcl" data-lang="hcl"><span class="line"><span class="cl"><span class="c1"># EKS Node IAM Role
</span></span></span><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_iam_role&#34; &#34;node&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  name</span> <span class="o">=</span> <span class="s2">&#34;eks-node-role&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">  assume_role_policy</span> <span class="o">=</span> <span class="k">data</span><span class="p">.</span><span class="k">aws_iam_policy_document</span><span class="p">.</span><span class="k">ec2_assume_role_policy</span><span class="p">.</span><span class="k">json</span>
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">data</span> <span class="s2">&#34;aws_iam_policy_document&#34; &#34;ec2_assume_role_policy&#34;</span> {
</span></span><span class="line"><span class="cl">  <span class="k">statement</span> {
</span></span><span class="line"><span class="cl"><span class="n">    actions</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;sts:AssumeRole&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">principals</span> {
</span></span><span class="line"><span class="cl"><span class="n">      type</span>        <span class="o">=</span> <span class="s2">&#34;Service&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">      identifiers</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;ec2.amazonaws.com&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    }
</span></span><span class="line"><span class="cl">  }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></td></tr></table>
</div>
</div><p>Now we&rsquo;ll attach the policies to the role.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-hcl" data-lang="hcl"><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_iam_role_policy_attachment&#34; &#34;node_AmazonEKSWorkerNodePolicy&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  policy_arn</span> <span class="o">=</span> <span class="s2">&#34;arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">  role</span>       <span class="o">=</span> <span class="k">aws_iam_role</span><span class="p">.</span><span class="k">node</span><span class="p">.</span><span class="k">name</span>
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_iam_role_policy_attachment&#34; &#34;node_AmazonEKS_CNI_Policy&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  policy_arn</span> <span class="o">=</span> <span class="s2">&#34;arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">  role</span>       <span class="o">=</span> <span class="k">aws_iam_role</span><span class="p">.</span><span class="k">node</span><span class="p">.</span><span class="k">name</span>
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></td></tr></table>
</div>
</div><p>If you plan to use the <a href="https://kubernetes-sigs.github.io/aws-load-balancer-controller/">AWS Load Balancer controller</a>, and you want to directly assign the policy to the nodes, we can do that now:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-hcl" data-lang="hcl"><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_iam_policy&#34; &#34;alb_controller_policy&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  name</span> <span class="o">=</span> <span class="s2">&#34;AlbControllerPolicy&#34;</span><span class="c1">
</span></span></span><span class="line"><span class="cl"><span class="c1">  # We are going to use the ALB controller implementation from the Kubernetes SIGs
</span></span></span><span class="line"><span class="cl"><span class="c1">  # the following policy is needed
</span></span></span><span class="line"><span class="cl"><span class="c1">  # Source: `curl -o alb_controller_policy.json https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.4.4/docs/install/iam_policy.json`
</span></span></span><span class="line"><span class="cl"><span class="n">  policy</span> <span class="o">=</span> <span class="k">file</span><span class="p">(</span><span class="s2">&#34;${path.module}/alb_controller_policy.json&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_iam_role_policy_attachment&#34; &#34;node_alb_controller_policy&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  policy_arn</span> <span class="o">=</span> <span class="k">aws_iam_policy</span><span class="p">.</span><span class="k">alb_controller_policy</span><span class="p">.</span><span class="k">arn</span>
</span></span><span class="line"><span class="cl"><span class="n">  role</span>       <span class="o">=</span> <span class="k">aws_iam_role</span><span class="p">.</span><span class="k">node</span><span class="p">.</span><span class="k">name</span>
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></td></tr></table>
</div>
</div><p>Now we&rsquo;ll create the security group for the nodes. We&rsquo;ll call it <code>eks-node-sg</code>, and we&rsquo;ll allow traffic from the security group of the control plane.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-hcl" data-lang="hcl"><span class="line"><span class="cl"><span class="c1"># EKS Node Security Group
</span></span></span><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_security_group&#34; &#34;eks_nodes&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  name</span>        <span class="o">=</span> <span class="s2">&#34;eks-node-sg&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">  description</span> <span class="o">=</span> <span class="s2">&#34;Security group for all nodes in the cluster&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">  vpc_id</span>      <span class="o">=</span> <span class="k">aws_vpc</span><span class="p">.</span><span class="k">main</span><span class="p">.</span><span class="k">id</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">egress</span> {
</span></span><span class="line"><span class="cl"><span class="n">    from_port</span>   <span class="o">=</span> <span class="m">0</span>
</span></span><span class="line"><span class="cl"><span class="n">    to_port</span>     <span class="o">=</span> <span class="m">0</span>
</span></span><span class="line"><span class="cl"><span class="n">    protocol</span>    <span class="o">=</span> <span class="s2">&#34;-1&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">    cidr_blocks</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;0.0.0.0/0&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  }
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_security_group_rule&#34; &#34;nodes_internal&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  description</span>              <span class="o">=</span> <span class="s2">&#34;Allow nodes to communicate with each other&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">  from_port</span>                <span class="o">=</span> <span class="m">0</span>
</span></span><span class="line"><span class="cl"><span class="n">  protocol</span>                 <span class="o">=</span> <span class="s2">&#34;-1&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">  security_group_id</span>        <span class="o">=</span> <span class="k">aws_security_group</span><span class="p">.</span><span class="k">eks_nodes</span><span class="p">.</span><span class="k">id</span>
</span></span><span class="line"><span class="cl"><span class="n">  source_security_group_id</span> <span class="o">=</span> <span class="k">aws_security_group</span><span class="p">.</span><span class="k">eks_nodes</span><span class="p">.</span><span class="k">id</span>
</span></span><span class="line"><span class="cl"><span class="n">  to_port</span>                  <span class="o">=</span> <span class="m">65535</span>
</span></span><span class="line"><span class="cl"><span class="n">  type</span>                     <span class="o">=</span> <span class="s2">&#34;ingress&#34;</span>
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_security_group_rule&#34; &#34;nodes_cluster_inbound&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  description</span>              <span class="o">=</span> <span class="s2">&#34;Allow worker Kubelets and pods to receive communication from the cluster control plane&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">  from_port</span>                <span class="o">=</span> <span class="m">1025</span>
</span></span><span class="line"><span class="cl"><span class="n">  protocol</span>                 <span class="o">=</span> <span class="s2">&#34;tcp&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">  security_group_id</span>        <span class="o">=</span> <span class="k">aws_security_group</span><span class="p">.</span><span class="k">eks_nodes</span><span class="p">.</span><span class="k">id</span>
</span></span><span class="line"><span class="cl"><span class="n">  source_security_group_id</span> <span class="o">=</span> <span class="k">aws_security_group</span><span class="p">.</span><span class="k">eks_cluster</span><span class="p">.</span><span class="k">id</span>
</span></span><span class="line"><span class="cl"><span class="n">  to_port</span>                  <span class="o">=</span> <span class="m">65535</span>
</span></span><span class="line"><span class="cl"><span class="n">  type</span>                     <span class="o">=</span> <span class="s2">&#34;ingress&#34;</span>
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_security_group_rule&#34; &#34;nodes_cluster_outbound&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  description</span>              <span class="o">=</span> <span class="s2">&#34;Allow worker Kubelets and pods to receive communication from the cluster control plane&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">  from_port</span>                <span class="o">=</span> <span class="m">1025</span>
</span></span><span class="line"><span class="cl"><span class="n">  protocol</span>                 <span class="o">=</span> <span class="s2">&#34;tcp&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">  security_group_id</span>        <span class="o">=</span> <span class="k">aws_security_group</span><span class="p">.</span><span class="k">eks_nodes</span><span class="p">.</span><span class="k">id</span>
</span></span><span class="line"><span class="cl"><span class="n">  source_security_group_id</span> <span class="o">=</span> <span class="k">aws_security_group</span><span class="p">.</span><span class="k">eks_cluster</span><span class="p">.</span><span class="k">id</span>
</span></span><span class="line"><span class="cl"><span class="n">  to_port</span>                  <span class="o">=</span> <span class="m">65535</span>
</span></span><span class="line"><span class="cl"><span class="n">  type</span>                     <span class="o">=</span> <span class="s2">&#34;egress&#34;</span>
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></td></tr></table>
</div>
</div><p>And we finally have everything to create our EKS cluster.</p>
<!-- vim-markdown-toc Redcarpet -->
<!-- vim-markdown-toc -->
<h2 id="creating-the-eks-cluster">Creating the EKS cluster:</h2>
<p>We&rsquo;ll create the cluster using the <a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_cluster"><code>eks_cluster</code></a> resource. We&rsquo;ll call it <code>eks-cluster</code> and use everything we created before. But we also want to log events in CloudWatch for the cluster, so let&rsquo;s create the log group first:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-hcl" data-lang="hcl"><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_cloudwatch_log_group&#34; &#34;cluster&#34;</span> {<span class="c1">
</span></span></span><span class="line"><span class="cl"><span class="c1">  # Reference: https://docs.aws.amazon.com/eks/latest/userguide/control-plane-logs.html
</span></span></span><span class="line"><span class="cl"><span class="n">  name</span>              <span class="o">=</span> <span class="s2">&#34;/eks/eks-cluster/cluster&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">  retention_in_days</span> <span class="o">=</span> <span class="m">0</span>
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></td></tr></table>
</div>
</div><p>Ok, now we are ready to put everything together and set up the EKS cluster:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-hcl" data-lang="hcl"><span class="line"><span class="cl"><span class="c1"># EKS Cluster
</span></span></span><span class="line"><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_eks_cluster&#34; &#34;main&#34;</span> {
</span></span><span class="line"><span class="cl"><span class="n">  name</span> <span class="o">=</span> <span class="s2">&#34;eks-cluster&#34;</span><span class="c1">
</span></span></span><span class="line"><span class="cl"><span class="c1">
</span></span></span><span class="line"><span class="cl"><span class="c1">  # reference for log_types:
</span></span></span><span class="line"><span class="cl"><span class="c1">  # https://docs.aws.amazon.com/eks/latest/userguide/control-plane-logs.html
</span></span></span><span class="line"><span class="cl"><span class="n">  enabled_cluster_log_types</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;api&#34;, &#34;audit&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">  role_arn</span> <span class="o">=</span> <span class="k">aws_iam_role</span><span class="p">.</span><span class="k">cluster</span><span class="p">.</span><span class="k">arn</span><span class="c1">
</span></span></span><span class="line"><span class="cl"><span class="c1">
</span></span></span><span class="line"><span class="cl"><span class="c1">  # reference for EKS versions:
</span></span></span><span class="line"><span class="cl"><span class="c1">  # https://docs.aws.amazon.com/eks/latest/userguide/kubernetes-versions.html
</span></span></span><span class="line"><span class="cl"><span class="n">  version</span> <span class="o">=</span> <span class="s2">&#34;1.23&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">vpc_config</span> {<span class="c1">
</span></span></span><span class="line"><span class="cl"><span class="c1">    # Security Groups considerations reference:
</span></span></span><span class="line"><span class="cl"><span class="c1">    # https://docs.aws.amazon.com/eks/latest/userguide/sec-group-reqs.html
</span></span></span><span class="line"><span class="cl"><span class="n">    security_group_ids</span>      <span class="o">=</span> <span class="p">[</span><span class="k">aws_security_group</span><span class="p">.</span><span class="k">eks_cluster</span><span class="p">.</span><span class="k">id</span><span class="p">,</span> <span class="k">aws_security_group</span><span class="p">.</span><span class="k">eks_nodes</span><span class="p">.</span><span class="k">id</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="n">    subnet_ids</span>              <span class="o">=</span> <span class="k">concat</span><span class="p">(</span><span class="k">aws_subnet</span><span class="p">.</span><span class="k">public</span><span class="p">.</span><span class="err">*</span><span class="p">.</span><span class="k">id</span><span class="p">,</span> <span class="k">aws_subnet</span><span class="p">.</span><span class="k">private</span><span class="p">.</span><span class="err">*</span><span class="p">.</span><span class="k">id</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">    endpoint_private_access</span> <span class="o">=</span> <span class="kt">true</span>
</span></span><span class="line"><span class="cl"><span class="n">    endpoint_public_access</span>  <span class="o">=</span> <span class="kt">true</span>
</span></span><span class="line"><span class="cl"><span class="n">    public_access_cidrs</span>     <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;0.0.0.0/0&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">  }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">  depends_on</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="k">aws_iam_role_policy_attachment</span><span class="p">.</span><span class="k">cluster_AmazonEKSClusterPolicy</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="k">aws_cloudwatch_log_group</span><span class="p">.</span><span class="k">cluster</span>
</span></span><span class="line"><span class="cl">  <span class="p">]</span>
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></td></tr></table>
</div>
</div><p>Now we can happily <code>terraform apply</code>, and we&rsquo;ll have our EKS cluster up and running.</p>
<!-- vim-markdown-toc Redcarpet -->
<!-- vim-markdown-toc -->
<h2 id="configuriong-kubectl-to-connect-to-the-cluster">Configuriong <code>kubectl</code> to connect to the cluster</h2>
<p>Now that we have our cluster up and running, we need to configure <code>kubectl</code> to connect to it. We can do that by running the following command:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">aws eks --region us-east-1 update-kubeconfig --name eks-cluster
</span></span></code></pre></td></tr></table>
</div>
</div><p>Modify the region and cluster name if you choose different ones.</p>
<!-- vim-markdown-toc Redcarpet -->
<!-- vim-markdown-toc -->
<h2 id="final-thoughts">Final thoughts</h2>
<p>Using Terraform to create the EKS cluster seems more convoluted than just typing a couple of lines with <code>eksctl</code>, but it has the benefit of describing what was deployed and having it as part of our Infrastructure as Code.</p>
<p>I hope this article was helpful and shed some light on how to set up your initial EKS cluster.</p>
<!-- vim-markdown-toc Redcarpet -->
<!-- vim-markdown-toc -->
<h2 id="references">References</h2>
<ul>
<li><a href="https://docs.aws.amazon.com/eks/latest/userguide/network_reqs.html">AWS Documentation - Amazon EKS VPC and subnet requirements and considerations</a></li>
<li><a href="https://kubernetes-sigs.github.io/aws-load-balancer-controller/">AWS Load Balancer controller</a></li>
<li><a href="https://docs.aws.amazon.com/eks/latest/userguide/create-cluster.html">AWS documentation - EKS create cluster</a></li>
<li><a href="https://docs.aws.amazon.com/eks/latest/userguide/eks-compute.html">Amazon - EKS nodes</a></li>
<li><a href="https://rdicidr.rderik.com/">CIDR Calculator - https://rdicidr.rderik.com/</a></li>
<li><a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc">Terraform resoruce -<code>aws_vpc</code></a></li>
<li><a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role">Terraform resource - <code>aws_iam_role</code></a> and <a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment"><code>aws_iam_role_policy_attachment</code></a></li>
<li><a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group">Terraform resource - <code>aws_security_group</code></a></li>
<li><a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_cluster">Terraform resource - <code>eks_cluster</code></a></li>
<li><a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_cluster">Terraform resource - <code>eks_cluster</code></a></li>
<li><a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet">Terraform resource -<code>aws_subnet</code></a></li>
</ul>
]]></content></entry></feed>