<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ivan Bliskavka</title>
    <description>The latest articles on DEV Community by Ivan Bliskavka (@ibliskavka).</description>
    <link>https://dev.to/ibliskavka</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F705329%2Ff75f9346-d8b7-49d9-a534-f7b539ca392e.PNG</url>
      <title>DEV Community: Ivan Bliskavka</title>
      <link>https://dev.to/ibliskavka</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ibliskavka"/>
    <language>en</language>
    <item>
      <title>10x API Start-up Boost</title>
      <dc:creator>Ivan Bliskavka</dc:creator>
      <pubDate>Mon, 04 Mar 2024 22:55:18 +0000</pubDate>
      <link>https://dev.to/ibliskavka/10x-api-start-up-boost-2jh9</link>
      <guid>https://dev.to/ibliskavka/10x-api-start-up-boost-2jh9</guid>
      <description>&lt;p&gt;I while ago I optimized my &lt;a href="https://bliskavka.com/Screen-Saver-Gallery" rel="noopener noreferrer"&gt;Screen Saver Gallery&lt;/a&gt; API by loading a flat data file into lambda memory. A nightly job selects a random subset of the database and stores it in S3, and the API uses that file for the next 24 hours.&lt;/p&gt;

&lt;p&gt;This simplified my code, reduced costs, and improved performance.&lt;/p&gt;

&lt;p&gt;The average API calls took 50-100ms! ...but the start-up time was &lt;strong&gt;5 seconds&lt;/strong&gt;!!!&lt;/p&gt;

&lt;p&gt;I was using a lambda warmer, so most clients didn't hit the 5-second latency, but I wondered if I could reduce it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Before
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;A nightly process reads data from Dynamo DB and generates a zip file that looks like this:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;cache.zip

&lt;ul&gt;
&lt;li&gt;tableA.json&lt;/li&gt;
&lt;li&gt;tableB.json&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;The zip file is stored in S3&lt;/li&gt;
&lt;li&gt;When the lambda starts, it loads the zip file from S3, and loads it into memory (Duration: 4-5 seconds)
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;   &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ITable&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;All future requests are served from the in-memory cache (50-100ms)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Attempt 1
&lt;/h2&gt;

&lt;p&gt;Instead of loading from S3, store the zip file in a lambda layer, and rebuild the layer every night.&lt;/p&gt;

&lt;p&gt;Result: This reduced the start-up by 1 second.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attempt 2
&lt;/h2&gt;

&lt;p&gt;The layer improved the load time, so it was a good start, but I felt like it should be faster.&lt;/p&gt;

&lt;p&gt;I added some basic timers to my warmup function and discovered that almost 3 seconds were spent parsing the 217 files from the zip file.&lt;/p&gt;

&lt;p&gt;I updated the code to parse the table files in parallel but it only shaved off 500ms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attempt 3
&lt;/h2&gt;

&lt;p&gt;Armed with the timing information, I understood that my load issue was computationally constrained - decompressing zip files requires CPU cycles.&lt;/p&gt;

&lt;p&gt;I refactored my lambda layer cache to be a single &lt;code&gt;cache.json&lt;/code&gt; file that looks like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tableA"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tableB"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The lambda can load the data using &lt;code&gt;readFileSync('/opt/cache.json')&lt;/code&gt; and parse it immediately.&lt;/p&gt;

&lt;p&gt;Presto: Start-up load time is 500ms - a 10x improvement!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;If your data is relatively static you can cache it as a flat file in a Lambda layer to reduce app complexity, costs, and startup times. Use a Lambda to periodically query a new data set and publish a new Lambda Layer Version.&lt;/p&gt;

&lt;p&gt;When using layers, your maximum package size is 250MB. By compressing your data files you can cache A LOT of data, but you will experience decompression latency.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>lambda</category>
      <category>api</category>
      <category>performance</category>
    </item>
    <item>
      <title>Dynamic AWS IAM Policies</title>
      <dc:creator>Ivan Bliskavka</dc:creator>
      <pubDate>Sat, 02 Mar 2024 12:27:50 +0000</pubDate>
      <link>https://dev.to/ibliskavka/dynamic-aws-iam-policies-2jcn</link>
      <guid>https://dev.to/ibliskavka/dynamic-aws-iam-policies-2jcn</guid>
      <description>&lt;p&gt;We maintain a CloudFormation custom resource provider for Amazon Connect. The provider has grown organically, and as new features were added, the default role policy has become large.&lt;/p&gt;

&lt;p&gt;The provider can do simple low-security tasks like &lt;code&gt;associateLambda&lt;/code&gt;, or complex tasks like &lt;code&gt;createInstance&lt;/code&gt;, which requires access to security-sensitive resources like &lt;code&gt;kms&lt;/code&gt; and &lt;code&gt;iam&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;During a recent security review, we discovered that the same role policy was being used across all provider instances. This meant that if we used a low-security operation, such as &lt;code&gt;associateLambda&lt;/code&gt;, the role would be granted access to high-security resources like &lt;code&gt;kms&lt;/code&gt; and &lt;code&gt;iam&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution 1 - Inject a Pre-Built Role
&lt;/h2&gt;

&lt;p&gt;For the current project, we resolved the issue by introducing an optional role prop. This allowed the developer to select specific IAM permissions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// PSEUDO-CODE&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ConnectProvider&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IRole&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;role&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;IRole&lt;/span&gt;&lt;span class="p"&gt;}){&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
      &lt;span class="c1"&gt;// Configures the default (overly permissive) permissions&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;(...);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Uses the injected role&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Custom resource handler&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;(...);&lt;/span&gt;
&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addToPrincipalPolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PolicyStatement&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;effect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Effect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ALLOW&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;connect:AssociateLambdaFunction&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;connect:DisassociateLambdaFunction&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;instanceArn&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ConnectProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;associateLambda&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pros
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;We were able to quickly patch the current app&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Each dependent app would have to be updated manually. We have A LOT!&lt;/li&gt;
&lt;li&gt;The app developer must know exactly which IAM permissions are required.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Solution 2 - Dynamically Generate the Role
&lt;/h2&gt;

&lt;p&gt;I updated the custom resource constructs to dynamically build up the policy based on which resources are used, so I could roll out the update in a backward-compatible way.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// PSEUDO-CODE&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ConnectProvider&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IRole&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;role&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;IRole&lt;/span&gt;&lt;span class="p"&gt;}){&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;(...);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
      &lt;span class="c1"&gt;// Convert to IRole to avoid manipulating the role&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromArn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;roleArn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Custom resource handler&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Users call helper functions to create the custom resource&lt;/span&gt;
  &lt;span class="nf"&gt;associateLambda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;instanceArn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
      &lt;span class="c1"&gt;// Dynamically update self-managed role&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addToPrincipalPolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PolicyStatement&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;effect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Effect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ALLOW&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;connect:AssociateLambdaFunction&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;connect:DisassociateLambdaFunction&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;instanceArn&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CustomResource&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;serviceToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;functionArn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;instanceArn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;functionArn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;functionArn&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pros
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;No manual intervention is needed for dependent apps. Simply upgrade the NPM package and redeploy.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Resource deletion does not work properly.

&lt;ul&gt;
&lt;li&gt;If you had a custom resource like &lt;code&gt;associateLambda&lt;/code&gt;, everything works fine because the role policy is updated before the resource is created.&lt;/li&gt;
&lt;li&gt;But if you remove the custom resource in a future release, CloudFormation will update the role policy first (and remove the associated permission) before cleaning up the resource.&lt;/li&gt;
&lt;li&gt;As a result, you encounter a permission error when cleaning up the &lt;code&gt;associateLambda&lt;/code&gt; resource&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Circular dependencies

&lt;ul&gt;
&lt;li&gt;If you used the provider to &lt;code&gt;createInstance&lt;/code&gt; and then used the instance ARN in another construct like &lt;code&gt;associateLambda&lt;/code&gt; you will encounter a circular reference&lt;/li&gt;
&lt;li&gt;Details&lt;/li&gt;
&lt;li&gt;Invoke &lt;code&gt;createInstance&lt;/code&gt; and get instance ARN&lt;/li&gt;
&lt;li&gt;Invoke &lt;code&gt;associateLambda&lt;/code&gt; using instance ARN

&lt;ul&gt;
&lt;li&gt;Instance ARN is used in the dynamic policy, resulting in a circular reference&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Solution 3 - Mix of both
&lt;/h2&gt;

&lt;p&gt;In the end, I decided to use a combination of both solutions. I created a &lt;code&gt;ConnectProviderRoleBuilder&lt;/code&gt; to make it easier for developers to build the role.&lt;/p&gt;

&lt;p&gt;Additionally, I also updated the &lt;code&gt;ConnectProvider&lt;/code&gt; to automatically use the builder if a role is not provided.&lt;/p&gt;

&lt;p&gt;This means that we can update existing apps without any manual intervention. If the app encounters the issues described in Solution 2 during ongoing development, the team can use the &lt;code&gt;ConnectProviderRoleBuilder&lt;/code&gt; to generate an appropriate role quickly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// PSEUDO-CODE&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ConnectProviderRoleBuilder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IRole&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="cm"&gt;/**
    * Tracks if the provider was used to create an instance.
    * If so, we cannot limit role permissions to a specific instance
    * due to circular dependency.
    */&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;createdInstance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;existingRole&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;IRole&lt;/span&gt;&lt;span class="p"&gt;}){&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
      &lt;span class="c1"&gt;// Ensures role is not manipulated by the builder&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromArn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;existingRole&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;roleArn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;existingRole&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="cm"&gt;/**
  * Create an instance ARN for permission filtering
  * If the provider was used to create the instance the ARN will be
  * `instance/*` to avoid circular dependency error
  * Assumes this provider will operate on a single instance.
  */&lt;/span&gt;
  &lt;span class="nf"&gt;instanceArn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instanceId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createdInstance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// We can't reference the instanceId (circular ref)&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`arn:aws:connect:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:instance/*`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`arn:aws:connect:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:instance/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;instanceId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
      &lt;span class="c1"&gt;// Only add permissions if the role is being managed by the construct.&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addToPrincipalPolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PolicyStatement&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;effect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Effect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ALLOW&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nx"&gt;resources&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Helpers to add policy statements&lt;/span&gt;
  &lt;span class="nf"&gt;allowAssociateLambda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instanceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;functionArns&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;connect:AssociateLambdaFunction&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;connect:DisassociateLambdaFunction&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instanceArn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instanceId&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Update lambda resource policy to allow connect invoke&lt;/span&gt;

    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;allowCreateInstance&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createdInstance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ConnectProvider&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ConnectProviderRoleBuilder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IRole&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;role&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;IRole&lt;/span&gt;&lt;span class="p"&gt;}){&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;builder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ConnectProviderRoleBuilder&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;associateLambda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instanceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;allowAssociateLambda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instanceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;functionArn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CustomResource&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;serviceToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;functionArn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;instanceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;functionArn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;functionArn&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myLambda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IFunction&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Pre-build the role&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;builder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ConnectProviderRoleBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;allowAssociateLambda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instanceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;myLambda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;functionArn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ConnectProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;associateLambda&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instanceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;myLambda&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The simplest solution would have been to simply force the developer to inject a role but it would have created unnecessary developer friction because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"My app used to deploy fine, but now I have to manually create a new role".&lt;/li&gt;
&lt;li&gt;"I have no idea what is happening under the hood and which permissions are required", resulting in even more friction.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This solution was certainly more work, but it solved the problem with the least effort from the downstream developers.&lt;/p&gt;

&lt;p&gt;No, go build secure and elegant tools!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cdk</category>
      <category>iam</category>
      <category>security</category>
    </item>
    <item>
      <title>Resolution error: PolicySynthesizer at 'PolicySynthesizer' should be created in the scope of a Stack, but no Stack found</title>
      <dc:creator>Ivan Bliskavka</dc:creator>
      <pubDate>Fri, 16 Feb 2024 15:47:00 +0000</pubDate>
      <link>https://dev.to/ibliskavka/resolution-error-policysynthesizer-at-policysynthesizer-should-be-created-in-the-scope-of-a-stack-but-no-stack-found-5h20</link>
      <guid>https://dev.to/ibliskavka/resolution-error-policysynthesizer-at-policysynthesizer-should-be-created-in-the-scope-of-a-stack-but-no-stack-found-5h20</guid>
      <description>&lt;p&gt;We occasionally have a client who does not allow us to create IAM roles in their AWS account. In this scenario we must define the roles in CloudFormation, they create them, and we inject the role ARNs into the app.&lt;/p&gt;

&lt;p&gt;In the CloudFormation days, this would have been a significant overhaul if your app was not already set up for it. But with CDK, this is pretty easy with the &lt;a href="https://github.com/aws/aws-cdk/wiki/Security-And-Safety-Dev-Guide#using-the-customize-roles-feature-to-generate-a-report-and-supply-role-names" rel="noopener noreferrer"&gt;Role.customizeRoles&lt;/a&gt; method.&lt;/p&gt;

&lt;p&gt;While working on such a project, we bumped into this weird error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Resolution error: Resolution error: PolicySynthesizer at 'PolicySynthesizer' should be created in the scope of a Stack, but no Stack found.
Object creation stack:
  at stack traces disabled.
Object creation stack:
  at Execute again with CDK_DEBUG=true to capture stack traces
    at _lookup (/home/ibliskavka/git/data-lakedata/node_modules/aws-cdk-lib/core/lib/stack.js:1:3005)
    at _lookup (/home/ibliskavka/git/data-lakedata/node_modules/aws-cdk-lib/core/lib/stack.js:1:3178)
    at Function.of (/home/ibliskavka/git/data-lakedata/node_modules/aws-cdk-lib/core/lib/stack.js:1:2736)
    at Object.produce (/home/ibliskavka/git/data-lakedata/node_modules/aws-cdk-lib/core/lib/resource.js:1:4264)
    at Reference.resolve (/home/ibliskavka/git/data-lakedata/node_modules/aws-cdk-lib/core/lib/resource.js:1:4877)
    at DefaultTokenResolver.resolveToken (/home/ibliskavka/git/data-lakedata/node_modules/aws-cdk-lib/core/lib/resolvable.js:1:1401)
    at resolve (/home/ibliskavka/git/data-lakedata/node_modules/aws-cdk-lib/core/lib/private/resolve.js:1:2711)
    at Object.resolve [as mapToken] (/home/ibliskavka/git/data-lakedata/node_modules/aws-cdk-lib/core/lib/private/resolve.js:1:1079)
    at TokenizedStringFragments.mapTokens (/home/ibliskavka/git/data-lakedata/node_modules/aws-cdk-lib/core/lib/string-fragments.js:1:1475)
    at DefaultTokenResolver.resolveString (/home/ibliskavka/git/data-lakedata/node_modules/aws-cdk-lib/core/lib/resolvable.js:4:362)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above error doesn't provide any meaningful info, but if you follow the instructions like so:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CDK_DEBUG=true npm run cdk -- synth&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You get a much more meaningful error&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Resolution error: Resolution error: PolicySynthesizer at 'PolicySynthesizer' should be created in the scope of a Stack, but no Stack found.
Object creation stack:
  at new Intrinsic (/home/ibliskavka/git/data-lakedata/node_modules/aws-cdk-lib/core/lib/private/intrinsic.js:1:942)
  at new Reference (/home/ibliskavka/git/data-lakedata/node_modules/aws-cdk-lib/core/lib/reference.js:1:599)
  at new &amp;lt;anonymous&amp;gt; (/home/ibliskavka/git/data-lakedata/node_modules/aws-cdk-lib/core/lib/resource.js:1:4806)
  at mimicReference (/home/ibliskavka/git/data-lakedata/node_modules/aws-cdk-lib/core/lib/resource.js:1:4802)
  at CacheTable.getResourceArnAttribute (/home/ibliskavka/git/data-lakedata/node_modules/aws-cdk-lib/core/lib/resource.js:1:4185)
  at new Table (/home/ibliskavka/git/data-lakedata/node_modules/aws-cdk-lib/aws-dynamodb/lib/table.js:1:18313)
  at new CacheTable (/home/ibliskavka/git/data-lakedata/packages/caching/src/constructs/CacheTable.ts:20:5)
  ... (truncated)
Object creation stack:
  at Function.string (/home/ibliskavka/git/data-lakedata/node_modules/aws-cdk-lib/core/lib/lazy.js:1:953)
  at CacheTable.combinedGrant (/home/ibliskavka/git/data-lakedata/node_modules/aws-cdk-lib/aws-dynamodb/lib/table.js:1:13211)
  at CacheTable.grantReadWriteData (/home/ibliskavka/git/data-lakedata/node_modules/aws-cdk-lib/aws-dynamodb/lib/table.js:1:6157)
  ... (truncated)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;CacheTable.grantReadWriteData&lt;/code&gt; stack trace deals with permissions (which we are customizing), so I traced it down to this bit of code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CachingLambdaRole&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Role&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CachingLambdaRoleProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;assumedBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ServicePrincipal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lambda.amazonaws.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;grantReadWriteData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I replaced &lt;code&gt;props.table.grantReadWriteData(this)&lt;/code&gt; with policy statements in the role &lt;code&gt;inlinePolicy&lt;/code&gt; the error went away and I was able to deploy.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR;
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Enable CDK_DEBUG&lt;/li&gt;
&lt;li&gt;Check if your stack trace uses the &lt;code&gt;grant&lt;/code&gt; API. If so, try replacing it with policy statements.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  More Info
&lt;/h2&gt;

&lt;p&gt;I was able to find only a few articles about this error message and none of them dealt with &lt;code&gt;customizeRoles&lt;/code&gt;. I suspect this may be a bug in the CDK code because my app uses the &lt;code&gt;grant&lt;/code&gt; API elsewhere in the code and it works as-is.&lt;/p&gt;

</description>
      <category>troubleshooting</category>
      <category>aws</category>
      <category>cdk</category>
    </item>
    <item>
      <title>TechHub Hackathon 2023</title>
      <dc:creator>Ivan Bliskavka</dc:creator>
      <pubDate>Mon, 02 Oct 2023 14:27:04 +0000</pubDate>
      <link>https://dev.to/ibliskavka/techhub-hackathon-2023-mkd</link>
      <guid>https://dev.to/ibliskavka/techhub-hackathon-2023-mkd</guid>
      <description>&lt;p&gt;We pulled together a team on Friday night, built an app on Saturday, and presented on Sunday. Not a win, but what a great experience!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Team
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vMAnq2aw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://bliskavka.com/2023/10/02/tech-hub-hackathon-2023/store-front.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vMAnq2aw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://bliskavka.com/2023/10/02/tech-hub-hackathon-2023/store-front.jpg" alt="Juliana, Divyesh, Pavan, Ivan, Daniel" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Description
&lt;/h2&gt;

&lt;p&gt;Habitat for Humanity, a renowned non-profit organization dedicated to building homes and better communities, faces several operational challenges that your hackathon team can help address:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Targeting New Audiences/Real-Time Inventory Management&lt;/strong&gt; : Implement a solution that updates the online inventory in real-time, ensuring that customers are aware of available items and reducing discrepancies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;Before starting any design work, our team decided to take a field trip to the Broward ReStore, which was 30 minutes away.&lt;/p&gt;

&lt;p&gt;The store manager Mark, was very gracious and spent 30 minutes talking about the store, the mission, the volunteers, and the amazing items that pass through his store.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9oFNqziJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://bliskavka.com/2023/10/02/tech-hub-hackathon-2023/store-manager.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9oFNqziJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://bliskavka.com/2023/10/02/tech-hub-hackathon-2023/store-manager.jpg" alt="Selfie with Mark, the Store Manager" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Some Interesting Points
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Each store operates like a franchise and typically manages its own outreach, website, etc.&lt;/li&gt;
&lt;li&gt;Mark just came back from a conference with other ReStores where they shared ideas and approaches.&lt;/li&gt;
&lt;li&gt;Some stores (Los Angeles) have an excellent social media presence including viral videos on TikTok.&lt;/li&gt;
&lt;li&gt;Independent stores can have their own website, but there is no central platform for posting or managing inventory.&lt;/li&gt;
&lt;li&gt;The Broward store does not have a website, but they have a Facebook page.&lt;/li&gt;
&lt;li&gt;They move a lot of furniture, and the entire sales floor is typically replaced in a couple of weeks. Because each item is unique, maintaining a website inventory is a lot of overhead.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Donation Receiving
&lt;/h3&gt;

&lt;p&gt;Mark said that 54% of donations are corporate. Typically the item is scratched, dented, or discontinued.&lt;/p&gt;

&lt;p&gt;He typically uses Google Lens to scan the item to get a feel for what the item sells for. From this price, he discounts about 50%.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proposal
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A SaaS-like platform that is owned and managed by the Habitat for Humanity “Corporate”&lt;/li&gt;
&lt;li&gt;Each store is a “tenant” on the platform and can manage its own inventory.&lt;/li&gt;
&lt;li&gt;The inventory is streamlined using a web app, on a mobile phone

&lt;ul&gt;
&lt;li&gt;Receiving takes a photo and adds a quality rating.&lt;/li&gt;
&lt;li&gt;The app uploads a photo to the cloud and uses image recognition to identify the product.&lt;/li&gt;
&lt;li&gt;If the product is IDed, meta-data and trending price is returned to the app.&lt;/li&gt;
&lt;li&gt;Price is automatically discounted based on quality rating: 3-50%, 2-60%, 1-70%&lt;/li&gt;
&lt;li&gt;If the item is not IDed, the app prompts for metadata and price.&lt;/li&gt;
&lt;li&gt;Idea: If the current user is not the manager, the item will need to be approved by the manager.&lt;/li&gt;
&lt;li&gt;Once approved, the item automatically shows up on the website and in the Point of Sale system.&lt;/li&gt;
&lt;li&gt;A QR Code sticker is printed and attached to the item. (Bluetooth printer)&lt;/li&gt;
&lt;li&gt;When the item is sold, the sticker is scanned, and it is automatically removed from the website.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;The SaaS platform dynamically builds a storefront for each tenant

&lt;ul&gt;
&lt;li&gt;Adds the ability to search all stores in the vicinity.&lt;/li&gt;
&lt;li&gt;Customers can buy online and pick up in the store.&lt;/li&gt;
&lt;li&gt;Orders not picked up within X days are a donation.&lt;/li&gt;
&lt;li&gt;Online customer contact data can be imported into Outreach programs.&lt;/li&gt;
&lt;/ul&gt;


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

&lt;h3&gt;
  
  
  Technical Architecture
&lt;/h3&gt;

&lt;p&gt;With 900 stores across the country and hundreds of thousands of customers, we need to consider scalability. We chose to build using managed services in the cloud for resilience, durability, and scalability.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--I48PsQ2l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://bliskavka.com/2023/10/02/tech-hub-hackathon-2023/architecture.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--I48PsQ2l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://bliskavka.com/2023/10/02/tech-hub-hackathon-2023/architecture.png" alt="Architecture" width="800" height="542"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/u8ERNvwUoOA"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Unfortunately, we didn’t win. Here are some presentation takeaways for future reference.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;del&gt;Going first was a disadvantage. We didn’t know what to expect, and probably the judges didn’t either.&lt;/del&gt;

&lt;ul&gt;
&lt;li&gt;&lt;del&gt;Even if our presentation was a 10/10, there is no way a judge would assign that to the first presentation without seeing all the other stuff. And there is no way they remembered ours after 16 presentations.&lt;/del&gt;&lt;/li&gt;
&lt;li&gt;[Correction 10/3/23]: Going first was a challenge, we were up for it, and we did great!&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;We should have figured out how to screen share an iPhone. Seeing it on a phone would be more impressive.&lt;/li&gt;
&lt;li&gt;Personally, I was more focused on the practicality of the idea and our ability to execute (in 8-12) hours

&lt;ul&gt;
&lt;li&gt;Since only 2 of the judges were asking technical questions, I assume the others were more business-minded. They weren’t judging on “can these people do it”, they were judging on “as described, will this idea impact a lot of people?”&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TL;DR;&lt;/strong&gt; Go for the big idea - not on the technical feasibility.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Currently, AI is all the rage. Reverse Image Search uses an AI model, we should have emphasized that more in the presentation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://d8ftny2i4nqz0.cloudfront.net/capture"&gt;Demo Site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bliskavka.com/2023/10/02/tech-hub-hackathon-2023/ScreenRecording.mp4"&gt;App Recording&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/PradatiusD/habitat-restore"&gt;Code Repo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>career</category>
      <category>aws</category>
      <category>hackathon</category>
      <category>techhub</category>
    </item>
    <item>
      <title>Interview Pointers for Bootcamp Grads</title>
      <dc:creator>Ivan Bliskavka</dc:creator>
      <pubDate>Wed, 05 Apr 2023 20:05:00 +0000</pubDate>
      <link>https://dev.to/ibliskavka/interview-pointers-for-bootcamp-grads-18ab</link>
      <guid>https://dev.to/ibliskavka/interview-pointers-for-bootcamp-grads-18ab</guid>
      <description>&lt;p&gt;I met &lt;a href="https://www.linkedin.com/in/toddalbert/" rel="noopener noreferrer"&gt;Todd Albert&lt;/a&gt;, the founder of &lt;a href="https://bocacode.com/" rel="noopener noreferrer"&gt;Boca Code&lt;/a&gt; at an AWS networking event hosted by &lt;a href="https://www.cloudhesive.com/" rel="noopener noreferrer"&gt;CloudHesive&lt;/a&gt;. He got a kick out of the fact that I was a plumber before I got into IT, so he invited me to speak to the 9th cohort of the Boca Code Developer Bootcamp.&lt;/p&gt;

&lt;p&gt;It was a great experience! The facility is cozy and everyone was super friendly. They also had great coffee and an awesome selection of laptop stickers!&lt;/p&gt;

&lt;p&gt;I spoke about my journey from being a plumber to being a Sr. Architect at the leading Amazon Connect integrator, there were jokes, laughter, and most importantly - questions!&lt;/p&gt;

&lt;h2&gt;
  
  
  Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How long did it take before you finally felt confident as a developer?
&lt;/h3&gt;

&lt;p&gt;About 3 years - but you can get there sooner! The first 2 years I worked alone and didn’t have anyone to compare to, so I assumed I wasn't very good but I kept studying and kept at it.&lt;/p&gt;

&lt;p&gt;The 3rd year I worked with a team of sub-contractors on a project that got scrapped - so I figured that wasn't a very good metric.&lt;/p&gt;

&lt;p&gt;In my fourth year I moved to another company with a full internal dev team - and I realized that in many respects I was ahead of guys with 10-15 years of experience. They knew A LOT, but much of that tech was already dead. I knew a lot MORE than them about the current tech.&lt;/p&gt;

&lt;p&gt;Don't forget that as a bootcamp graduate your education may be more current than the other devs. You still have to get experience, but I promise you are doing much better than your imposter syndrome is telling you.&lt;/p&gt;

&lt;h3&gt;
  
  
  What makes a good impression for new employees?
&lt;/h3&gt;

&lt;p&gt;Typically the onboarding material includes reading, and assignments to get you familiar with the toolset. If you are spinning your wheels for 2 days on something - and the answer is in the reading, this is a strong negative.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You did not read the assignment and you just wasted 2 days because you are afraid to ask for help.&lt;/li&gt;
&lt;li&gt;I would rather you do the reading, do some googling, and when you are stuck for 4-8 hours, reach out for help. This work requires communication - maybe the requirements were unclear - being able to clear that up on your own initiative is very impressive!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Corollary - don’t ask for help for every little thing. Do your due diligence. Also, take notes - don’t ask the same thing over and over again.&lt;/p&gt;

&lt;h3&gt;
  
  
  What type of questions do you usually ask at an interview?
&lt;/h3&gt;

&lt;p&gt;I like to gauge a candidate's skill level, and then dive a little deeper based on their response.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For a Jr role I may ask what is the difference between an RDBMS and a NoSQL db.

&lt;ul&gt;
&lt;li&gt;Followup: When would you choose one over the other and why?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;For a mid role I would ask them to name the 3 types of testing (Unit, Integration, Manual).

&lt;ul&gt;
&lt;li&gt;What is the difference?&lt;/li&gt;
&lt;li&gt;How would you design your code to make testing easier? (expecting mocks &amp;amp; dependency injection)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;For a senior role I would have a conversation about design patterns.&lt;/li&gt;

&lt;li&gt;I always love to ask about personal or favorite projects. You have to love this work to be exceptional at it. If you don’t have any code you are proud of - it’s a turn-off.&lt;/li&gt;

&lt;li&gt;What is the most impactful project you have participated in? What was the impact - social, financial, etc? You have to be able to speak about your work in terms of return on investment&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  What tips do you have for interviewing?
&lt;/h3&gt;

&lt;p&gt;Be collaborative. If you are asked design questions they may be intentionally vague - ask the interviewer clarifying questions.&lt;/p&gt;

&lt;p&gt;If the question requires a succinct factual response, and you don't know the answer immediately, you can ask questions to get partial credit.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;I am not familiar with the term X, would you provide a short description and I may be able to able to answer the rest of the question?&lt;/li&gt;
&lt;li&gt;I have never used that in my work, but I think I know what it does based on your description - can I try to guess?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Remember not to B.S. during a technical interview. I prefer someone who says honestly "I don't know the answer, but am willing to try anyway if you give me some hints".&lt;/p&gt;

&lt;p&gt;In the real world you will rarely have all the facts. You will have to reach out to the client who reported the bug and ask them to do a screen share so that you can see what is happening. Asking good questions is part of the job.&lt;/p&gt;

&lt;h3&gt;
  
  
  Would you recommend I get cloud certs?
&lt;/h3&gt;

&lt;p&gt;I work in an AWS shop. I would take an AWS certified bootcamp grad over a non-certified grad any day. It's probably less relevant if the company is not using AWS, but it still shows personal initiative and a willingness to learn.&lt;/p&gt;

&lt;p&gt;If you are applying to work for an AWS Partner, they have to have a minimum amount of AWS Certified engineers to maintain their partner status. Having a cert in this case is extremely useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding your job economics
&lt;/h2&gt;

&lt;p&gt;During my talk I mentioned a couple of concepts and a student asked me to elaborate on them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding the economics of your business
&lt;/h3&gt;

&lt;p&gt;I work in the call center space - for each minute a customer is on a call it costs the company $1. If you have thousands of agents, and tens of thousands of calls per month, these figures start to add up!&lt;/p&gt;

&lt;p&gt;Reducing the average call handle time for a call by 1% for a sufficiently large business can reduce costs by millions over several years. If I can contribute that improvement consistently - my career is made.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding the economics of your position
&lt;/h3&gt;

&lt;p&gt;Typically IT is a cost center, not a profit center. &lt;em&gt;You&lt;/em&gt; cost money. If you are doing right, you cost a &lt;em&gt;lot&lt;/em&gt; of money (per hour).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You&lt;/em&gt; should &lt;strong&gt;NOT&lt;/strong&gt; be doing work that a computer can do faster, better, and more consistently. For this reason you should be on the lookout for automation, devops, scripts, or processes that reduce the amount of repetitive/manual work that you do, so you can focus on real innovations.&lt;/p&gt;

&lt;p&gt;Example: If some nightly job runs for 3 hours - don't bother spending 3 days speeding it up. It starts at 12:00, finishes by 3:00, and the first person that needs it is at 8:00. There is a 5-hour buffer - but you just cost the company 3 days for no economic benefit.&lt;/p&gt;

&lt;p&gt;Not everything needs to be a script. If some work only happens once a month and takes 15 minutes - a &lt;code&gt;How-To&lt;/code&gt; guide may be more appropriate. Next time you have to do the task, you can do it in 10 minutes, or simply hand it off to the new guy :)&lt;/p&gt;

&lt;p&gt;If you can make yourself (and others) more productive through automation and/or documentation you will also go far.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;It was fun to reflect on my journey and share some personal experiences with budding new developers!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://bocacode.com" rel="noopener noreferrer"&gt;Boca Code&lt;/a&gt; is an intense 40/hrs a week, for 10 weeks, software development bootcamp. Having spoken with the class and founders - I highly recommend them!&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>career</category>
    </item>
    <item>
      <title>CDK package.json Scripts</title>
      <dc:creator>Ivan Bliskavka</dc:creator>
      <pubDate>Wed, 05 Apr 2023 19:49:00 +0000</pubDate>
      <link>https://dev.to/ibliskavka/cdk-packagejson-scripts-2ldd</link>
      <guid>https://dev.to/ibliskavka/cdk-packagejson-scripts-2ldd</guid>
      <description>&lt;p&gt;I found the following package.json scripts very convenient when managing a complex CDK app. The key is the &lt;code&gt;--&lt;/code&gt; operator, which allows us to append additional parameters to a script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tsc --noEmit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cdk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm run build &amp;amp;&amp;amp; cdk"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cdk:pipeline"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm run build &amp;amp;&amp;amp; cdk --app 'npx ts-node --prefer-ts-exts bin/pipeline.ts'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jest"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"diff"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm run cdk -- diff"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"synth"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm run cdk -- synth --quiet"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"deploy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm run cdk -- deploy --all --require-approval never"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"deploy:pipeline"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm run cdk:pipeline -- deploy --all --require-approval never"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration allows us to create convenient scripts like deploy/diff/synth, but we still have the ability to pass in additional parameters like:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm run deploy -- --no-rollback --profile default&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I can also define multiple entry points&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;cdk&lt;/code&gt;: Interact with the stack directly.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cdk:pipeline&lt;/code&gt;: Deploy a CDK Pipeline which is able patch the CDK app when new changes are pushed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A pipeline execution can be slow, so being able to circumvent the pipeline in a dev/sandbox environment is extremely useful.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cdk</category>
      <category>devops</category>
    </item>
    <item>
      <title>Synth CDK app to Custom Bucket</title>
      <dc:creator>Ivan Bliskavka</dc:creator>
      <pubDate>Mon, 07 Feb 2022 22:50:32 +0000</pubDate>
      <link>https://dev.to/ibliskavka/synth-cdk-app-to-custom-bucket-2n5</link>
      <guid>https://dev.to/ibliskavka/synth-cdk-app-to-custom-bucket-2n5</guid>
      <description>&lt;p&gt;Some AWS customers don't use the CLI, and will not grant an external contractor CLI access. Trying to get access is a waste of time and resources. Do not fear, there is a solution!&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Create a client specific staging bucket&lt;/li&gt;
&lt;li&gt;Share the bucket with the client account via Bucket Policy&lt;/li&gt;
&lt;li&gt;Synth the stack to the staging bucket&lt;/li&gt;
&lt;li&gt;Share template URL with client&lt;/li&gt;
&lt;li&gt;Client can install using the URL in CloudFormation web console with their own user credentials&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  App Staging Bucket Policy
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Sid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MyClient"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Principal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"AWS"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::DEV_ACCOUNT_ID:root"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iam::PROD_ACCOUNT_ID:root"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"s3:GetObject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"s3:GetObjectVersion"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:s3:::app-staging-bucket/*"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Install CDK Assets &lt;code&gt;npm i -D cdk-assets&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Customize the stack synthesizer to use your custom staging bucket&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;template&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;someParam&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;someValue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;synthesizer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DefaultStackSynthesizer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;fileAssetsBucketName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-staging-bucket&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;// Use a custom role which has access to the asset bucket&lt;/span&gt;
    &lt;span class="na"&gt;fileAssetPublishingRoleArn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-client-staging-role&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;// Consider using a build date or version&lt;/span&gt;
    &lt;span class="na"&gt;bucketPrefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2.4.1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;// The client account does not need to be bootstrapped&lt;/span&gt;
    &lt;span class="na"&gt;generateBootstrapVersionRule&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;synth&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run &lt;code&gt;cdk synth&lt;/code&gt; to generate your assets.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Modify &lt;code&gt;cdk.out/template.assets.json&lt;/code&gt; to make the template file name more predictable&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;find entry with &lt;code&gt;sourcePath&lt;/code&gt;=&lt;code&gt;template.template.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;modify its &lt;code&gt;objectKey&lt;/code&gt; to something like &lt;code&gt;2.4.1/template.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;(you should probably write some code to automate this)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run &lt;code&gt;cdk-assets -v -p ./cdk.out/template.assets.json publish&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Share your template URL with the client. It will look something like:&lt;br&gt;&lt;br&gt;
&lt;code&gt;https://app-staging-bucket.s3.amazonaws.com/2.4.1/template.json&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Client can install the app using the CloudFormation web console.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Simpler Template Output
&lt;/h2&gt;

&lt;p&gt;Not sure what the side-effects of these are, but this produces a simpler template with less CDK metadata.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cdk synth --path-metadata false --version-reporting false&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  cdk.json
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"context"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@aws-cdk/core:newStyleStackSynthesis"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This has been very helpful for creating installers that are accessible to non-developers and usable in beginner AWS environments. I hope it save you some head-scratching!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://bliskavka.com/2022/02/07/synth-cdk-to-custom-bucket/" rel="noopener noreferrer"&gt;Originally posted on my blog&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloudformation</category>
      <category>cdk</category>
      <category>devops</category>
    </item>
    <item>
      <title>AWS Athena SAM Policies</title>
      <dc:creator>Ivan Bliskavka</dc:creator>
      <pubDate>Fri, 24 Dec 2021 17:04:07 +0000</pubDate>
      <link>https://dev.to/ibliskavka/aws-athena-sam-policies-19m6</link>
      <guid>https://dev.to/ibliskavka/aws-athena-sam-policies-19m6</guid>
      <description>&lt;p&gt;AWS Athena provides SQL queries over S3 data. The service depends on S3, Glue, and Athena itself so getting permissions set up can be tricky. Here is what worked for me.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;StartQueryFunction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless::Function&lt;/span&gt;
  &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;src/lambda/search.start&lt;/span&gt;
    &lt;span class="na"&gt;Policies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;S3ReadPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;BucketName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;DataBucket&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;S3CrudPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;BucketName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;AthenaResultsBucket&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;AthenaQueryPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;WorkGroupName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;AthenaWorkGroup&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
          &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;glue:GetTable&lt;/span&gt;
          &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;arn:aws:glue:${AWS::Region}:${AWS::AccountId}:catalog&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;arn:aws:glue:${AWS::Region}:${AWS::AccountId}:database/${GlueDatabase}&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s"&gt;arn:aws:glue:${AWS::Region}:${AWS::AccountId}:table/${GlueDatabase}/*&lt;/span&gt;

&lt;span class="na"&gt;GetResultFunction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless::Function&lt;/span&gt;
  &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;src/lambda/search.results&lt;/span&gt;
    &lt;span class="na"&gt;Policies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;S3CrudPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;BucketName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;AthenaResultsBucket&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;AthenaQueryPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;WorkGroupName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;AthenaWorkGroup&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cheers!&lt;/p&gt;

</description>
      <category>howto</category>
      <category>aws</category>
      <category>security</category>
    </item>
    <item>
      <title>Async Array Filter</title>
      <dc:creator>Ivan Bliskavka</dc:creator>
      <pubDate>Fri, 22 Oct 2021 13:04:33 +0000</pubDate>
      <link>https://dev.to/ibliskavka/async-array-filter-3pja</link>
      <guid>https://dev.to/ibliskavka/async-array-filter-3pja</guid>
      <description>&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;asyncFilter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;predicate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;predicate&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;_v&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>javascript</category>
    </item>
    <item>
      <title>Fargate + EFS Permissions using CDK</title>
      <dc:creator>Ivan Bliskavka</dc:creator>
      <pubDate>Thu, 21 Oct 2021 21:16:20 +0000</pubDate>
      <link>https://dev.to/ibliskavka/fargate-with-efs-cdk-9ep</link>
      <guid>https://dev.to/ibliskavka/fargate-with-efs-cdk-9ep</guid>
      <description>&lt;p&gt;I struggled WAY too long trying to sort out the permissions for EFS. Turns out, there are 2 layers. The IAM role, and the Posix permissions. Both throw a similar looking access denied. Finally!&lt;/p&gt;

&lt;p&gt;Don't judge me on the single AZ. I am running a single task in Fargate and only need one instance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;az&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fileSystem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FileSystem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Efs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;performanceMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PerformanceMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GENERAL_PURPOSE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;vpcSubnets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;subnetType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SubnetType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PUBLIC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;onePerAz&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;availabilityZones&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;az&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;accessPoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AccessPoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AccessPoint&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;fileSystem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fileSystem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;FargateTaskDefinition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Task&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;memoryLimitMiB&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;volumeName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;efs-volume&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addVolume&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;volumeName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;efsVolumeConfiguration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;fileSystemId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fileSystem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fileSystemId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;transitEncryption&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ENABLED&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;authorizationConfig&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;
      &lt;span class="na"&gt;accessPointId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;accessPoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accessPointId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ENABLED&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addContainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Container&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ContainerImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromAsset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./container&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;portMappings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;hostPort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addMountPoints&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;containerPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/mount/data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;sourceVolume&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;volumeName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;readOnly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addToTaskRolePolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PolicyStatement&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;elasticfilesystem:ClientRootAccess&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;elasticfilesystem:ClientWrite&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;elasticfilesystem:ClientMount&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;elasticfilesystem:DescribeMountTargets&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;`arn:aws:elasticfilesystem:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:file-system/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fileSystem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fileSystemId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addToTaskRolePolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PolicyStatement&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ec2:DescribeAvailabilityZones&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I hope this save someone a headache!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://bliskavka.com/2021/10/21/AWS-CDK-Fargate-with-EFS" rel="noopener noreferrer"&gt;Originally posted on my blog&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cdk</category>
      <category>devops</category>
    </item>
    <item>
      <title>Help: Logical ID Changed in my CDK App</title>
      <dc:creator>Ivan Bliskavka</dc:creator>
      <pubDate>Fri, 17 Sep 2021 15:52:42 +0000</pubDate>
      <link>https://dev.to/ibliskavka/help-logical-id-changed-in-my-cdk-app-14p0</link>
      <guid>https://dev.to/ibliskavka/help-logical-id-changed-in-my-cdk-app-14p0</guid>
      <description>&lt;p&gt;I recently came across a weird error in a private package CDK deploy:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;{dynamo table name} already exists in stack {current stack arn}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Upon further investigation it turned out that one of our internal CDK modules for building the dynamo tables has changed how it was naming the construct, resulting in a different CloudFormation Logical ID. Since we were explicitly naming the table, the stack update could not complete, because the table already exists.&lt;/p&gt;

&lt;p&gt;This app is a self-contained NPM module, so I wasn't able to modify the construct, but I came up with a workaround.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Back up the dynamo table data&lt;/li&gt;
&lt;li&gt;Copy the raw stack yaml from CloudFormation and save it on your computer&lt;/li&gt;
&lt;li&gt;Delete the table resource and references from the template&lt;/li&gt;
&lt;li&gt;Update the stack with the new template using the CloudFormation web console&lt;/li&gt;
&lt;li&gt;Verify that the table was deleted (and not retained) by CloudFormation&lt;/li&gt;
&lt;li&gt;Redeploy the CDK app, which will create a new table&lt;/li&gt;
&lt;li&gt;Restore the table data&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>cdk</category>
      <category>aws</category>
      <category>troubleshooting</category>
    </item>
    <item>
      <title>Missing Amplify UI Styles in Electron App</title>
      <dc:creator>Ivan Bliskavka</dc:creator>
      <pubDate>Wed, 15 Sep 2021 18:57:28 +0000</pubDate>
      <link>https://dev.to/ibliskavka/missing-amplify-ui-styles-in-electron-app-3beg</link>
      <guid>https://dev.to/ibliskavka/missing-amplify-ui-styles-in-electron-app-3beg</guid>
      <description>&lt;p&gt;I recently discovered the &lt;a href="https://electron-react-boilerplate.js.org/" rel="noopener noreferrer"&gt;Electron React Boilerplate&lt;/a&gt; project and wanted to use the &lt;a href="https://docs.amplify.aws/ui/auth/authenticator/q/framework/react/" rel="noopener noreferrer"&gt;AWS Amplify Authenticator&lt;/a&gt;. Lo and Behold: its ugly...&lt;/p&gt;

&lt;p&gt;I had used the typical &lt;code&gt;index.tsx&lt;/code&gt; import and I could see the CSS output, but for some reason it did not work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// index.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@aws-amplify/ui/dist/style.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After much Googling, it turns out that Electron React Boiler plate does some webpack-ing magic under the hood.&lt;/p&gt;

&lt;p&gt;The correct way to add the css is to import it in &lt;code&gt;App.global.css&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* App.global.css */&lt;/span&gt;
&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s2"&gt;'~@aws-amplify/ui/dist/style.css'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://bliskavka.com" rel="noopener noreferrer"&gt;Originally posted on my blog&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
