<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>AWS SDK on Digi Hunch</title><link>https://www.digihunch.com/tag/aws-sdk/</link><description>Recent content in AWS SDK on Digi Hunch</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><lastBuildDate>Wed, 02 Apr 2025 10:39:45 -0400</lastBuildDate><atom:link href="https://www.digihunch.com/tag/aws-sdk/index.xml" rel="self" type="application/rss+xml"/><item><title>IAM Roles for any workload</title><link>https://www.digihunch.com/2024/07/iam-roles-for-any-workload/</link><pubDate>Sun, 14 Jul 2024 23:53:49 -0400</pubDate><guid>https://www.digihunch.com/2024/07/iam-roles-for-any-workload/</guid><description>&lt;img src="https://www.digihunch.com/wp-content/uploads/2025/04/feature-iam-role-anywhere.webp" alt="Featured image of post IAM Roles for any workload" /&gt;&lt;h2 class="wp-block-heading"&gt;Background&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A few month back a client of mine wanted to use GitLab pipeline to deploy infrastructure on AWS with Terraform. The key question is how to authenticate the Terraform process running in the pipeline to AWS with temporary credential. Having &lt;a href="https://www.digihunch.com/2023/01/github-action-gotchas/"&gt;worked it out on GitHub&lt;/a&gt;, my proposal at time was to &lt;a href="https://docs.gitlab.com/ee/ci/cloud_services/aws/"&gt;add OIDC provider&lt;/a&gt; to represent the GitLab runner. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;After a few months, they told me that they are self-hosting their GitLab instance. The idea above was based on exposing an identity provider document on the public Internet, which the client is unable to do. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Now, I have an idea: IAM Roles Anywhere.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;IAM Role Introduction&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Many 101 tutorials asks beginners to create standalone IAM users (or group) with IAM policies directly attached. For programatic access they also include creating a pair of access key and secret access key and pass them along to an external application. The keys are long term credentials, and worse, never expires. The leakage of these long-term credentials had been such a headache that AWS strongly discourage the use of long term credentials. You can feel the discouragement when trying to create an access key through the web console, or by the banners on top of the &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html"&gt;documentation page&lt;/a&gt; about how to do so. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The recommendation is use temporary security credentials. In the context of AWS that means IAM roles. The users must assume an IAM role by issuing an API call, and the Security Token Service (STS) grants temporary credential in response.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="351px" viewBox="-0.5 -0.5 351 122" style="max-width:100%;max-height:122px;"&gt;&lt;defs&gt;&lt;/defs&gt;&lt;g&gt;&lt;g data-cell-id="0"&gt;&lt;g data-cell-id="1"&gt;&lt;g data-cell-id="VRZMaZhQ_cbes_kdLvWi-8"&gt;&lt;g&gt;&lt;rect x="0" y="0" width="350" height="120" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"&gt;&lt;/rect&gt;&lt;/g&gt;&lt;/g&gt;&lt;g data-cell-id="VRZMaZhQ_cbes_kdLvWi-1"&gt;&lt;g&gt;&lt;path d="M 320 76.55 C 320 78.52 318.4 80 316.39 80 L 275.69 80 C 273.24 80 272 78.23 272 76.73 L 272 23.42 C 272 21.21 273.91 20 275.64 20 L 316.61 20 C 318.37 20 320 21.41 320 23.36 Z" fill="#759c3e" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 295.89 56.07 C 287 56.07 278.95 49.27 278.95 39.5 C 278.95 30.98 286.32 23.48 296.19 23.48 C 304.93 23.48 313.05 30.44 313.05 39.71 C 313.05 48.35 305.84 56.07 295.89 56.07 Z" fill="#4f4f4f" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 290.59 67.19 L 289.18 67.19 C 289.18 66.44 288.94 65.57 287.89 65.1 C 287.29 64.88 286.32 64.7 285.21 65.03 C 284.66 65.21 284.16 65.6 284.03 66.33 C 283.83 67.41 284.66 67.82 285.47 68.06 C 287.2 68.52 287.94 68.57 288.78 68.88 C 289.71 69.2 290.7 69.74 291 70.92 C 291.25 72.24 290.73 73.18 290.02 73.74 C 289.29 74.34 288.02 74.8 286.34 74.74 C 284.99 74.68 283.8 74.23 283.11 73.54 C 282.51 72.94 282.12 72.18 282.12 71.01 L 283.51 71.01 C 283.56 72.22 284.17 72.91 285.07 73.32 C 286.09 73.71 287.44 73.72 288.38 73.32 C 288.99 73.06 289.55 72.54 289.54 71.68 C 289.52 70.86 288.97 70.36 287.76 70.05 C 286.65 69.75 285.55 69.68 284.52 69.32 C 283.44 68.93 282.23 68.21 282.53 66.33 C 282.71 65.16 283.64 64.33 285.11 63.98 C 286.46 63.7 287.91 63.83 288.89 64.34 C 289.87 64.87 290.54 65.81 290.59 67.19 Z M 300.45 65.04 L 296.82 65.04 L 296.82 74.38 L 295.34 74.38 L 295.34 65.04 L 291.64 65.04 L 291.64 63.82 L 300.45 63.82 Z M 309.41 67.19 L 308 67.19 C 308 66.44 307.76 65.55 306.71 65.07 C 306.12 64.85 305.15 64.67 304.03 65.01 C 303.48 65.19 302.98 65.57 302.85 66.3 C 302.65 67.4 303.48 67.8 304.3 68.04 C 306.02 68.51 306.75 68.54 307.61 68.85 C 308.53 69.17 309.52 69.71 309.8 70.89 C 310.07 72.22 309.55 73.15 308.85 73.72 C 308.11 74.31 306.84 74.77 305.16 74.71 C 303.81 74.67 302.62 74.2 301.93 73.51 C 301.33 72.93 300.94 72.18 300.94 71.01 L 302.34 71.01 C 302.38 72.22 303 72.88 303.89 73.29 C 304.91 73.69 306.26 73.71 307.2 73.3 C 307.81 73.05 308.38 72.52 308.36 71.65 C 308.35 70.84 307.78 70.35 306.59 70.03 C 305.47 69.72 304.38 69.65 303.34 69.29 C 302.26 68.91 301.05 68.18 301.35 66.32 C 301.54 65.13 302.46 64.31 303.94 63.95 C 305.29 63.67 306.73 63.82 307.7 64.33 C 308.69 64.85 309.36 65.81 309.41 67.19 Z M 298.93 42.57 L 298.93 29.55 L 295.42 29.55 L 295.42 39.21 L 288.94 39.21 L 288.94 42.57 Z M 295.95 52.7 C 288.61 52.7 282.46 46.9 282.46 39.65 C 282.46 32.29 288.96 26.83 296.13 26.83 C 303.15 26.83 309.52 32.51 309.52 39.77 C 309.52 47.09 303.15 52.7 295.95 52.7 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;g data-cell-id="VRZMaZhQ_cbes_kdLvWi-2"&gt;&lt;g&gt;&lt;path d="M 56.16 79.58 L 48.37 75.17 L 43 78.24 L 30 70.84 L 30 56.08 L 37.43 51.83 L 37.43 32.71 L 59.71 20 L 82 32.69 L 82 58.05 L 64.07 68.25 L 64.07 75.04 Z" fill="#444444" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 59.71 44.44 L 79.65 33.11 L 59.79 21.87 L 39.94 33.11 Z M 42.35 63.72 L 31.21 57.4 L 31.21 70.11 L 42.35 76.5 Z M 42.92 62.75 L 54.06 56.35 L 42.92 50.03 L 31.78 56.35 Z M 54.7 62.3 L 54.7 57.48 L 43.56 63.79 L 43.56 76.5 L 48.17 73.87 L 48.17 65.98 Z M 58.9 63.12 L 58.9 45.9 L 39.04 34.57 L 39.04 50.92 L 42.99 48.66 L 55.91 56.02 L 55.91 61.62 L 56.08 61.54 Z M 64.06 66.51 L 80.46 57.16 L 80.46 34.49 L 60.6 45.82 L 60.6 64.07 L 64.06 66.03 Z M 55.92 69.87 L 62.05 66.39 L 55.99 62.9 L 49.93 66.39 Z M 55.51 77.8 L 55.51 70.84 L 49.37 67.35 L 49.37 74.32 Z M 56.63 77.8 L 62.78 74.32 L 62.78 67.35 L 56.63 70.84 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;/g&gt;&lt;g&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 87px; margin-left: 56px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;"&gt;CLI&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="56" y="99" fill="rgb(0, 0, 0)" font-family="&amp;quot;Helvetica&amp;quot;" font-size="12px" text-anchor="middle"&gt;CLI&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g data-cell-id="VRZMaZhQ_cbes_kdLvWi-4"&gt;&lt;g&gt;&lt;path d="M 82 32.81 L 263.63 32.99" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 268.88 33 L 261.88 36.49 L 263.63 32.99 L 261.89 29.49 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;/g&gt;&lt;g data-cell-id="VRZMaZhQ_cbes_kdLvWi-6"&gt;&lt;g&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 20px; margin-left: 163px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;Request: AssumeRole*&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="163" y="23" fill="rgb(0, 0, 0)" font-family="&amp;quot;Helvetica&amp;quot;" font-size="11px" text-anchor="middle"&gt;Request: AssumeRole*&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g data-cell-id="VRZMaZhQ_cbes_kdLvWi-5"&gt;&lt;g&gt;&lt;path d="M 270 60 L 88.37 57.91" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 83.12 57.85 L 90.16 54.43 L 88.37 57.91 L 90.08 61.43 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;/g&gt;&lt;g data-cell-id="VRZMaZhQ_cbes_kdLvWi-7"&gt;&lt;g&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 90px; margin-left: 122px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;Response:&lt;div&gt;&amp;#8211; AccessKeyId&lt;/div&gt;&lt;div&gt;&amp;#8211; SecretAccessKey&lt;/div&gt;&lt;div&gt;&amp;#8211; SessionToken&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="122" y="94" fill="rgb(0, 0, 0)" font-family="&amp;quot;Helvetica&amp;quot;" font-size="11px"&gt;Response:&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;switch&gt;&lt;g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;/g&gt;&lt;a transform="translate(0,-5)" xlink:href="https://www.drawio.com/doc/faq/svg-export-text-problems" target="_blank" rel="noopener"&gt;&lt;text text-anchor="middle" font-size="10px" x="50%" y="100%"&gt;Text is not SVG &amp;#8211; cannot display&lt;/text&gt;&lt;/a&gt;&lt;/switch&gt;&lt;/svg&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This diagram has several variations. For example, the request can be AssumeRole, AssumeRoleWithSAML and AssumeRoleWithWebIdentity, depending on whether and how the user info is federated with external identity store. The returned response, a triplet of three values, makes the temporary credential that we should use in any secure environment. They must be renewed before expiry. This model works not only for human identity (e.g. SAML integration, OIDC integration, cross-account access) but also for workload identity (e.g. EC2 instance profile, Lambda execution role, ECS task role, etc). &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Another good example is &lt;a href="https://www.digihunch.com/2024/01/workload-identity-on-kubernetes-2-of-2-eks-and-rosa-on-aws/"&gt;IAM Role for Service Account (IRSA)&lt;/a&gt;, where a web identity represents a Kuberentes Service Account to gain role credential using the AssumeRoleWithWebIdentity API. In this post however, I&amp;#8217;d like to explore more about the IAM role for EC2 instance profile.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;The IMDS service &lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For EC2 instance, we all know that we can associate an IAM role as the instance profile and grant the process using AWS SDK running on the instance with permissions associated with the IAM role. At a lower level, this relies on the IMDS (Instance Metadata Service) running on the instance.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If an EC2 instance&amp;#8217;s profile points to an IAM role, a process running on the instance using AWS SDK will also need to get the triplet from the STS. It is summarized in this diagram:&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="531px" viewBox="-0.5 -0.5 531 141" style="max-width:100%;max-height:141px;"&gt;&lt;defs&gt;&lt;/defs&gt;&lt;g&gt;&lt;g data-cell-id="0"&gt;&lt;g data-cell-id="1"&gt;&lt;g data-cell-id="mtzehsWG86ey03g9Zuuk-1"&gt;&lt;g&gt;&lt;rect x="0" y="0" width="530" height="140" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"&gt;&lt;/rect&gt;&lt;/g&gt;&lt;/g&gt;&lt;g data-cell-id="mtzehsWG86ey03g9Zuuk-2"&gt;&lt;g&gt;&lt;rect x="10" y="10" width="330" height="100" fill="#d5e8d4" stroke="#82b366" pointer-events="all"&gt;&lt;/rect&gt;&lt;/g&gt;&lt;/g&gt;&lt;g data-cell-id="mtzehsWG86ey03g9Zuuk-3"&gt;&lt;g&gt;&lt;path d="M 518 101.55 C 518 103.52 516.4 105 514.39 105 L 473.69 105 C 471.24 105 470 103.23 470 101.73 L 470 48.42 C 470 46.21 471.91 45 473.64 45 L 514.61 45 C 516.37 45 518 46.41 518 48.36 Z" fill="#759c3e" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 493.89 81.07 C 485 81.07 476.95 74.27 476.95 64.5 C 476.95 55.98 484.32 48.48 494.19 48.48 C 502.93 48.48 511.05 55.44 511.05 64.71 C 511.05 73.35 503.84 81.07 493.89 81.07 Z" fill="#4f4f4f" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 488.59 92.19 L 487.18 92.19 C 487.18 91.44 486.94 90.57 485.89 90.1 C 485.29 89.88 484.32 89.7 483.21 90.03 C 482.66 90.21 482.16 90.6 482.03 91.33 C 481.83 92.41 482.66 92.82 483.47 93.06 C 485.2 93.52 485.94 93.57 486.78 93.88 C 487.71 94.2 488.7 94.74 489 95.92 C 489.25 97.24 488.73 98.18 488.02 98.74 C 487.29 99.34 486.02 99.8 484.34 99.74 C 482.99 99.68 481.8 99.23 481.11 98.54 C 480.51 97.94 480.12 97.18 480.12 96.01 L 481.51 96.01 C 481.56 97.22 482.17 97.91 483.07 98.32 C 484.09 98.71 485.44 98.72 486.38 98.32 C 486.99 98.06 487.55 97.54 487.54 96.68 C 487.52 95.86 486.97 95.36 485.76 95.05 C 484.65 94.75 483.55 94.68 482.52 94.32 C 481.44 93.93 480.23 93.21 480.53 91.33 C 480.71 90.16 481.64 89.33 483.11 88.98 C 484.46 88.7 485.91 88.83 486.89 89.34 C 487.87 89.87 488.54 90.81 488.59 92.19 Z M 498.45 90.04 L 494.82 90.04 L 494.82 99.38 L 493.34 99.38 L 493.34 90.04 L 489.64 90.04 L 489.64 88.82 L 498.45 88.82 Z M 507.41 92.19 L 506 92.19 C 506 91.44 505.76 90.55 504.71 90.07 C 504.12 89.85 503.15 89.67 502.03 90.01 C 501.48 90.19 500.98 90.57 500.85 91.3 C 500.65 92.4 501.48 92.8 502.3 93.04 C 504.02 93.51 504.75 93.54 505.61 93.85 C 506.53 94.17 507.52 94.71 507.8 95.89 C 508.07 97.22 507.55 98.15 506.85 98.72 C 506.11 99.31 504.84 99.77 503.16 99.71 C 501.81 99.67 500.62 99.2 499.93 98.51 C 499.33 97.93 498.94 97.18 498.94 96.01 L 500.34 96.01 C 500.38 97.22 501 97.88 501.89 98.29 C 502.91 98.69 504.26 98.71 505.2 98.3 C 505.81 98.05 506.38 97.52 506.36 96.65 C 506.35 95.84 505.78 95.35 504.59 95.03 C 503.47 94.72 502.38 94.65 501.34 94.29 C 500.26 93.91 499.05 93.18 499.35 91.32 C 499.54 90.13 500.46 89.31 501.94 88.95 C 503.29 88.67 504.73 88.82 505.7 89.33 C 506.69 89.85 507.36 90.81 507.41 92.19 Z M 496.93 67.57 L 496.93 54.55 L 493.42 54.55 L 493.42 64.21 L 486.94 64.21 L 486.94 67.57 Z M 493.95 77.7 C 486.61 77.7 480.46 71.9 480.46 64.65 C 480.46 57.29 486.96 51.83 494.13 51.83 C 501.15 51.83 507.52 57.51 507.52 64.77 C 507.52 72.09 501.15 77.7 493.95 77.7 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;g data-cell-id="mtzehsWG86ey03g9Zuuk-4"&gt;&lt;g&gt;&lt;rect x="212.5" y="60" width="105" height="35" rx="5.25" ry="5.25" fill="#ffe6cc" stroke="#d79b00" pointer-events="all"&gt;&lt;/rect&gt;&lt;/g&gt;&lt;g&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 103px; height: 1px; padding-top: 78px; margin-left: 214px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;IMDS v2&lt;div&gt;169.254.169.254&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="265" y="81" fill="rgb(0, 0, 0)" font-family="&amp;quot;Helvetica&amp;quot;" font-size="12px" text-anchor="middle"&gt;IMDS v2&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g data-cell-id="mtzehsWG86ey03g9Zuuk-5"&gt;&lt;g&gt;&lt;rect x="30" y="62.5" width="120" height="30" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"&gt;&lt;/rect&gt;&lt;/g&gt;&lt;g&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 78px; margin-left: 31px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;AWS SDK&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="90" y="81" fill="rgb(0, 0, 0)" font-family="&amp;quot;Helvetica&amp;quot;" font-size="12px" text-anchor="middle"&gt;AWS SDK&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g data-cell-id="mtzehsWG86ey03g9Zuuk-6"&gt;&lt;g&gt;&lt;path d="M 149.88 74.26 L 206.76 75.35" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 212.01 75.45 L 204.95 78.81 L 206.76 75.35 L 205.08 71.82 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;g data-cell-id="mtzehsWG86ey03g9Zuuk-7"&gt;&lt;g&gt;&lt;path d="M 211.87 84.5 L 156.37 84.95" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 151.12 84.99 L 158.09 81.43 L 156.37 84.95 L 158.15 88.43 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;g data-cell-id="mtzehsWG86ey03g9Zuuk-8"&gt;&lt;g&gt;&lt;path d="M 317.29 70.78 L 463.63 70.03" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 468.88 70.01 L 461.9 73.54 L 463.63 70.03 L 461.86 66.54 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;/g&gt;&lt;g data-cell-id="mtzehsWG86ey03g9Zuuk-9"&gt;&lt;g&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 58px; margin-left: 398px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;Request: AssumeRole&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="398" y="61" fill="rgb(0, 0, 0)" font-family="&amp;quot;Helvetica&amp;quot;" font-size="11px" text-anchor="middle"&gt;Request: AssumeRole&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g data-cell-id="mtzehsWG86ey03g9Zuuk-10"&gt;&lt;g&gt;&lt;path d="M 470 80 L 322.29 81.53" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 317.04 81.58 L 324.01 78.01 L 322.29 81.53 L 324.08 85.01 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;/g&gt;&lt;g data-cell-id="mtzehsWG86ey03g9Zuuk-11"&gt;&lt;g&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 112px; margin-left: 352px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;Response:&lt;div&gt;&amp;#8211; AccessKeyId&lt;/div&gt;&lt;div&gt;&amp;#8211; SecretAccessKey&lt;/div&gt;&lt;div&gt;&amp;#8211; SessionToken&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="352" y="115" fill="rgb(0, 0, 0)" font-family="&amp;quot;Helvetica&amp;quot;" font-size="11px"&gt;Response:&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g data-cell-id="mtzehsWG86ey03g9Zuuk-12"&gt;&lt;g&gt;&lt;rect x="30" y="25" width="120" height="37.5" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 42 25 L 42 62.5 M 138 25 L 138 62.5" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;/g&gt;&lt;g&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 94px; height: 1px; padding-top: 44px; margin-left: 43px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Application&lt;div&gt;Process&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="90" y="47" fill="rgb(0, 0, 0)" font-family="&amp;quot;Helvetica&amp;quot;" font-size="12px" text-anchor="middle"&gt;Application&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g data-cell-id="mtzehsWG86ey03g9Zuuk-13"&gt;&lt;g&gt;&lt;path d="M 180 20 L 210 20 L 210 50 L 180 50 Z" fill="#ed7100" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 189.43 40.57 L 200.14 40.57 L 200.14 29.86 L 189.43 29.86 Z M 201 29.86 L 202.71 29.86 L 202.71 30.71 L 201 30.71 L 201 32.43 L 202.71 32.43 L 202.71 33.29 L 201 33.29 L 201 34.57 L 202.71 34.57 L 202.71 35.43 L 201 35.43 L 201 37.14 L 202.71 37.14 L 202.71 38 L 201 38 L 201 39.71 L 202.71 39.71 L 202.71 40.57 L 201 40.57 L 201 40.63 C 201 41.07 200.64 41.43 200.2 41.43 L 200.14 41.43 L 200.14 43.14 L 199.29 43.14 L 199.29 41.43 L 197.57 41.43 L 197.57 43.14 L 196.71 43.14 L 196.71 41.43 L 195.43 41.43 L 195.43 43.14 L 194.57 43.14 L 194.57 41.43 L 192.86 41.43 L 192.86 43.14 L 192 43.14 L 192 41.43 L 190.29 41.43 L 190.29 43.14 L 189.43 43.14 L 189.43 41.43 L 189.37 41.43 C 188.93 41.43 188.57 41.07 188.57 40.63 L 188.57 40.57 L 187.29 40.57 L 187.29 39.71 L 188.57 39.71 L 188.57 38 L 187.29 38 L 187.29 37.14 L 188.57 37.14 L 188.57 35.43 L 187.29 35.43 L 187.29 34.57 L 188.57 34.57 L 188.57 33.29 L 187.29 33.29 L 187.29 32.43 L 188.57 32.43 L 188.57 30.71 L 187.29 30.71 L 187.29 29.86 L 188.57 29.86 L 188.57 29.8 C 188.57 29.36 188.93 29 189.37 29 L 189.43 29 L 189.43 27.29 L 190.29 27.29 L 190.29 29 L 192 29 L 192 27.29 L 192.86 27.29 L 192.86 29 L 194.57 29 L 194.57 27.29 L 195.43 27.29 L 195.43 29 L 196.71 29 L 196.71 27.29 L 197.57 27.29 L 197.57 29 L 199.29 29 L 199.29 27.29 L 200.14 27.29 L 200.14 29 L 200.2 29 C 200.64 29 201 29.36 201 29.8 Z M 195.43 46.09 C 195.43 46.12 195.4 46.14 195.38 46.14 L 183.91 46.14 C 183.88 46.14 183.86 46.12 183.86 46.09 L 183.86 34.62 C 183.86 34.6 183.88 34.57 183.91 34.57 L 186.43 34.57 L 186.43 33.71 L 183.91 33.71 C 183.41 33.71 183 34.12 183 34.62 L 183 46.09 C 183 46.59 183.41 47 183.91 47 L 195.38 47 C 195.88 47 196.29 46.59 196.29 46.09 L 196.29 44 L 195.43 44 Z M 207 23.91 L 207 35.38 C 207 35.88 206.59 36.29 206.09 36.29 L 203.57 36.29 L 203.57 35.43 L 206.09 35.43 C 206.12 35.43 206.14 35.4 206.14 35.38 L 206.14 23.91 C 206.14 23.88 206.12 23.86 206.09 23.86 L 194.62 23.86 C 194.6 23.86 194.57 23.88 194.57 23.91 L 194.57 26.43 L 193.71 26.43 L 193.71 23.91 C 193.71 23.41 194.12 23 194.62 23 L 206.09 23 C 206.59 23 207 23.41 207 23.91 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;/g&gt;&lt;g&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 35px; margin-left: 212px;"&gt;&lt;div data-drawio-colors="color: #232F3E; " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(35, 47, 62); line-height: 1.2; pointer-events: all; white-space: nowrap;"&gt;&lt;div&gt;&lt;span style="background-color: initial;"&gt;EC2 Instance&lt;/span&gt;&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="212" y="39" fill="#232F3E" font-family="&amp;quot;Helvetica&amp;quot;" font-size="12px"&gt;EC2 I&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;switch&gt;&lt;g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;/g&gt;&lt;a transform="translate(0,-5)" xlink:href="https://www.drawio.com/doc/faq/svg-export-text-problems" target="_blank" rel="noopener"&gt;&lt;text text-anchor="middle" font-size="10px" x="50%" y="100%"&gt;Text is not SVG &amp;#8211; cannot display&lt;/text&gt;&lt;/a&gt;&lt;/switch&gt;&lt;/svg&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The IMDS is a service available on one of the link-local IP address (169.254.169.254) on the EC2 instance. Requests made to this IP address are not routed elsewhere. The Instance Metadata Service (IMDS) is a means for the cloud service provider&amp;#8217;s virtualization layer to share information with the processes on the operating system of a virtual machine. It responds with information related to the instance itself, such as the subnets, IAM role, instance ID, AMI ID, security group. The instance metadata also includes user data script for cloud init process to consume, and most relevantly, the role credential for the instance. This also requires that the IMDS service to have connectivity to the STS endpoint, either via interface endpoint or over the Internet. The AssumeRole calls are logged in CloudTrail.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;All major cloud vendors (AWS, Azure and GCP) uses the IMDS mechanism, and this mechanism obviously draws the attention of bad actors. I find some good articles on this &lt;a href="https://www.sans.org/blog/cloud-instance-metadata-services-imds-/"&gt;here&lt;/a&gt; and &lt;a href="https://www.tenable.com/blog/secure-your-aws-ec2-instance-metadata-service-imds"&gt;here&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;IAM Role for EC2 Workload&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For EC2 instances at AWS, the initial IMDS v1 was introduced in 2012 and allows a GET method to fetch instance metadata. The IMDS v1 is subject to attacks such as SSRF (Server-side request forgery). In 2019 AWS introduced IMDS v2 which tackles those &lt;a href="https://aws.amazon.com/blogs/security/defense-in-depth-open-firewalls-reverse-proxies-ssrf-vulnerabilities-ec2-instance-metadata-service/"&gt;vulnerabilities&lt;/a&gt;. As of date, the recommendation is to use IMDSv2. Here is an example of how to fetch instance metadata, including the credential:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Grab a token&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;TOKEN&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;curl -X PUT &lt;span style="color:#e6db74"&gt;&amp;#34;http://169.254.169.254/latest/api/token&amp;#34;&lt;/span&gt; -H &lt;span style="color:#e6db74"&gt;&amp;#34;X-aws-ec2-metadata-token-ttl-seconds: 21600&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;`&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Get top-level instance metadata information&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl http://169.254.169.254/latest/meta-data/profile -H &lt;span style="color:#e6db74"&gt;&amp;#34;X-aws-ec2-metadata-token: &lt;/span&gt;$TOKEN&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Get the name of the role&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -H &lt;span style="color:#e6db74"&gt;&amp;#34;X-aws-ec2-metadata-token: &lt;/span&gt;$TOKEN&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; http://169.254.169.254/latest/meta-data/iam/security-credentials&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Get the credential for the role session&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -H &lt;span style="color:#e6db74"&gt;&amp;#34;X-aws-ec2-metadata-token: &lt;/span&gt;$TOKEN&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; http://169.254.169.254/latest/meta-data/iam/security-credentials/InstanceProfileRoleName&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;These commands emulate how the SDK library fetches the credentials to assume the instance profile role. You can also find similar commands on the &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html#instance-metadata-security-credentials"&gt;documentation&lt;/a&gt;. However, there isn&amp;#8217;t much details about how the instance metadata service interacts with the STS service, except a general statement:&lt;/p&gt;&#10;&lt;blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;/p&gt;&#10;&lt;cite&gt;These security credentials are temporary and we rotate them automatically. We make new credentials available at least five minutes before the expiration of the old credentials.&lt;/cite&gt;&lt;/blockquote&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When creating a new EC2 instance, make sure that the instance metadata option has http_endpoint enabled, to enable the IMDS service. Also set http_tokens to required, which would run IMDSv2 exclusively. With that setup, the application does not have to mana to use an &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/use-a-supported-sdk-version-for-imdsv2.html"&gt;SDK version&lt;/a&gt; that supports IMDSv2. Another metadata option is http_put_response_hop_limit, with default of 1. This limits the number of hops in the metadata request. If the process runs from a Docker container with bridge networking mode, set it to 2 or the process cannot even secure a token.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="581px" viewBox="-0.5 -0.5 581 161" style="max-width:100%;max-height:161px;"&gt;&lt;defs&gt;&lt;/defs&gt;&lt;g&gt;&lt;g data-cell-id="0"&gt;&lt;g data-cell-id="1"&gt;&lt;g data-cell-id="rtsicqcqSDVIAgyO7OKc-1"&gt;&lt;g&gt;&lt;rect x="0" y="0" width="580" height="160" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"&gt;&lt;/rect&gt;&lt;/g&gt;&lt;/g&gt;&lt;g data-cell-id="rtsicqcqSDVIAgyO7OKc-2"&gt;&lt;g&gt;&lt;rect x="10" y="10" width="360" height="138.75" fill="#d5e8d4" stroke="#82b366" pointer-events="all"&gt;&lt;/rect&gt;&lt;/g&gt;&lt;/g&gt;&lt;g data-cell-id="rtsicqcqSDVIAgyO7OKc-14"&gt;&lt;g&gt;&lt;rect x="20" y="20" width="190" height="110" rx="16.5" ry="16.5" fill="#fff2cc" stroke="#d6b656" pointer-events="all"&gt;&lt;/rect&gt;&lt;/g&gt;&lt;/g&gt;&lt;g data-cell-id="rtsicqcqSDVIAgyO7OKc-3"&gt;&lt;g&gt;&lt;path d="M 565.5 114.05 C 565.5 116.02 563.9 117.5 561.89 117.5 L 521.19 117.5 C 518.74 117.5 517.5 115.73 517.5 114.23 L 517.5 60.92 C 517.5 58.71 519.41 57.5 521.14 57.5 L 562.11 57.5 C 563.87 57.5 565.5 58.91 565.5 60.86 Z" fill="#759c3e" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 541.39 93.57 C 532.5 93.57 524.45 86.77 524.45 77 C 524.45 68.48 531.82 60.98 541.69 60.98 C 550.43 60.98 558.55 67.94 558.55 77.21 C 558.55 85.85 551.34 93.57 541.39 93.57 Z" fill="#4f4f4f" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 536.09 104.69 L 534.68 104.69 C 534.68 103.94 534.44 103.07 533.39 102.6 C 532.79 102.38 531.82 102.2 530.71 102.53 C 530.16 102.71 529.66 103.1 529.53 103.83 C 529.33 104.91 530.16 105.32 530.97 105.56 C 532.7 106.02 533.44 106.07 534.28 106.38 C 535.21 106.7 536.2 107.24 536.5 108.42 C 536.75 109.74 536.23 110.68 535.52 111.24 C 534.79 111.84 533.52 112.3 531.84 112.24 C 530.49 112.18 529.3 111.73 528.61 111.04 C 528.01 110.44 527.62 109.68 527.62 108.51 L 529.01 108.51 C 529.06 109.72 529.67 110.41 530.57 110.82 C 531.59 111.21 532.94 111.22 533.88 110.82 C 534.49 110.56 535.05 110.04 535.04 109.18 C 535.02 108.36 534.47 107.86 533.26 107.55 C 532.15 107.25 531.05 107.18 530.02 106.82 C 528.94 106.43 527.73 105.71 528.03 103.83 C 528.21 102.66 529.14 101.83 530.61 101.48 C 531.96 101.2 533.41 101.33 534.39 101.84 C 535.37 102.37 536.04 103.31 536.09 104.69 Z M 545.95 102.54 L 542.32 102.54 L 542.32 111.88 L 540.84 111.88 L 540.84 102.54 L 537.14 102.54 L 537.14 101.32 L 545.95 101.32 Z M 554.91 104.69 L 553.5 104.69 C 553.5 103.94 553.26 103.05 552.21 102.57 C 551.62 102.35 550.65 102.17 549.53 102.51 C 548.98 102.69 548.48 103.07 548.35 103.8 C 548.15 104.9 548.98 105.3 549.8 105.54 C 551.52 106.01 552.25 106.04 553.11 106.35 C 554.03 106.67 555.02 107.21 555.3 108.39 C 555.57 109.72 555.05 110.65 554.35 111.22 C 553.61 111.81 552.34 112.27 550.66 112.21 C 549.31 112.17 548.12 111.7 547.43 111.01 C 546.83 110.43 546.44 109.68 546.44 108.51 L 547.84 108.51 C 547.88 109.72 548.5 110.38 549.39 110.79 C 550.41 111.19 551.76 111.21 552.7 110.8 C 553.31 110.55 553.88 110.02 553.86 109.15 C 553.85 108.34 553.28 107.85 552.09 107.53 C 550.97 107.22 549.88 107.15 548.84 106.79 C 547.76 106.41 546.55 105.68 546.85 103.82 C 547.04 102.63 547.96 101.81 549.44 101.45 C 550.79 101.17 552.23 101.32 553.2 101.83 C 554.19 102.35 554.86 103.31 554.91 104.69 Z M 544.43 80.07 L 544.43 67.05 L 540.92 67.05 L 540.92 76.71 L 534.44 76.71 L 534.44 80.07 Z M 541.45 90.2 C 534.11 90.2 527.96 84.4 527.96 77.15 C 527.96 69.79 534.46 64.33 541.63 64.33 C 548.65 64.33 555.02 70.01 555.02 77.27 C 555.02 84.59 548.65 90.2 541.45 90.2 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;g data-cell-id="rtsicqcqSDVIAgyO7OKc-4"&gt;&lt;g&gt;&lt;rect x="260" y="72.5" width="105" height="35" rx="5.25" ry="5.25" fill="#ffe6cc" stroke="#d79b00" pointer-events="all"&gt;&lt;/rect&gt;&lt;/g&gt;&lt;g&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 103px; height: 1px; padding-top: 90px; margin-left: 261px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;IMDS v2&lt;div&gt;169.254.169.254&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="313" y="94" fill="rgb(0, 0, 0)" font-family="&amp;quot;Helvetica&amp;quot;" font-size="12px" text-anchor="middle"&gt;IMDS v2&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g data-cell-id="rtsicqcqSDVIAgyO7OKc-5"&gt;&lt;g&gt;&lt;rect x="30" y="87.5" width="120" height="30" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"&gt;&lt;/rect&gt;&lt;/g&gt;&lt;g&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 103px; margin-left: 31px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;AWS SDK&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="90" y="106" fill="rgb(0, 0, 0)" font-family="&amp;quot;Helvetica&amp;quot;" font-size="12px" text-anchor="middle"&gt;AWS SDK&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g data-cell-id="rtsicqcqSDVIAgyO7OKc-8"&gt;&lt;g&gt;&lt;path d="M 364.79 83.28 L 511.13 82.53" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 516.38 82.51 L 509.4 86.04 L 511.13 82.53 L 509.36 79.04 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;/g&gt;&lt;g data-cell-id="rtsicqcqSDVIAgyO7OKc-9"&gt;&lt;g&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 71px; margin-left: 445px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;Request: AssumeRole&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="445" y="74" fill="rgb(0, 0, 0)" font-family="&amp;quot;Helvetica&amp;quot;" font-size="11px" text-anchor="middle"&gt;Request: AssumeRole&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g data-cell-id="rtsicqcqSDVIAgyO7OKc-10"&gt;&lt;g&gt;&lt;path d="M 517.5 92.5 L 369.79 94.03" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 364.54 94.08 L 371.51 90.51 L 369.79 94.03 L 371.58 97.51 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;/g&gt;&lt;g data-cell-id="rtsicqcqSDVIAgyO7OKc-11"&gt;&lt;g&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 124px; margin-left: 400px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;Response:&lt;div&gt;&amp;#8211; AccessKeyId&lt;/div&gt;&lt;div&gt;&amp;#8211; SecretAccessKey&lt;/div&gt;&lt;div&gt;&amp;#8211; SessionToken&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="400" y="128" fill="rgb(0, 0, 0)" font-family="&amp;quot;Helvetica&amp;quot;" font-size="11px"&gt;Response:&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g data-cell-id="rtsicqcqSDVIAgyO7OKc-12"&gt;&lt;g&gt;&lt;rect x="30" y="50" width="120" height="37.5" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 42 50 L 42 87.5 M 138 50 L 138 87.5" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;/g&gt;&lt;g&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 94px; height: 1px; padding-top: 69px; margin-left: 43px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Application&lt;div&gt;Process&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="90" y="72" fill="rgb(0, 0, 0)" font-family="&amp;quot;Helvetica&amp;quot;" font-size="12px" text-anchor="middle"&gt;Application&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g data-cell-id="rtsicqcqSDVIAgyO7OKc-13"&gt;&lt;g&gt;&lt;path d="M 240 20 L 270 20 L 270 50 L 240 50 Z" fill="#ed7100" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 249.43 40.57 L 260.14 40.57 L 260.14 29.86 L 249.43 29.86 Z M 261 29.86 L 262.71 29.86 L 262.71 30.71 L 261 30.71 L 261 32.43 L 262.71 32.43 L 262.71 33.29 L 261 33.29 L 261 34.57 L 262.71 34.57 L 262.71 35.43 L 261 35.43 L 261 37.14 L 262.71 37.14 L 262.71 38 L 261 38 L 261 39.71 L 262.71 39.71 L 262.71 40.57 L 261 40.57 L 261 40.63 C 261 41.07 260.64 41.43 260.2 41.43 L 260.14 41.43 L 260.14 43.14 L 259.29 43.14 L 259.29 41.43 L 257.57 41.43 L 257.57 43.14 L 256.71 43.14 L 256.71 41.43 L 255.43 41.43 L 255.43 43.14 L 254.57 43.14 L 254.57 41.43 L 252.86 41.43 L 252.86 43.14 L 252 43.14 L 252 41.43 L 250.29 41.43 L 250.29 43.14 L 249.43 43.14 L 249.43 41.43 L 249.37 41.43 C 248.93 41.43 248.57 41.07 248.57 40.63 L 248.57 40.57 L 247.29 40.57 L 247.29 39.71 L 248.57 39.71 L 248.57 38 L 247.29 38 L 247.29 37.14 L 248.57 37.14 L 248.57 35.43 L 247.29 35.43 L 247.29 34.57 L 248.57 34.57 L 248.57 33.29 L 247.29 33.29 L 247.29 32.43 L 248.57 32.43 L 248.57 30.71 L 247.29 30.71 L 247.29 29.86 L 248.57 29.86 L 248.57 29.8 C 248.57 29.36 248.93 29 249.37 29 L 249.43 29 L 249.43 27.29 L 250.29 27.29 L 250.29 29 L 252 29 L 252 27.29 L 252.86 27.29 L 252.86 29 L 254.57 29 L 254.57 27.29 L 255.43 27.29 L 255.43 29 L 256.71 29 L 256.71 27.29 L 257.57 27.29 L 257.57 29 L 259.29 29 L 259.29 27.29 L 260.14 27.29 L 260.14 29 L 260.2 29 C 260.64 29 261 29.36 261 29.8 Z M 255.43 46.09 C 255.43 46.12 255.4 46.14 255.38 46.14 L 243.91 46.14 C 243.88 46.14 243.86 46.12 243.86 46.09 L 243.86 34.62 C 243.86 34.6 243.88 34.57 243.91 34.57 L 246.43 34.57 L 246.43 33.71 L 243.91 33.71 C 243.41 33.71 243 34.12 243 34.62 L 243 46.09 C 243 46.59 243.41 47 243.91 47 L 255.38 47 C 255.88 47 256.29 46.59 256.29 46.09 L 256.29 44 L 255.43 44 Z M 267 23.91 L 267 35.38 C 267 35.88 266.59 36.29 266.09 36.29 L 263.57 36.29 L 263.57 35.43 L 266.09 35.43 C 266.12 35.43 266.14 35.4 266.14 35.38 L 266.14 23.91 C 266.14 23.88 266.12 23.86 266.09 23.86 L 254.62 23.86 C 254.6 23.86 254.57 23.88 254.57 23.91 L 254.57 26.43 L 253.71 26.43 L 253.71 23.91 C 253.71 23.41 254.12 23 254.62 23 L 266.09 23 C 266.59 23 267 23.41 267 23.91 Z" fill="#ffffff" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;/g&gt;&lt;g&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 1px; height: 1px; padding-top: 35px; margin-left: 272px;"&gt;&lt;div data-drawio-colors="color: #232F3E; " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(35, 47, 62); line-height: 1.2; pointer-events: all; white-space: nowrap;"&gt;&lt;div&gt;&lt;span style="background-color: initial;"&gt;EC2 Instance&lt;/span&gt;&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="272" y="39" fill="#232F3E" font-family="&amp;quot;Helvetica&amp;quot;" font-size="12px"&gt;EC2 I&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g data-cell-id="rtsicqcqSDVIAgyO7OKc-23"&gt;&lt;g&gt;&lt;path d="M 150 110 Q 160 170 171.64 113.74" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 172.7 108.59 L 174.71 116.16 L 171.64 113.74 L 167.86 114.74 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;g data-cell-id="rtsicqcqSDVIAgyO7OKc-24"&gt;&lt;g&gt;&lt;path d="M 238.45 107.5 Q 260 170 287.49 105.85" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 289.56 101.03 L 290.02 108.84 L 287.49 105.85 L 283.59 106.08 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;g data-cell-id="rtsicqcqSDVIAgyO7OKc-26"&gt;&lt;g&gt;&lt;rect x="180" y="87.5" width="50" height="22.5" fill="#75dfff" stroke="rgb(0, 0, 0)" pointer-events="all"&gt;&lt;/rect&gt;&lt;/g&gt;&lt;g&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 48px; height: 1px; padding-top: 85px; margin-left: 181px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Bridge&lt;div&gt;Network&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="205" y="85" fill="rgb(0, 0, 0)" font-family="&amp;quot;Helvetica&amp;quot;" font-size="12px" text-anchor="middle"&gt;Bridge&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g data-cell-id="rtsicqcqSDVIAgyO7OKc-22"&gt;&lt;g&gt;&lt;rect x="220" y="87.5" width="30" height="20" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 225.13 105.54 L 225.13 100.05 L 235.79 100.05 L 235.79 102.79 L 241.32 102.79 L 241.32 103.58 L 247.24 103.58 L 247.24 101.23 L 241.32 101.23 L 241.32 102.01 L 236.58 102.01 L 236.58 98.09 L 244.08 98.09 L 244.08 98.87 L 248.82 98.87 L 248.82 96.52 L 244.08 96.52 L 244.08 97.3 L 235.79 97.3 L 235.79 99.26 L 225.13 99.26 L 225.13 96.13 L 232.63 96.13 L 232.63 93.38 L 236.58 93.38 L 236.58 94.56 L 242.5 94.56 L 242.5 91.42 L 236.58 91.42 L 236.58 92.6 L 231.84 92.6 L 231.84 95.34 L 225.13 95.34 L 225.13 89.85 L 250 89.85 L 250 105.54 L 239.74 105.54 L 239.74 107.5 L 238.95 107.5 L 238.95 105.54 L 238.16 105.54 L 238.16 107.5 L 237.37 107.5 L 237.37 105.54 L 236.58 105.54 L 236.58 107.5 L 235.79 107.5 L 235.79 105.54 L 235 105.54 L 235 107.5 L 234.21 107.5 L 234.21 105.54 L 233.42 105.54 L 233.42 107.5 L 232.63 107.5 L 232.63 105.54 L 231.84 105.54 L 231.84 107.5 L 231.05 107.5 L 231.05 105.54 L 230.26 105.54 L 230.26 107.5 L 229.47 107.5 L 229.47 105.54 L 228.68 105.54 L 228.68 107.5 L 227.89 107.5 L 227.89 105.54 Z M 222.76 107.5 L 222.76 88.68 L 220 88.68 L 220 87.5 L 223.95 87.5 L 223.95 107.5 Z" fill="#505050" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;g data-cell-id="rtsicqcqSDVIAgyO7OKc-16"&gt;&lt;g&gt;&lt;rect x="160" y="87.5" width="30" height="20" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;path d="M 165.13 105.54 L 165.13 100.05 L 175.79 100.05 L 175.79 102.79 L 181.32 102.79 L 181.32 103.58 L 187.24 103.58 L 187.24 101.23 L 181.32 101.23 L 181.32 102.01 L 176.58 102.01 L 176.58 98.09 L 184.08 98.09 L 184.08 98.87 L 188.82 98.87 L 188.82 96.52 L 184.08 96.52 L 184.08 97.3 L 175.79 97.3 L 175.79 99.26 L 165.13 99.26 L 165.13 96.13 L 172.63 96.13 L 172.63 93.38 L 176.58 93.38 L 176.58 94.56 L 182.5 94.56 L 182.5 91.42 L 176.58 91.42 L 176.58 92.6 L 171.84 92.6 L 171.84 95.34 L 165.13 95.34 L 165.13 89.85 L 190 89.85 L 190 105.54 L 179.74 105.54 L 179.74 107.5 L 178.95 107.5 L 178.95 105.54 L 178.16 105.54 L 178.16 107.5 L 177.37 107.5 L 177.37 105.54 L 176.58 105.54 L 176.58 107.5 L 175.79 107.5 L 175.79 105.54 L 175 105.54 L 175 107.5 L 174.21 107.5 L 174.21 105.54 L 173.42 105.54 L 173.42 107.5 L 172.63 107.5 L 172.63 105.54 L 171.84 105.54 L 171.84 107.5 L 171.05 107.5 L 171.05 105.54 L 170.26 105.54 L 170.26 107.5 L 169.47 107.5 L 169.47 105.54 L 168.68 105.54 L 168.68 107.5 L 167.89 107.5 L 167.89 105.54 Z M 162.76 107.5 L 162.76 88.68 L 160 88.68 L 160 87.5 L 163.95 87.5 L 163.95 107.5 Z" fill="#505050" stroke="none" pointer-events="all"&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;g data-cell-id="rtsicqcqSDVIAgyO7OKc-28"&gt;&lt;g&gt;&lt;rect x="30" y="20" width="110" height="30" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;/g&gt;&lt;g&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 35px; margin-left: 31px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Docker Daemon&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="85" y="39" fill="rgb(0, 0, 0)" font-family="&amp;quot;Helvetica&amp;quot;" font-size="12px" text-anchor="middle"&gt;Docker Daemon&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;switch&gt;&lt;g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;/g&gt;&lt;a transform="translate(0,-5)" xlink:href="https://www.drawio.com/doc/faq/svg-export-text-problems" target="_blank" rel="noopener"&gt;&lt;text text-anchor="middle" font-size="10px" x="50%" y="100%"&gt;Text is not SVG &amp;#8211; cannot display&lt;/text&gt;&lt;/a&gt;&lt;/switch&gt;&lt;/svg&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The diagram above illustrates this scenario with two hops.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;IAM Role Anywhere Concept&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As this point, we know that the process of workload assuming an IAM role, is essentially using SDK to gain role credentials from instance metadata. In addition to using SDK and instance metadata, AWS also supports using X.509 certificate to gain role credentials. As a result, workload no longer needs AWS SDK, and it doesn&amp;#8217;t rely on instance metadata from an EC2 instance. This mechanism is known as IAM Role Anywhere, and it greatly expands the use cases for IAM Role. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To make this work, we first have to provide a certificate authority (CA) to AWS as a trust anchor. It can be any X.509 CA including AWS Private CA. The IAM Roles Anywhere will allow any end-entity endorsed by this trust anchor, to assume an IAM role as specified. We also need to create a profile, in which we can add IAM policies directly, or link to an IAM roles with a trust policy for service principal &lt;code&gt;rolesanywhere.amazonaws.com&lt;/code&gt;. &lt;/p&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="691" height="283" src="https://www.digihunch.com/wp-content/uploads/2024/07/iam-role-anywhere-diagram.webp" alt="" class="wp-image-12989" srcset="https://www.digihunch.com/wp-content/uploads/2024/07/iam-role-anywhere-diagram.webp 691w, https://www.digihunch.com/wp-content/uploads/2024/07/iam-role-anywhere-diagram-300x123.webp 300w" sizes="auto, (max-width: 691px) 100vw, 691px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To gain role credential, the requestor must provide both the private key, and its end-entity certificate. The certificate proofs the endorsement of the CA as Role&amp;#8217;s trust anchor specifies. The private key proofs the requestor&amp;#8217;s identity. The requestor uses the &lt;a href="https://docs.aws.amazon.com/rolesanywhere/latest/userguide/credential-helper.html"&gt;aws_signing_helper&lt;/a&gt; utility to request role credentials. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The utility is compatible with the &lt;code&gt;credential_process&lt;/code&gt; feature in AWS config, which passes the returned role credentials to the AWS config profile for AWS CLI or SDK running on external virtual machine.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;IAM Role Anywhere Lab&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Let&amp;#8217;s tweak the three commands from this &lt;a href="https://www.digihunch.com/2021/08/creating-tls-certificate-kubernetes/"&gt;old post&lt;/a&gt; of mine to create the test materials: a self-signed CA and a certificate signed by the CA:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;openssl req -x509 -sha256 -newkey rsa:4096 -keyout ca.key -out ca.crt -days &lt;span style="color:#ae81ff"&gt;356&lt;/span&gt; -nodes -subj &lt;span style="color:#e6db74"&gt;&amp;#39;/CN=Health Certificate Authority&amp;#39;&lt;/span&gt; -addext basicConstraints&lt;span style="color:#f92672"&gt;=&lt;/span&gt;critical,CA:TRUE,pathlen:1 -addext keyUsage&lt;span style="color:#f92672"&gt;=&lt;/span&gt;keyCertSign&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cat &amp;gt; ext.cnf &lt;span style="color:#e6db74"&gt;&amp;lt;&amp;lt;EOF&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;[v3_leaf]&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;keyUsage = digitalSignature&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;basicConstraints=CA:false&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;EOF&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;openssl req -new -newkey rsa:4096 -keyout server.key -out server.csr -nodes -subj &lt;span style="color:#e6db74"&gt;&amp;#39;/CN=*.digihunch.com&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;openssl x509 -req -sha256 -days &lt;span style="color:#ae81ff"&gt;365&lt;/span&gt; -in server.csr -CA ca.crt -CAkey ca.key -set_serial &lt;span style="color:#ae81ff"&gt;01&lt;/span&gt; -out server.crt -extfile ext.cnf -extensions v3_leaf&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;I tweak them to add the &lt;a href="https://www.digihunch.com/2024/02/public-key-infrastructure-1-of-3-basics/"&gt;X.509 extensions&lt;/a&gt; to meet the &lt;a href="https://docs.aws.amazon.com/rolesanywhere/latest/userguide/trust-model.html"&gt;requirement&lt;/a&gt; for signature validation. We need the following files from the output.&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;ca.crt -&amp;gt; the certificate of the CA. We provide this file as the trust anchor&lt;/li&gt;&#10;&lt;li&gt;server.crt -&amp;gt; the certificate of the server, we need it as the end-entity certificate&lt;/li&gt;&#10;&lt;li&gt;server.key -&amp;gt; we need to present this file to proof identity of the requestor&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;First, we go to AWS console and create a new trust anchor. Copy the content of ca.crt as the certificate. Then we can create a profile with an IAM role, with the trust policy looking like &lt;a href="https://docs.aws.amazon.com/rolesanywhere/latest/userguide/trust-model.html"&gt;this&lt;/a&gt; example. Then we can request the role credential with one command:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;aws_signing_helper credential-process --certificate server.crt --private-key server.key --trust-anchor-arn $TRUST_ANCHOR_ARN --profile-arn $PROFILE_ARN --role-arn $ROLE_ARN&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Moreover, if the workload supports AWS SDK or can use CLI but not an EC2 instance, we can bake this in the AWS profile on the external machine:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[profile myprofile]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;output = json&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;credential_process = aws_signing_helper credential-process --certificate /path/server.crt --private-key /path/server.key --trust-anchor-arn $TRUST_ANCHOR_ARN --profile-arn $PROFILE_ARN --role-arn $ROLE_ARN&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;We can even configure this in any &lt;a href="https://aws.amazon.com/blogs/security/enable-external-pipeline-deployments-to-aws-cloud-by-using-iam-roles-anywhere/"&gt;pipeline&lt;/a&gt; as code to allow deployment from a non-AWS pipeline. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Summary&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In summary, apart from native AWS services, an IAM role can trust the following types of principals:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Native IAM identity such as an IAM user or an IAM group&lt;/li&gt;&#10;&lt;li&gt;Authenticated identity from SAML identity provider that IAM is configured to trust&lt;/li&gt;&#10;&lt;li&gt;Authenticated identity from OIDC identity provider that IAM is configured to trust&lt;/li&gt;&#10;&lt;li&gt;Validated identity endorsed by a Certificate Authority that IAM designate as a trust anchor&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The first type is rarely used because few organizations uses AWS IAM as their identity store. Most organizations have their identity store with federation capability via SAML. On the other hand, a lot of modern applications adopts identity stores with OIDC compliance. Now with IAM Role Anywhere, any entity with X.509 identity can also assume an IAM role. It works with any CI/CD pipeline, whether it is self-hosted. Also, it is now more important to keep the keys safe. &lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2024/05/managing-ec2-instances-across-aws-accounts-ssm/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Managing EC2 instances across accounts with Ansible&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2024/08/test-open-id-connect-flows-locally/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Test Open ID Connect Flows Locally&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>EKS impression</title><link>https://www.digihunch.com/2022/12/eks-impression/</link><pubDate>Fri, 23 Dec 2022 18:18:19 -0400</pubDate><guid>https://www.digihunch.com/2022/12/eks-impression/</guid><description>&lt;img src="https://www.digihunch.com/wp-content/uploads/2025/04/eks-impression-feature.webp" alt="Featured image of post EKS impression" /&gt;&lt;p class="wp-block-paragraph"&gt;I&amp;#8217;ve worked on a few &lt;a href="https://www.digihunch.com/2021/12/aks-troubleshooting-lessons-learned/"&gt;AKS projects&lt;/a&gt; previously. Since I joined AWS I wanted to put aside some time to check out EKS (Elastic Kubernetes Service). Here in this post, I put down my first impression on EKS, and also share my Terraform template in &lt;a href="https://github.com/digihunch/cloudkube"&gt;cloudkube&lt;/a&gt; project to create an EKS cluster.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Similar to AKS, EKS exposes API endpoint and the control plane components are hidden from AWS users. When creating EKS cluster it does not create the underlying VPC and subnets. Therefore, you have create an existing VPC and at least two subnets ahead of time, and specify them during EKS creation. Bear in mind that there is a &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/network_reqs.html"&gt;list of requirement&lt;/a&gt; for the VPC and subnets.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the cluster, the CNI that EKS officially supports is Amazon VPC CNI plugin. It is available as an add-on. Similar to Azure CNI, each Pod gets its own IP address. In addition, EKS supports other &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/alternate-cni-plugins.html"&gt;compatible CNI plugins&lt;/a&gt; such as Calico, Cilium, Weave Net and Antrea.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-computing-nodes-in-eks"&gt;Computing Nodes in EKS&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are three modes to address computing capacity: self-managed nodes, EKS managed node groups and AWS Fargate. The documentation has a &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/eks-compute.html"&gt;comparison table&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;With self-managed nodes, users create EC2 instances separately and then register them to the control plane. The instances must use the same IAM role and AMI. You can use Auto Scaling groups of &lt;a href="https://aws.amazon.com/bottlerocket/"&gt;Bottlerocket&lt;/a&gt; (AWS-sponsored purpose-built Linux distro for container host) nodes. The self-managed node option is mostly for AWS outpost customers who bring in their own computing capacity from data centre.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If you provision computing capacity from AWS, it makes sense to assign EKS managed node groups when creating EKS cluster. We can turn on &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/autoscaling.html"&gt;Cluster Autoscaler&lt;/a&gt;, a Kubernetes construct to manage the auto scaling of node groups. Sometimes we want to have more than one node groups. For example, to build a multi-architecture cluster, we need one node group with amd64 nodes and the other with arm64 nodes (e.g. instances with &lt;a href="https://aws.amazon.com/ec2/graviton/"&gt;Graviton&lt;/a&gt; processor). In general, arm-based CPU delivers better performance with less power consumption and the industry is slowly moving towards more arm-based CPU architecture.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Fargate is what I call managed computing service for EKS. With Fargate you do not need to tweak Cluster Autoscaler to self-manage computing capacity. The Fargate documentation has a long list of &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/fargate.html"&gt;considerations&lt;/a&gt;. For example, Pods must match a Fargate profile (&lt;a href="https://github.com/digihunch/real-quicK-cluster/blob/main/eks/cluster-fargate.yaml"&gt;here&lt;/a&gt;&amp;#8216;s an example) at the time that they&amp;#8217;re scheduled to run on Fargate. So we need to build Fargate profile and Pod labelling properly. Also, Fargate does not support DaemonSet. Another big consideration is that Fargate does not support non-VPC CNI. In my opinion these are pretty significant limitations. Many workloads (system-level or application-level) would need Daemonset (e.g. kube-proxy, some CNI or CSI drivers, &lt;a href="https://www.dynatrace.com/support/help/setup-and-configuration/setup-on-container-platforms/kubernetes/get-started-with-kubernetes-monitoring/set-up-k8s-monitoring-daemonset"&gt;Dynatrace&lt;/a&gt; monitoring). &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The pro of Fargate is the serverless computing model. The construct of a Fargate profile isn&amp;#8217;t complicated. You just specify subnets, namespace and labels. However, the downside is the long list of considerations. Some teams may consider these restrictions too much. The other overhead is the need to manage Fargate profile to ensure all Pods are scheduled somewhere. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To me, using Fargate alone impairs portability of workload. The good thing is that Fargate and Managed Node Group are not mutually exclusive on a cluster. In most cases, we can go partially serverless, and reap the benefits of both of them. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Node AutoScaling&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For workloads that don&amp;#8217;t have a matching Fargate profile, we have to figure out node autoscaling ourselves. I touched on Cluster Autoscaler in &amp;#8220;&lt;a href="https://www.digihunch.com/2022/03/autoscaling-in-kubernetes-from-metric-based-to-event-driven/"&gt;Autoscaling on Kubernetes Platform&lt;/a&gt;&amp;#8220;. CA works on AWS as well and is triggered upon a Pod coming to &lt;em&gt;unschedulable&lt;/em&gt; status in Scheduler. There is some limitations though. For example, CA interacts with Autoscaling Group (instead of EC2 instances directly). When it determines it&amp;#8217;s time to scale up, it bumps up the desired capacity by one at a time in the Autoscaling group. The configurations in Autoscaling group may also be at play and CA do not have direct control. For example, the &amp;#8220;&lt;a href="https://docs.aws.amazon.com/autoscaling/ec2/userguide/ec2-auto-scaling-scaling-cooldowns.html"&gt;scaling cooldown&lt;/a&gt;&amp;#8220;. The pool of nodes is homogenous as per the pre-configured launch template and CA has no control. If a Pod requires a different type of node (e.g. ARM64 CPU, spot instance, etc), then we&amp;#8217;d first have to create a node group with the desired node type. Moreover, in the worst cases, one-at-a-time scale-up does not meet the increase of demand driven by Pod increases, causing nuances such as racing conditions. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Because the Cluster Autoscaler doesn&amp;#8217;t really deal with the nodes themselves, this kind of integration is clunky and slow. Nearly half of Kubernetes customers on AWS report that configuring cluster auto scaling using the Kubernetes Cluster Autoscaler is challenging and restrictive, according to &lt;a href="https://aws.amazon.com/blogs/aws/introducing-karpenter-an-open-source-high-performance-kubernetes-cluster-autoscaler/"&gt;this&lt;/a&gt; blog post. As a result, AWS launched an open-source cluster autoscaler project, &lt;a href="https://karpenter.sh/"&gt;Karpenter&lt;/a&gt;. Karpenter first only supported EKS but now the support includes other CSPs. For EKS, Karpenter directly interact with different types of EC2 instances.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Karpenter makes node scaling work in a more cloud-native manner. In the presence of unschedulable Pods, Karpenter &lt;span style="text-decoration: underline" class="underline"&gt;bypasses the Kubernetes scheduler&lt;/span&gt; and works directly with the Cloud provider, to launch the minimal compute resources needed to fit those Pods and immediately binds the Pods to the newly provisioned Nodes without waiting for scheduler. As Pods are removed or rescheduled to other nodes, Karpenter looks for opportunities to terminate under-utilized nodes. Karpender defines a CR called Provisioner to specify node provisioning configuration, such as instance size, zone, CPU architecture, etc. It is a manifest that describes a node group so the node scaler is aware of all the available node types. You can have multiple Provisioners for different needs, just like node groups. The Provisioner CR can also set TTL for empty Nodes, such that once a Node has no pods other than DaemonSet, Karpenter will terminate the Node on TTL expiry.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Karpenter&amp;#8217;s idea is similar to the idea of AutoPilot cluster in GKE. The new EKS workshop has an &lt;a href="https://www.eksworkshop.com/docs/autoscaling/compute/karpenter/"&gt;section&lt;/a&gt; on how to set up CA and Karpenter in practice.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-identity-management-for-eks"&gt;Identity Management for EKS&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For IAM, we need to be concerned with three aspects. The management traffic to the cloud service, the management traffic for Kubernetes cluster and business traffic. &lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-regular"&gt;&lt;table class="has-white-background-color has-background has-fixed-layout"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;strong&gt;Traffic type&lt;/strong&gt;&lt;/th&gt;&lt;th&gt;AWS&lt;/th&gt;&lt;th&gt;Azure&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;I. Cloud Service Endpoint (Management Traffic for Cloud Service)&lt;/td&gt;&lt;td&gt;AWS IAM identity&lt;/td&gt;&lt;td&gt;Azure RBAC&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;II. Kubernetes API (Management Traffic for K8s Cluster)&lt;/td&gt;&lt;td&gt;IAM mapping or OIDC&lt;/td&gt;&lt;td&gt;Azure RBAC (implementation of OIDC)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;III. Business traffic&lt;/td&gt;&lt;td&gt;Up to Kubernetes Ingress&lt;/td&gt;&lt;td&gt;Up to Kubernetes Ingress&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For business traffic (type III), it is all up to the Ingress. I&amp;#8217;ve written another article on &lt;a href="https://medium.com/slalom-build/managing-ingress-traffic-on-kubernetes-platforms-ebd537cdfb46"&gt;managing ingress traffic on Kubernetes platforms&lt;/a&gt;. We interact with cloud service endpoint (type II) with either AWS CLI or Terraform, to create any object, including resources needed for a cluster. This is generally how we work with cloud service, not specific to Kubernetes. Usually the IAM identity assumes another IAM role, which empowers it with a lot of permissions.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For access to Kubernetes API (type III), EKS supports OIDC and IAM mapping. AWS documentation refers to this as &amp;#8220;&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/cluster-auth.html"&gt;Cluster Authentication&lt;/a&gt;&amp;#8220;. There is one special scenario where your identity for type II access inherits your identity for type I access. As the &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/add-user-role.html"&gt;document&lt;/a&gt; puts:&lt;/p&gt;&#10;&lt;blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When you create an Amazon EKS cluster, the AWS Identity and Access Management (IAM) entity user or role, such as a&amp;nbsp;&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers.html"&gt;federated user&lt;/a&gt;&amp;nbsp;that creates the cluster, is automatically granted&amp;nbsp;&lt;code&gt;system:masters&lt;/code&gt;&amp;nbsp;permissions in the cluster&amp;#8217;s role-based access control (RBAC) configuration in the Amazon EKS control plane. This IAM entity doesn&amp;#8217;t appear in any visible configuration, so make sure to keep track of which IAM entity originally created the cluster.&amp;nbsp;&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This special scenario (I call it the &amp;#8220;&lt;strong&gt;implicit master&lt;/strong&gt; &lt;strong&gt;user&lt;/strong&gt;&amp;#8220;) allows us to perform critical activities on the cluster, such as creating IAM mapping, or OIDC configuration. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The above addressed how AWS resource access Kubernetes resource. On the other hand, to address how a Kubernetes resource access AWS resources, we need IRSA (&lt;a href="https://docs.aws.amazon.com/emr/latest/EMR-on-EKS-DevelopmentGuide/setting-up-enable-IAM.html"&gt;IAM Roles for Service Account&lt;/a&gt;). We have a service account in Kubernetes and map it to an IAM role.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;AppMesh&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://aws.amazon.com/blogs/compute/introducing-aws-app-mesh-service-mesh-for-microservices-on-aws/"&gt;AppMesh&lt;/a&gt; is AWS&amp;#8217; Envoy based service-mesh offering supporting Kubernetes cluster, ECS service and even EC2 instance. AppMesh&amp;#8217;s control plane is a managed AWS service, with a &lt;a href="https://aws.github.io/aws-app-mesh-controller-for-k8s/"&gt;controller&lt;/a&gt; running on the Kubernetes cluster. To install AppMesh on the cluster:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;On the EKS cluster, install AppMesh Controller using Helm&lt;/li&gt;&#10;&lt;li&gt;Associate the cluster with IAM OIDC provider&lt;/li&gt;&#10;&lt;li&gt;Create an IAM role for the appmesh-controller service account&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;After these steps, you can create a mesh using CloudFormation, Terraform, etc. The data plane (Envoy proxy) can run on Kubernetes (as sidecar). Traffic between control plane and data plane can go through private link (Interface VPC &lt;a href="https://docs.aws.amazon.com/app-mesh/latest/userguide/infrastructure-security.html"&gt;endpoint&lt;/a&gt;) for added security. Like Istio, AppMesh enables mTLS. For observability, you can export Envoy metrics with Prometheus. Coupled with XRay, AppMesh also supports distributed tracing.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="832" height="354" src="https://www.digihunch.com/wp-content/uploads/2022/12/appmesh-virtualgateway.webp" alt="" class="wp-image-12883" srcset="https://www.digihunch.com/wp-content/uploads/2022/12/appmesh-virtualgateway.webp 832w, https://www.digihunch.com/wp-content/uploads/2022/12/appmesh-virtualgateway-300x128.webp 300w, https://www.digihunch.com/wp-content/uploads/2022/12/appmesh-virtualgateway-768x327.webp 768w" sizes="auto, (max-width: 832px) 100vw, 832px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;AppMesh uses a different set of CRDs than Istio. Key CRDs are:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;Mesh&lt;/strong&gt;: represents an entire service mesh. At mesh level you can configure Egress filter (to allow or deny external traffic) and set IP version (v4 vs v6)&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;VirtualGateway&lt;/strong&gt;: a CRD that represents an &lt;a href="https://aws.amazon.com/blogs/containers/introducing-ingress-support-in-aws-app-mesh/"&gt;Ingress&lt;/a&gt; in to the Mesh. A virtual gateway allows resources that are outside of your mesh to communicate to resources that are inside of your mesh. A virtual gateway references Envoy proxy deployment by podSelector. It references GatewayRoutes by namespaceSelector, and optionally gatewayRouteSelector. You also specify listeners in the manifest to reference Envoy proxy Service (LoadBalancer Type).&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;GatewayRoute&lt;/strong&gt;: A gateway route is attached to a virtual gateway and routes traffic to an existing virtual service. If a route matches a request, it can distribute traffic to a target virtual service. In the manifest, you specify a list of httpRoute, each with matching condition and action. In the action section you can specify virtualService as target.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;VirtualService&lt;/strong&gt;: an abstraction of a real service provided by a virtual node directly or indirectly by means of a virtual router. Dependent services call your virtual service by its virtualServiceName, and those requests are routed to the VirtualNode or VirtualRouter that is specified as the provider for the VirtualService.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;VirtualRouter&lt;/strong&gt;: Virtual routers handle traffic for virtual services. In a virtual router manifest, you can define Route to direct incoming requests to virtual nodes as target.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;VirtualNode&lt;/strong&gt;: A virtual node acts as a logical pointer to a particular task group (i.e. ECS service, Kubernetes deployment). It represent a Service in the AppMesh. In the manifest, you reference Pods by podSelector, specify listeners for any inbound traffic that your virtual node expects, and specify serviceDiscovery for your task group.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;You can configure those Custom Resources using YAML manifests (and check the &lt;a href="https://docs.aws.amazon.com/app-mesh/latest/APIReference/Welcome.html"&gt;API reference&lt;/a&gt; a lot). Alternatively, you can configure them from AWS CLI or AWS console. The console will help you visualize what can be configured. For further details on how these CRs play together, there is a &lt;a href="https://www.appmeshworkshop.com/"&gt;workshop&lt;/a&gt; for AppMesh.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;EKS cluster using Terraform&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Officially, there is an &lt;a href="https://github.com/aws-ia/terraform-aws-eks-blueprints"&gt;EKS blueprint&lt;/a&gt; project for provisioning EKS cluster in Terraform.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I also keep my own Terraform code in the AWS directory of &lt;a href="https://github.com/digihunch/cloudkube"&gt;cloudkube&lt;/a&gt; project. It works out to be a little more complex than my Terraform template to create Azure Kubernetes Cluster (Azure directory). Because I had to create Cognito resources with initial credential to allow users to connect to cluster without using the implicit master account.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Below is the diagram of the processes.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full"&gt;&lt;img loading="lazy" decoding="async" width="776" height="496" src="https://www.digihunch.com/wp-content/uploads/2022/12/eks-tf-mod.webp" alt="" class="wp-image-12881" srcset="https://www.digihunch.com/wp-content/uploads/2022/12/eks-tf-mod.webp 776w, https://www.digihunch.com/wp-content/uploads/2022/12/eks-tf-mod-300x192.webp 300w, https://www.digihunch.com/wp-content/uploads/2022/12/eks-tf-mod-768x491.webp 768w" sizes="auto, (max-width: 776px) 100vw, 776px" /&gt;&lt;figcaption class="wp-element-caption"&gt;Create EKS cluster with Terraform module&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The template configures kubectl access on a Bastion host, which assumed the same role that our IAM user uses to create the Kubernetes cluster. Therefore, the IAM role is the master identity. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Note that the IAM user (power-user) has very powerful permissions. Usually it is ideal to assign lots of permission to IAM Roles (temporary credential) instead of IAM user (long-term credential). So the &lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_terms-and-concepts.html"&gt;role chaining&lt;/a&gt; would look like:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;The IAM user that Terraform uses has no permission other than assuming a &amp;#8220;PowerUser&amp;#8221; role&lt;/li&gt;&#10;&lt;li&gt;The PowerUser role trusts the IAM user. It also has the permission to assume the &amp;#8220;EKS-Manager&amp;#8221; role&lt;/li&gt;&#10;&lt;li&gt;The EKS-Manager role trusts PowerUser&amp;#8217;s role session.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;However, the role chaining scenario above is not currently supported in &lt;a href="https://github.com/hashicorp/terraform-provider-aws/issues/22728"&gt;Terraform&lt;/a&gt;. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I use a Bastion host because the cluster endpoint is on private subnet. The bastion host is on a public subnet. However, if we do not like public subnet and public IP, we can place the bastion host on a private subnet, and use SSM system manager agent with &lt;a href="https://aws.amazon.com/premiumsupport/knowledge-center/systems-manager-ssh-vpc-resources/"&gt;SSH tunnel plugin &lt;/a&gt;to have SSH access to private bastion host.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-summary"&gt;Summary&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I first came across &lt;a href="https://medium.com/@andreidascalu/the-awfulness-of-aws-eks-d7700c1eccdc"&gt;this&lt;/a&gt; article about EKS and its awfulness and then decided to check out EKS. I&amp;#8217;m not sure all points are still valid but it&amp;#8217;s generally real-life experiences. There are also many peripheral services, such as AMP (AWS Managed Prometheus), AMG (AWS Managed Grafana), ADOT (AWS Distro for Open Telemetry), AppMesh (Another &lt;a href="https://www.appmeshworkshop.com/introduction/appmesh_components/"&gt;Envoy-based Service Mesh&lt;/a&gt;, &lt;a href="https://vedcraft.com/architecture/aws-appmesh-vs-istio-comparison-of-service-mesh/"&gt;easier to manage than Istio&lt;/a&gt; but less Powerful), with a lot to explore.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2022/12/landing-zone-in-aws/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Landing Zone in AWS – An Introduction&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2023/01/optimize-cpu-and-memory-for-kubernetes-pods/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Optimize CPU and Memory for Kubernetes Pod&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>AWS CDK example in Python – provision Kubernetes Nodes</title><link>https://www.digihunch.com/2020/12/instance-initialization-with-aws-cdk-in-python/</link><pubDate>Thu, 03 Dec 2020 21:14:00 -0400</pubDate><guid>https://www.digihunch.com/2020/12/instance-initialization-with-aws-cdk-in-python/</guid><description>&lt;p class="wp-block-paragraph"&gt;There are two mechanisms to initialize instances in AWS. Cloud init and CloudFormation Init. Both are widely used and we discuss each of them in this posting. Then we will give an example of using AWS CDK in Python.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-cloud-init"&gt;Cloud-Init&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Cloud-Init is a service originally built for Ubuntu, as a bootstrapping utility to customize a Linux VM as it boots for the first time. It has evolved to be an industry standard multi-distribution method for cross-platform (public or private) cloud instance initialization, or even bare-metal installation. In &lt;a href="https://cloudbase-init.readthedocs.io/en/latest/intro.html" class="rank-math-link"&gt;cloud-init&lt;/a&gt; you can install packages and write files, or configure users and security. Because cloud-init is called during the initial boot process, there are no additional steps or required agents to apply your configuration. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Cloud-Init uses UserData, which is part of instance metadata. With AWS, you can pass two types of user data to&lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html#user-data-shell-scripts" class="rank-math-link"&gt; Amazon EC2&lt;/a&gt;: shell scripts and cloud-init directives.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The cloud-config files are text files encoded in base64, with more details covered in the documentation &lt;a href="https://cloudinit.readthedocs.io/en/latest/topics/format.html#cloud-config-data" class="rank-math-link"&gt;here&lt;/a&gt;. cloud-init also works across distributions. For example, you don&amp;#8217;t use apt-get install or yum install to install a package. Instead you can define a list of packages to install. cloud-init automatically uses the native package management tool for the distro you select.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-cloudformation-init"&gt;CloudFormation Init&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The cloudformation init mechanism does not only initialize instance, it also provides a mechanism for the resource being created to communicate with other resources. It allows an instance to emit signal to a different resource (via cfn-signal). It can also monitor changes to external resource and invoke local action (using cfn-hup with hooks). CloudFormation Init requires several components to work together:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;The cloudformation resource should have metadata. The metadata must have a key &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-init.html" class="rank-math-link"&gt;AWS::CloudFormation::Init&lt;/a&gt; in which configsets are declared.&lt;/li&gt;&#10;&lt;li&gt;The UserData must use helper script (&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-init.html" class="rank-math-link"&gt;cfn-init&lt;/a&gt;) to invoke configuration jobs&lt;/li&gt;&#10;&lt;li&gt;The UserData can use helper script (&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-signal.html" class="rank-math-link"&gt;cfn-signal&lt;/a&gt;) to signal with a CreationPolicy or WaitCondition (of the same or different resource), so you can synchronize other resources in the stack when the prerequisite resource or application is ready.&lt;/li&gt;&#10;&lt;li&gt;The &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-hup.html" class="rank-math-link"&gt;cfn-hup&lt;/a&gt; service on the instance can be configured, to check for updates to metadata and execute custom hooks when changes are detected.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 class="wp-block-heading" id="h-comparison"&gt;Comparison&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;While there are overlaps between the functionalities of Cloud Init and CloudFormation Init, the major difference is the latter support extended features (signal, update, etc); whereas the former is vendor neutral. The table below summarized some the differences:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-background" style="background-color:#e9fbe5"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Cloud Init&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;CloudFormation Init&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Works on&lt;/td&gt;&lt;td&gt;Linux OS distribution&lt;/td&gt;&lt;td&gt;CloudFormation resource, in combination with cfn &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-helper-scripts-reference.html" class="rank-math-link"&gt;helper scripts&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Usecase&lt;/td&gt;&lt;td&gt;Initialization only&lt;/td&gt;&lt;td&gt;Both initialization and resource update&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Trigger&lt;/td&gt;&lt;td&gt;cloud-init systemd service&lt;/td&gt;&lt;td&gt;Initial: from UserData&lt;br&gt;Update: by cfn hook&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Adoption&lt;/td&gt;&lt;td&gt;Multiple cloud vendors and bare-metal system&lt;/td&gt;&lt;td&gt;AWS cloud instances&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Action Playbook&lt;/td&gt;&lt;td&gt;/var/lib/cloud/&lt;br&gt;Instance Metadata -&amp;gt; User Data, encoded in base 64&lt;/td&gt;&lt;td&gt;CloudFormation Resource -&amp;gt; Metadata section -&amp;gt; AWS::CloudFormation::Init -&amp;gt; configSets and configs&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Log file and stdout&lt;/td&gt;&lt;td&gt;/var/log/cloud-init.log&lt;br&gt;/var/log/cloud-init-output.log&lt;/td&gt;&lt;td&gt;/var/log/cfn-init.log&lt;br&gt;/var/log/cfn-init-cmd.log&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;figcaption class="wp-element-caption"&gt;Comparison between cloud-init and cfn-init&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;h3 class="wp-block-heading" id="h-aws-cloud-development-toolkit-cdk"&gt;AWS Cloud Development Toolkit (CDK)&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Traditionally, AWS CloudFormation uses &lt;a href="https://aws.amazon.com/cloudformation/resources/templates/" class="rank-math-link"&gt;template &lt;/a&gt;in YAML or JSON for resource declaration. As the size of system grows, the amount of resource involved grows quickly and the size of such declaration file may grow beyond manageable.&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-nested-stacks.html" class="rank-math-link"&gt; Nested stacks&lt;/a&gt; and &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-stack-exports.html" class="rank-math-link"&gt;export of output&lt;/a&gt; are mechanisms designed to combat the template sprawling, but to a very limited extent. Two reasons it is hard to control template size are:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;In declarative statements, each line carries very small piece of information. Without flow controls such as if-else, loops, object oriented structure, the level of code reusability is very low;&lt;/li&gt;&#10;&lt;li&gt;Some auxiliary resources (such as AWS::EC2::VPCGatewayAttachment) must be declared explicitly, even though they are insignificant to the stack functionality&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To address these challenges, AWS introduced &lt;a class="rank-math-link" href="https://aws.amazon.com/cdk/"&gt;AWS CDK&lt;/a&gt; (cloud development tookkit), which supports multiple languages (JavaScript, TypeScript, Python, Java, and C#). The CDK was natively developed in TypeScript, which is supposed to be the preferred development language. A &lt;a class="rank-math-link" href="https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html"&gt;tutorial &lt;/a&gt;is provided here, with detailed API documentation &lt;a class="rank-math-link" href="https://docs.aws.amazon.com/cdk/api/latest/docs/aws-construct-library.html"&gt;here&lt;/a&gt;.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://www.digihunch.com/wp-content/uploads/2022/04/image-1.png" alt="" class="wp-image-4962" width="621" height="242"/&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To &lt;a href="https://docs.aws.amazon.com/cdk/latest/guide/work-with-cdk-python.html" class="rank-math-link"&gt;install &lt;/a&gt;aws cdk and create a hello world project, follow &lt;a href="https://docs.aws.amazon.com/cdk/latest/guide/hello_world.html" class="rank-math-link"&gt;this &lt;/a&gt;example.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-an-example-in-python"&gt;An Example in Python&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I have created an example for AWS CDK in Python. The purpose is to create some EC2 instance to complete a lab for Kubernetes (without using managed EKS service). The example provisions the followings:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;VPC, a public and private subnets, Internet and NAT gateways;&lt;/li&gt;&#10;&lt;li&gt;Relevant security groups and permissions&lt;/li&gt;&#10;&lt;li&gt;Bastion host, public instances in public subnet&lt;/li&gt;&#10;&lt;li&gt;Private instances in private subnet, with public route through NAT gateway&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The private instances forms a cluster for Kubernetes lab. We will use &lt;a href="https://kubernetes.io/docs/setup/production-environment/tools/kubespray/" class="rank-math-link"&gt;kubespray &lt;/a&gt;to initialize these instances. During the bootstraping, we download &lt;a href="https://github.com/kubernetes-sigs/kubespray" class="rank-math-link"&gt;kubespray&lt;/a&gt;, install ansible, etc.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Here is the code &lt;a href="https://github.com/digihunch/kubelab/tree/main/kube-cdk"&gt;repo&lt;/a&gt; for this example. With CloudFormation only, the single template could go well beyond 1000 lines. With CDK, the code are organized into several different python files, each representing a stack. The stacks can be stood up with command:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cdk deploy vpc-stack&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cdk deploy security-stack&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cdk deploy bastion-stack&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cdk deploy private-stack&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Although the &lt;a href="https://docs.aws.amazon.com/cdk/api/latest/python/index.html" class="rank-math-link"&gt;documentation &lt;/a&gt;in Python is available, there are generally not a lot of examples built out on the Internet. The &lt;a href="https://pypi.org/" class="rank-math-link"&gt;pypi &lt;/a&gt;site provides some Python specific examples for each module (e.g. &lt;a href="https://pypi.org/project/aws-cdk.core/" class="rank-math-link"&gt;core &lt;/a&gt;and &lt;a href="https://pypi.org/project/aws-cdk.aws-ec2/" class="rank-math-link"&gt;aws-ec2&lt;/a&gt;). Given these libraries are available for only 2 years (since 2018), many advocates TypeScript as the language. However, I have implemented some CloudFormation init, used helper script, and UserData in this example, without running into any language specific issues.It should be noted that the EC2 instance by default will call cfn-init. So there is no need to explicitly run cfn-signal or cfn-init from user data in python code (&lt;a href="https://github.com/digihunch/kubelab/blob/main/kube-cdk/kube_cdk/bastion_stack.py" class="rank-math-link"&gt;example&lt;/a&gt;). This can be verified in file /var/lib/cloud/instances/&amp;lt;instance-id&amp;gt;/user-data.txt which automatically includes the following lines:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# fingerprint: e1b32ead13878deb&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; set +e&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; /opt/aws/bin/cfn-init -v --region us-east-1 --stack bastion-stack --resource bastionhost5F466975da9934ba490de456 -c config_set_1,config_set_2&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; /opt/aws/bin/cfn-signal -e $? --region us-east-1 --stack bastion-stack --resource bastionhost5F466975da9934ba490de456&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; cat /var/log/cfn-init.log &amp;gt;&amp;amp;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;In addition to Python, AWS CDK also supports other languages. In the &lt;a href="https://www.digihunch.com/2020/12/instance-initialization-with-aws-cdk-in-python/"&gt;next&lt;/a&gt; post, we will discuss use of CDK in Typescript.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://www.digihunch.com/2020/11/ipvs-iptables-and-kube-proxy/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;IPVS, iptables and kube-proxy&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://www.digihunch.com/2020/12/high-performance-computing-cluster/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;High Performance Computing&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item></channel></rss>