<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Sebastian Korfmann</title>
    <description>The latest articles on Forem by Sebastian Korfmann (@skorfmann).</description>
    <link>https://forem.com/skorfmann</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%2F333390%2Fd816999a-1aa3-4e72-8006-453c8d132bfb.jpeg</url>
      <title>Forem: Sebastian Korfmann</title>
      <link>https://forem.com/skorfmann</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/skorfmann"/>
    <language>en</language>
    <item>
      <title>Skymood - Watch Bluesky's heartbeat through emojis in real-time 🌟</title>
      <dc:creator>Sebastian Korfmann</dc:creator>
      <pubDate>Mon, 18 Nov 2024 08:56:07 +0000</pubDate>
      <link>https://forem.com/skorfmann/skymood-watch-blueskys-heartbeat-through-emojis-in-real-time-4knm</link>
      <guid>https://forem.com/skorfmann/skymood-watch-blueskys-heartbeat-through-emojis-in-real-time-4knm</guid>
      <description>&lt;p&gt;This is a brief background story to how I built &lt;a href="https://skymood.skorfmann.com/" rel="noopener noreferrer"&gt;Skymood&lt;/a&gt; over the course of two evenings.&lt;/p&gt;

&lt;p&gt;Since becoming more active on &lt;a href="https://bsky.app/profile/skorfmann.com" rel="noopener noreferrer"&gt;bluesky&lt;/a&gt; again, I started to look into how bluesky is built behind the scenes. The backbone which ties everything together is the &lt;a href="https://docs.bsky.app/docs/advanced-guides/firehose" rel="noopener noreferrer"&gt;Firehose&lt;/a&gt;, which is a full stream of events (posts, likes, follows, handle changes, etc). While that's one of the coolest aspects of Bluesky and &lt;a href="https://atproto.com/" rel="noopener noreferrer"&gt;ATProto&lt;/a&gt;, it's also a ton of data (in the realm of ~ 50 GB / day as of today) which would have to be transferred and processed, since it's an all or nothing approach with no filtering option.&lt;/p&gt;

&lt;p&gt;However, there's another option which got &lt;a href="https://docs.bsky.app/blog/jetstream" rel="noopener noreferrer"&gt;released&lt;/a&gt; a few weeks ago: &lt;a href="https://github.com/bluesky-social/jetstream" rel="noopener noreferrer"&gt;Jetstream&lt;/a&gt;. In contrast to the Firehose, this allows filtering by &lt;a href="https://atproto.com/specs/nsid" rel="noopener noreferrer"&gt;Collection NSIDs&lt;/a&gt; and &lt;a href="https://atproto.com/specs/repository" rel="noopener noreferrer"&gt;Repositories&lt;/a&gt;. This means, we can filter for e.g. all posts, either globally or scope it to a bunch of given user ids. That sounds pretty intriguing, doesn't it?&lt;/p&gt;

&lt;p&gt;As one does, I started to play around with just getting bunch of posts and a way to demonstrate what's possible. When looking at the stream of posts &lt;a href="https://firesky.tv/" rel="noopener noreferrer"&gt;flying&lt;/a&gt; by I was like, it would be nice to get some kind of moodboard based on the emojis. One prompt later:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa3acmwqnugk6yamnn7au.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa3acmwqnugk6yamnn7au.jpg" alt="Image description" width="800" height="590"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well, that's promising, so let's evolve that into a website we can put on the Internet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Literally Serverless
&lt;/h2&gt;

&lt;p&gt;The Jetstream Websocket endpoint is open and can be connected to from any browser. So technically, there's no reason why a server would be required. And sure enough, consuming the Jetstream filtered for posts is totally doable.&lt;/p&gt;

&lt;p&gt;Just give it a try in your terminal&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ websocat wss://jetstream2.us-east.bsky.network/subscribe\?wantedCollections=app.bsky.feed.post
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So I went ahead and threw together a quick React SPA using &lt;a href="https://waku.gg/" rel="noopener noreferrer"&gt;Waku&lt;/a&gt; which was subscribing straight to the Jetstream Websocket from the client side. The prototype was done pretty quickly. The only noticeable hurdle was, that the &lt;a href="https://skyware.js.org/guides/jetstream/introduction/getting-started/" rel="noopener noreferrer"&gt;Jetstream SDK&lt;/a&gt; depends on &lt;code&gt;node:events&lt;/code&gt;. Rather than trying to workaround that, it seemed a lot simpler to just go with a plain Websocket implementation.&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="nx"&gt;ws&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;WebSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wss://jetstream2.us-west.bsky.network/subscribe?wantedCollections=app.bsky.feed.post&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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 was kind of expecting some performance issues, but it was rather smooth. The only small optimization was to debounce rendering, so that not each and every new emoji would re-render.&lt;/p&gt;

&lt;p&gt;Deployed this to Cloudflare and pretty much done. Well, not so fast. Let's look at the consumed bandwidth.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ websocat wss://jetstream2.us-east.bsky.network/subscribe\?wantedCollections=app.bsky.feed.post | pv &amp;gt; /dev/null
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depending on the time of the day, this is currently using between ~ 60 KB/s and 150 KB/s for the global posts stream - and it's gonna increase by the day. Not really an issue on a fast, unmetered fibre connection, but on mobile it might be another story. Last but not least, it feels wrong to let the client do all the expensive work while potentially increasing the bandwidth &amp;amp; resources bill for the Bluesky team. Back to the drawing board.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cloudflare Durable Objects
&lt;/h2&gt;

&lt;p&gt;Cloudflare's &lt;a href="https://developers.cloudflare.com/durable-objects/" rel="noopener noreferrer"&gt;Durable Objects&lt;/a&gt; were on my list to try for a long time, handling Websockets are one of major &lt;a href="https://developers.cloudflare.com/durable-objects/examples/websocket-server/" rel="noopener noreferrer"&gt;use-cases&lt;/a&gt;. Rewriting the React client side handling was just a few prompts away and I was good to go.&lt;/p&gt;

&lt;p&gt;The main changes were to subscribe to Jetstream as an upstream connection, match for emojis and publish the emojis to subscribed clients.&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;// Track connected WebSocket clients and their emoji filters&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clients&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Set&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="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// When receiving a message from the data source&lt;/span&gt;
&lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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;try&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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&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;postText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Extract unique emojis from the text&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emojiRegex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[\p&lt;/span&gt;&lt;span class="sr"&gt;{Emoji_Presentation}&lt;/span&gt;&lt;span class="se"&gt;\p&lt;/span&gt;&lt;span class="sr"&gt;{Extended_Pictographic}&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/gu&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;emojis&lt;/span&gt; &lt;span class="o"&gt;=&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;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;postText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emojiRegex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[])];&lt;/span&gt;

    &lt;span class="c1"&gt;// Skip if no emojis found&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;emojis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Notify all connected clients&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;clients&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Send the list of emojis found&lt;/span&gt;
      &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;emojis&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;emojis&lt;/span&gt;
      &lt;span class="p"&gt;}));&lt;/span&gt;

      &lt;span class="c1"&gt;// If client has emoji filters and post contains matching emoji,&lt;/span&gt;
      &lt;span class="c1"&gt;// send the full post details&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;filters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;emojis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emoji&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emoji&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;post&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nx"&gt;emojis&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&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="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error processing message:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&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;And with that, we were down to somewhere between &lt;code&gt;0.5 - 3 KB/s&lt;/code&gt; for a client connection while the server is doing the heavylifting only once. That's a lot better!&lt;/p&gt;

&lt;p&gt;However, while the Durable Object was doing ok it seemed to be a bit slow (gut feeling, no data) and close the maximum memory of 128 MB (see &lt;a href="https://developers.cloudflare.com/durable-objects/platform/limits/" rel="noopener noreferrer"&gt;limits&lt;/a&gt;). What to do? So far it's a single Durable Object. Ideas from the forum / Discord of Cloudflare were along the lines of sharding, aka introduce a few durable objects for client handling while a single one does the upstream processing. That's a lot of complexity right there. I'm sure there are better ways, if anyone at Cloudflare reads this: I'd be more than happy to iterate on my approach. But for now I just wanted to ship it. So off to the next chapter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pivot: Bun.js
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://bun.sh/" rel="noopener noreferrer"&gt;Bun.js&lt;/a&gt; is another thing I wanted to look into for a while. In the back of my head it's categorized as Nodejs, but more performant. It turned out that Bun has a custom &lt;a href="https://bun.sh/docs/api/websockets" rel="noopener noreferrer"&gt;websocket server&lt;/a&gt; baked in, which claims 7x more throughput compare to Nodejs and &lt;code&gt;ws&lt;/code&gt;. Haven't verified those claims, but that's enough of an excuse to use it in this case.&lt;/p&gt;

&lt;p&gt;Again, a few prompts later the new version was up and running and deployed to fly on a rather small machine in Frankfurt, Germany. Let's see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ websocat wss://skymood-bun.fly.dev
{"type":"clientCount","count":1}
{"type":"emojis","emojis":["😘"]}
{"type":"emojis","emojis":["🧐"]}
{"type":"emojis","emojis":["🙏"]}
{"type":"emojis","emojis":["😀"]}
{"type":"emojis","emojis":["😂"]}
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the filtered version&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ (echo '{"type":"filter","emoji":"🥰"}'; cat) | websocat wss://skymood-bun.fly.dev
{"type":"emojis","emojis":["😊"]}
{"type":"emojis","emojis":["‼","✨"]}
{"type":"emojis","emojis":["🥰"]}
{"type":"post","text":"Good morning have a lovely day 🥰","url":"https://bsky.app/profile/did:plc:q4st6fcbn5jdi7xdf4o7a2jy/post/3lb7jrngi7c2m","timestamp":1731919511338,"emojis":["🥰"]}
{"type":"emojis","emojis":["🌻","🔪"]}
{"type":"emojis","emojis":["❤"]}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Beautiful :)&lt;/p&gt;

&lt;p&gt;Quite a journey, but we now have a rather robust Websocket relay, which connects to the Bluesky Jetstream once, and republishes only a small subset of only relevant messages. Find the current server code over at &lt;a href="https://github.com/skorfmann/skymood-relay" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Make sure to check out the end result &lt;a href="https://skymood.skorfmann.com/" rel="noopener noreferrer"&gt;https://skymood.skorfmann.com/&lt;/a&gt; and don't forget to share this post, the website or both :) Also, post comments either here or on this Bluesky &lt;a href="https://bsky.app/profile/skorfmann.com/post/3lb7tohpskk2i" rel="noopener noreferrer"&gt;post&lt;/a&gt;&lt;/p&gt;

</description>
      <category>bluesky</category>
      <category>bunjs</category>
      <category>javascript</category>
      <category>react</category>
    </item>
    <item>
      <title>The Journey of CDK.dev: From Static Site to Bluesky</title>
      <dc:creator>Sebastian Korfmann</dc:creator>
      <pubDate>Wed, 13 Nov 2024 13:48:43 +0000</pubDate>
      <link>https://forem.com/skorfmann/the-journey-of-cdkdev-from-static-site-to-bluesky-10ah</link>
      <guid>https://forem.com/skorfmann/the-journey-of-cdkdev-from-static-site-to-bluesky-10ah</guid>
      <description>&lt;p&gt;When we launched &lt;a href="https://cdk.dev/" rel="noopener noreferrer"&gt;cdk.dev&lt;/a&gt; in 2020, we created a simple landing page for the Cloud Development Kit ecosystem. This launch happened right after HashiCorp &lt;a href="https://www.hashicorp.com/blog/cdk-for-terraform-enabling-python-and-typescript-support" rel="noopener noreferrer"&gt;released&lt;/a&gt; CDK for Terraform. Our main goals were to connect people through our Slack community and share content from various sources.&lt;/p&gt;

&lt;p&gt;In the beginning, we built the website using NextJS, with all content stored directly in our git repository. Adding new content meant creating new files and submitting a pull request. This process wasn't very user-friendly, and people often made mistakes. Despite these challenges, community members continued to contribute new links.&lt;/p&gt;

&lt;p&gt;Earlier this year, we made changes to improve the website. We moved all our links and content to AWS using Amplify, storing everything in DynamoDB and S3. We thought this would make things easier for users, but it actually created new problems. Now we had to manage a state and deal with complex content submission processes. As a result, we noticed that people stopped adding new content over the past few months.&lt;/p&gt;

&lt;p&gt;Recently, many people started using &lt;a href="https://bsky.app/" rel="noopener noreferrer"&gt;Bluesky&lt;/a&gt;, a new social platform. I had created an &lt;a href="https://bsky.app/profile/skorfmann.com" rel="noopener noreferrer"&gt;account&lt;/a&gt; there last year but hadn't used it much. When I looked at it again, I became very interested in how it works, especially its &lt;a href="https://atproto.com/" rel="noopener noreferrer"&gt;AT Protocol&lt;/a&gt;. I realized we could use Bluesky's open data structure in many interesting ways.&lt;/p&gt;

&lt;p&gt;After successfully rebuilding my &lt;a href="https://skorfmann.com/" rel="noopener noreferrer"&gt;personal website&lt;/a&gt; using my Bluesky data, I became excited about the possibilities. Within a few hours, I created a new version of &lt;a href="https://cdk.dev/" rel="noopener noreferrer"&gt;cdk.dev&lt;/a&gt; that gets its information from our Bluesky &lt;a href="https://bsky.app/profile/cdk.dev" rel="noopener noreferrer"&gt;profile&lt;/a&gt;. I wrote a small &lt;a href="https://gist.github.com/skorfmann/8435d6bbb545d030540b76a590238621" rel="noopener noreferrer"&gt;script&lt;/a&gt; to move our existing links to the Bluesky account. When I shared this idea with others, people really liked it.&lt;/p&gt;

&lt;p&gt;Now, &lt;a href="https://cdk.dev/" rel="noopener noreferrer"&gt;cdk.dev&lt;/a&gt; pulls all its data from Bluesky. The website doesn't need to store any data itself, and it gets information from Bluesky through public requests that don't require login. We can still make many improvements to this system. For example, we could automate content publishing, create a special content feed, or analyze the shared content in new ways.&lt;/p&gt;

&lt;p&gt;For now, we're keeping it simple: if you want to share CDK-related content, just tag &lt;a href="https://bsky.app/profile/cdk.dev" rel="noopener noreferrer"&gt;@cdk.dev&lt;/a&gt; on Bluesky, and we'll help spread the word.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cdk</category>
      <category>bluesky</category>
      <category>atproto</category>
    </item>
    <item>
      <title>Exploring TypeScript Type Generation for JSON Paths: An AI-Assisted Journey</title>
      <dc:creator>Sebastian Korfmann</dc:creator>
      <pubDate>Thu, 27 Jun 2024 09:26:58 +0000</pubDate>
      <link>https://forem.com/skorfmann/exploring-typescript-type-generation-for-json-paths-an-ai-assisted-journey-1a5f</link>
      <guid>https://forem.com/skorfmann/exploring-typescript-type-generation-for-json-paths-an-ai-assisted-journey-1a5f</guid>
      <description>&lt;p&gt;As developers, we often find ourselves pushing the boundaries of what's possible with our tools. Recently, I conducted an intriguing experiment: creating a TypeScript type generator for multiple, arbitrary JSON path inputs. While the experiment isn't complete, the progress made with AI assistance is noteworthy.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge
&lt;/h2&gt;

&lt;p&gt;The goal was to create a TypeScript type that represents the structure of an object based on multiple JSON paths. For example, given paths like &lt;code&gt;$.dynamodb.NewImage.id.S&lt;/code&gt; and &lt;code&gt;$.dynamodb.NewImage.url.S&lt;/code&gt;, we wanted to generate a type that accurately represents the nested structure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj90jszr3xltfk7gp8seq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj90jszr3xltfk7gp8seq.png" alt="Image description" width="800" height="819"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Approach
&lt;/h2&gt;

&lt;p&gt;Here's a simplified version of the code that attempts to solve this problem:&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;type&lt;/span&gt; &lt;span class="nx"&gt;GenerateTypeFromJsonPaths&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Paths&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;JsonPath&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Paths&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;infer&lt;/span&gt; &lt;span class="nx"&gt;Rest&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;Path&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;JsonPath&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;MergeTypes&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ParseJsonPath&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;GenerateTypeFromJsonPaths&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Rest&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;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="p"&gt;{};&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ParseJsonPath&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Path&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="c1"&gt;// ... implementation&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;MergeTypes&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;U&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="c1"&gt;// ... implementation&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ResultType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;dynamodb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;NewImage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&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;S&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;S&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;S&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;foo&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="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code uses recursive type definitions to parse JSON paths and attempt to merge the resulting types into a single object type.&lt;/p&gt;

&lt;p&gt;You can view and experiment with the full code in the &lt;a href="https://www.typescriptlang.org/play/?#code/C4TwDgpgBAUgzgewHYAUCGwAWUC8UAGAJIQN5zABOAlkgOYC++A3AFAuiRToVwTpYAeACpQIAD2AQkAEzixEqDJgB8uKCPGSZcoqRoAzCBSgAlCOUZQA-KfPAoALihIIANyOt24aACEArlQANtIA8gBGAFYQAMbAQt7CohJSslDk1HSqOCxQ6klaqUQkBkZQANIQIPQAdHpIhsZmFvg5udZQJFAA2mVQNOWVALpO-kGhkTFxCU3AqvStuU6dPX1I6sNplDS0UPSeHNAAska0EPGQcMIAjAA06gBMWR2tK-0A1pUI+upXUAA+UA+IC+Dw2vU0KTkQJBQl+ADJAZ9vkJ7gt2scKKdzuZrj1BncUXjlGinODktpEcDkVc0TZYXiSeV8pDKTDUW12oSyoNGS53BRWHs2AcoABxKRGDBnbwAMQoCAAtvBkPxMJdVXIIRTlYosF1Bk8NcyKV0SsZVXdqlazbZyDzcjZVcbUjrVbTbfYtS6FKr9WiHVAMVjvJdRsFwlFYtiBNxeKqY0plMo7uKXBQpdi5YrXUpLjMk-7HFAw+NI1NIAmeHwlAmsAWOU4+UZWkshV5OEa8F1WgAiQjVaQgJBoBUIaRhaoAOQgAHcAJIKtCnapUaTVADKPZuvf7g+Ho-HU9nC6XEGqfgogQ3W53A6HI7HE+n88Xy+iioVUmA15Yg323ltPxAnLaA8FTSVJEzeUlR9XNazVZRPHfJByCgfQEAQJwmiAkC1BIVo9wfccljRZ8T1OEiOVyVcligdcnB7dCEB7XZtyoqAL0CWj6KgRiMJY+g2Ko98FU-JBgG4himIEtF5lyeYhSAA" rel="noopener noreferrer"&gt;TypeScript Playground&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The AI Contribution
&lt;/h2&gt;

&lt;p&gt;Interestingly, most of this code was generated with the help of AI models like GPT-4 and Claude 3.5 Sonnet. The experiment also drew inspiration from an existing GitHub repository, &lt;a href="https://github.com/sinclairnick/jsonpath-ts" rel="noopener noreferrer"&gt;jsonpath-ts&lt;/a&gt;, which provides type inference for JSONPath queries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current Limitations
&lt;/h2&gt;

&lt;p&gt;While the structure for each path is correctly generated, the code doesn't yet perfectly merge different branches into a single object type. This highlights an important point about AI-assisted coding: it can get you very far, but deep understanding is still crucial for solving complex problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reflections on AI and Programming
&lt;/h2&gt;

&lt;p&gt;This experiment brings to mind recent discussions about the changing landscape of software development, such as those outlined in the article &lt;a href="https://sourcegraph.com/blog/the-death-of-the-junior-developer" rel="noopener noreferrer"&gt;"The Death of the Junior Developer"&lt;/a&gt;. AI is indeed amplifying our capabilities, but it's a double-edged sword. It can accelerate both progress and dead ends.&lt;/p&gt;

&lt;p&gt;As we navigate this new era of AI-assisted development, it's clear that a solid understanding of fundamental concepts remains invaluable. AI can generate impressive code snippets, but knowing how to piece them together, debug issues, and push beyond the AI's limitations is where human expertise shines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Looking Ahead
&lt;/h2&gt;

&lt;p&gt;While the current implementation isn't perfect, it's remarkable that we can generate such complex types programmatically. As AI tools continue to evolve, experiments like this will likely become more feasible, opening up new possibilities in type-safe programming.&lt;/p&gt;

&lt;p&gt;For now, this experiment serves as a fascinating example of the potential - and current limitations - of AI-assisted coding in tackling advanced TypeScript challenges.&lt;/p&gt;




&lt;p&gt;For those interested in the original prompt that led to this experiment, you can find it &lt;a href="https://chatgpt.com/share/3afc2c24-f703-4d9e-b1f7-27f2ab444cf5" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>A Cloud Development Troubleshooting Treasure Hunt</title>
      <dc:creator>Sebastian Korfmann</dc:creator>
      <pubDate>Tue, 25 Jul 2023 09:03:55 +0000</pubDate>
      <link>https://forem.com/skorfmann/a-cloud-development-troubleshooting-treasure-hunt-495c</link>
      <guid>https://forem.com/skorfmann/a-cloud-development-troubleshooting-treasure-hunt-495c</guid>
      <description>&lt;p&gt;In the process of constructing a small &lt;a href="https://github.com/winglang/examples/pull/11" rel="noopener noreferrer"&gt;example&lt;/a&gt; project utilizing the &lt;a href="https://www.winglang.io/docs/standard-library/cloud/website" rel="noopener noreferrer"&gt;Website&lt;/a&gt; SDK resource of &lt;a href="https://www.winglang.io/" rel="noopener noreferrer"&gt;Winglang&lt;/a&gt;, I unintentionally found myself engaged in a troubleshooting quest due to some unforeseen behavior.&lt;/p&gt;

&lt;p&gt;In summary, the application I've created is depicted as a Wing diagram, and you can interact with the same version on the &lt;a href="https://www.winglang.io/play/?code=YgByAGkAbgBnACAAYwBsAG8AdQBkADsACgBiAHIAaQBuAGcAIAB1AHQAaQBsADsACgBiAHIAaQBuAGcAIABoAHQAdABwADsACgAKAGwAZQB0ACAAdwBlAGIAcwBpAHQAZQAgAD0AIABuAGUAdwAgAGMAbABvAHUAZAAuAFcAZQBiAHMAaQB0AGUAKAAKACAAIABwAGEAdABoADoAIAAiAC4ALwBzAHQAYQB0AGkAYwAiACwAIAAgAAoAKQA7AAoACgBsAGUAdAAgAGEAcABpACAAPQAgAG4AZQB3ACAAYwBsAG8AdQBkAC4AQQBwAGkAKAApADsACgB3AGUAYgBzAGkAdABlAC4AYQBkAGQASgBzAG8AbgAoACIAYwBvAG4AZgBpAGcALgBqAHMAbwBuACIALAAgAHsAIABhAHAAaQA6ACAAYQBwAGkALgB1AHIAbAAgAH0AKQA7AAoACgBsAGUAdAAgAGMAbwB1AG4AdABlAHIAIAA9ACAAbgBlAHcAIABjAGwAbwB1AGQALgBDAG8AdQBuAHQAZQByACgAKQA7AAoACgBsAGUAdAAgAGMAbwByAHMASABhAG4AZABsAGUAcgAgAD0AIABpAG4AZgBsAGkAZwBoAHQAKAByAGUAcQA6ACAAYwBsAG8AdQBkAC4AQQBwAGkAUgBlAHEAdQBlAHMAdAApADoAIABjAGwAbwB1AGQALgBBAHAAaQBSAGUAcwBwAG8AbgBzAGUAIAA9AD4AIAB7AAoAIAAgAHIAZQB0AHUAcgBuACAAYwBsAG8AdQBkAC4AQQBwAGkAUgBlAHMAcABvAG4AcwBlACAAewAKACAAIAAgACAAaABlAGEAZABlAHIAcwA6ACAAewAKACAAIAAgACAAIAAgACIAQQBjAGMAZQBzAHMALQBDAG8AbgB0AHIAbwBsAC0AQQBsAGwAbwB3AC0ASABlAGEAZABlAHIAcwAiACAAPQA%2BACAAIgAqACIALAAKACAAIAAgACAAIAAgACIAQQBjAGMAZQBzAHMALQBDAG8AbgB0AHIAbwBsAC0AQQBsAGwAbwB3AC0ATwByAGkAZwBpAG4AIgAgAD0APgAgACIAKgAiACwACgAgACAAIAAgACAAIAAiAEEAYwBjAGUAcwBzAC0AQwBvAG4AdAByAG8AbAAtAEEAbABsAG8AdwAtAE0AZQB0AGgAbwBkAHMAIgAgAD0APgAgACAAIgBPAFAAVABJAE8ATgBTACwAUABPAFMAVAAiACwACgAgACAAIAAgAH0ALAAKACAAIAAgACAAcwB0AGEAdAB1AHMAOgAgADIAMAA0AAoAIAAgAH0AOwAKAH0AOwAKAGEAcABpAC4AbwBwAHQAaQBvAG4AcwAoACIALwBoAGUAbABsAG8AIgAsACAAYwBvAHIAcwBIAGEAbgBkAGwAZQByACkAOwAKAGEAcABpAC4AcABvAHMAdAAoACIALwBoAGUAbABsAG8AIgAsACAAaQBuAGYAbABpAGcAaAB0ACAAKAByAGUAcQB1AGUAcwB0ADoAIABjAGwAbwB1AGQALgBBAHAAaQBSAGUAcQB1AGUAcwB0ACkAOgAgAGMAbABvAHUAZAAuAEEAcABpAFIAZQBzAHAAbwBuAHMAZQAgAD0APgAgAHsACgAgACAAcgBlAHQAdQByAG4AIABjAGwAbwB1AGQALgBBAHAAaQBSAGUAcwBwAG8AbgBzAGUAIAB7AAoAIAAgACAAIABzAHQAYQB0AHUAcwA6ACAAMgAwADAALAAKACAAIAAgACAAaABlAGEAZABlAHIAcwA6ACAAewAKACAAIAAgACAAIAAgACIAQwBvAG4AdABlAG4AdAAtAFQAeQBwAGUAIgAgAD0APgAgACIAdABlAHgAdAAvAGgAdABtAGwAIgAsAAoAIAAgACAAIAAgACAAIgBBAGMAYwBlAHMAcwAtAEMAbwBuAHQAcgBvAGwALQBBAGwAbABvAHcALQBPAHIAaQBnAGkAbgAiACAAPQA%2BACAAIgAqACIALAAKACAAIAAgACAAfQAsAAoAIAAgACAAIABiAG8AZAB5ADoAIAAiACAAPABkAGkAdgAgAGkAZAA9AFwAIgBoAGUAbABsAG8AXAAiACAAYwBsAGEAcwBzAD0AXAAiAG0AdAAtADQAXAAiAD4ASABlAGwAbABvACAAJAB7AGMAbwB1AG4AdABlAHIALgBpAG4AYwAoACkAfQA8AC8AZABpAHYAPgAiACwACgAgACAAfQA7AAoAfQApADsACgAKAC8ALwAgAHcAbwByAGsAYQByAG8AdQBuAGQAIABmAG8AcgAgAGgAdAB0AHAAcwA6AC8ALwBnAGkAdABoAHUAYgAuAGMAbwBtAC8AdwBpAG4AZwBsAGEAbgBnAC8AdwBpAG4AZwAvAGkAcwBzAHUAZQBzAC8AMwAyADgAOQAKAC8ALwAgAHQAaABpAHMAIABzAGgAbwB1AGwAZABuACcAdAAgAGIAZQAgAG4AZQBjAGUAcwBzAGEAcgB5ACwAIABzAGkAbgBjAGUAIABhAHAAaQAuAHUAcgBsACAAcwBoAG8AdQBsAGQACgAvAC8AIABiAGUAIABkAGkAcgBlAGMAdABsAHkAIABhAGMAYwBlAHMAcwBpAGIAbABlACAAIABpAG4AIAB0AGgAZQAgAHQAZQBzAHQACgBsAGUAdAAgAGEAcABpAFUAcgBsACAAPQAgAGEAcABpAC4AdQByAGwAOwAKAAoALwAvACAASABvAHcAZQB2AGUAcgAsACAAdwBoAGkAbABlACAAdABoAGkAcwAgAHcAbwByAGsAZQBkACAAZgBvAHIAIABBAFAASQAsACAAaQB0ACAAZABvAGUAcwBuACcAdAAgAHcAbwByAGsAIABmAG8AcgAKAC8ALwAgAHQAaABlACAAdwBlAGIAcwBpAHQAZQAuAHUAcgBsACAAcAByAG8AcABlAHIAdAB5ACAAOgAvAAoAbABlAHQAIAB3AGUAYgBzAGkAdABlAFUAcgBsACAAPQAgAHcAZQBiAHMAaQB0AGUALgB1AHIAbAA7AAoACgBsAGUAdAAgAGkAbgB2AG8AawBlAEEAbgBkAEEAcwBzAGUAcgB0ACAAPQAgAGkAbgBmAGwAaQBnAGgAdAAoAHIAZQBzAHAAbwBuAHMAZQA6ACAAaAB0AHQAcAAuAFIAZQBzAHAAbwBuAHMAZQAsACAAZQB4AHAAZQBjAHQAZQBkADoAIABzAHQAcgApACAAPQA%2BACAAewAKACAAIABsAG8AZwAoACIAcgBlAHMAcABvAG4AcwBlADoAIAAkAHsAcgBlAHMAcABvAG4AcwBlAC4AcwB0AGEAdAB1AHMAfQAgACIAKQA7AAoAIAAgAGEAcwBzAGUAcgB0ACgAcgBlAHMAcABvAG4AcwBlAC4AcwB0AGEAdAB1AHMAIAA9AD0AIAAyADAAMAApADsACgAgACAAYQBzAHMAZQByAHQAKAByAGUAcwBwAG8AbgBzAGUALgBiAG8AZAB5AD8ALgBjAG8AbgB0AGEAaQBuAHMAKABlAHgAcABlAGMAdABlAGQAKQAgAD0APQAgAHQAcgB1AGUAKQA7AAoAfQA7AAoACgAvAC8AIABEAG8AZQBzAG4AJwB0ACAAdwBvAHIAawAgAHIAaQBnAGgAdAAgAG4AbwB3AAoALwAvACAAdABlAHMAdAAgACIAcgBlAG4AZABlAHIAcwAgAHQAaABlACAAaQBuAGQAZQB4ACAAcABhAGcAZQAiACAAewAKAC8ALwAgACAAIABpAG4AdgBvAGsAZQBBAG4AZABBAHMAcwBlAHIAdAAoAGgAdAB0AHAALgBnAGUAdAAoAHcAZQBiAHMAaQB0AGUAVQByAGwAKQAsACAAIgBIAGUAbABsAG8ALAAgAFcAaQBuAGcAIgApADsACgAvAC8AIAB9AAoACgB0AGUAcwB0ACAAIgBhAHAAaQAgAHIAZQB0AHUAcgBuAHMAIAB0AGgAZQAgAGMAbwByAHIAZQBjAHQAIAByAGUAcwBwAG8AbgBzAGUAIgAgAHsACgAgACAAaQBuAHYAbwBrAGUAQQBuAGQAQQBzAHMAZQByAHQAKABoAHQAdABwAC4AcABvAHMAdAAoACIAJAB7AGEAcABpAFUAcgBsAH0ALwBoAGUAbABsAG8AIgApACwAIAAiAEgAZQBsAGwAbwAgADAAIgApADsACgB9AAoA" rel="noopener noreferrer"&gt;Wing playground&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frqcwyiuqve73wirz2ysr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frqcwyiuqve73wirz2ysr.png" alt="wing diagram" width="800" height="491"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The specifics of what this application does are not our focus today. However, it's crucial to note that the Website resource is composed of a Cloudfront Distribution that serves as a proxy to a &lt;em&gt;public&lt;/em&gt; S3 Bucket, which has been configured for hosting. Today, these will be our main subjects of discussion.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Brief Introduction to Wing
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Wing integrates both infrastructure and runtime code into a single language. This integration empowers developers to remain in their creative flow, enabling the delivery of superior, faster, and more secure software.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Wing is designed to compile down to various &lt;a href="https://www.winglang.io/docs/concepts/compiler-targets" rel="noopener noreferrer"&gt;targets&lt;/a&gt;. This feature provides the flexibility to execute &lt;a href="https://www.winglang.io/docs/concepts/tests" rel="noopener noreferrer"&gt;tests&lt;/a&gt; against each of these targets. You have the option to perform swift tests against the Wing &lt;a href="https://www.winglang.io/docs/concepts/simulator" rel="noopener noreferrer"&gt;simulator&lt;/a&gt; by using the command &lt;code&gt;wing test main.w&lt;/code&gt;. Alternatively, you can run a fully integrated test against the cloud provider of your choice with &lt;code&gt;wing test --progress -t tf-aws main.w&lt;/code&gt;. This command will render the application down to Terraform and JavaScript, allowing you to deploy the entire application and run the test against it, all without modifying a single line of code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Wing Examples Repository
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/winglang/examples" rel="noopener noreferrer"&gt;examples&lt;/a&gt; repository leverages GitHub Actions to test each of the examples against both the simulator and the &lt;code&gt;tf-aws&lt;/code&gt; target.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2m5eqo7jq525iuuil6tc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2m5eqo7jq525iuuil6tc.png" alt="github-actions" width="800" height="657"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The setup employs a GitHub/AWS OIDC &lt;a href="https://www.winglang.io/docs/guides/ci-cd#setup" rel="noopener noreferrer"&gt;configuration&lt;/a&gt;, utilizing a finely scoped AWS IAM role to manage the resources of all the examples within the repository. This aspect is vital, not just for security purposes, but also to trace the thought process that will be explained later.&lt;/p&gt;

&lt;h2&gt;
  
  
  First Act - Building &amp;amp; Testing Locally
&lt;/h2&gt;

&lt;p&gt;One of the great features of Wing is that it allows you to build and test your entire application locally on your own computer. Since the application itself is rather straightforward, I quickly assembled some resources and gave it a go with &lt;code&gt;wing it&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz6c6eh7mro4nx8pq5nrx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz6c6eh7mro4nx8pq5nrx.png" alt="wing console" width="800" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The impressive web interface for my small application is also functioning perfectly ;)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqr605kskqeb4zr40pw1m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqr605kskqeb4zr40pw1m.png" alt="website" width="800" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After adding a test, I was pleased by the speed and simplicity of the whole process.&lt;/p&gt;

&lt;p&gt;Naturally, running the same test against AWS took a bit more time because it involved a Cloudfront Distribution. However, the deployment and tests also executed flawlessly on my machine.&lt;/p&gt;

&lt;p&gt;Now, let's move on to the next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Second Act - Creating the Pull Request &amp;amp; So Begins the Treasure Hunt
&lt;/h2&gt;

&lt;p&gt;I created a pull request to initiate the previously mentioned Github Actions workflow. This would run all the tests in the CI setup. In previous new examples, I encountered some permission issues for the narrowly scoped CI role, so I wasn't too surprised when I saw some permission issues arise.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✖ terraform apply
Command failed: terraform apply -auto-approve
╷
│ Error: creating CloudFront Distribution: AccessDenied: User: arn:aws:sts::***:assumed-role/wing-examples/gh-actions-winglang-examples is not authorized to perform: cloudfront:CreateDistribution on resource: arn:aws:cloudfront::***:distribution/* because no identity-based policy allows the cloudfront:CreateDistribution action
- terraform destroy
│     status code: 403, request id: d1dfd897-05fa-4c0c-a69f-f082f9c1af28
│
│   with aws_cloudfront_distribution.env0_cloudWebsite_Distribution_5CC04CFB,
│   on main.tf.json line 133, in resource.aws_cloudfront_distribution.env0_cloudWebsite_Distribution_5CC04CFB:
│  133:       }

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

&lt;/div&gt;



&lt;p&gt;To fix this, I proceeded to add the necessary permissions to manage Cloudfront distributions for the CI role. I clicked on re-run, only to encounter another strange error related to S3.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Command failed: terraform apply -auto-approve
╷
│ Error: Error putting S3 policy: AccessDenied: Access Denied
│     status code: 403, request id: V1A0DQ4Y85ZJQQ6K, host id: wR65P8tjzKvu+1f7RZMhBJ65aFNWYCBVS90ck43Ji+YNDyHJw140V0dz5j/6XV+AHiDKG7ktrfM=
│
│   with aws_s3_bucket_policy.env0_cloudWebsite_PublicPolicy_67A62A0C,
│   on main.tf.json line 394, in resource.aws_s3_bucket_policy.env0_cloudWebsite_PublicPolicy_67A62A0C:
│  394:       }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hmm, an &lt;code&gt;Error putting S3 policy: AccessDenied: Access Denied&lt;/code&gt; for &lt;code&gt;aws_s3_bucket_policy&lt;/code&gt; seemed unusual. It pointed to the &lt;code&gt;s3:PutBucketPolicy&lt;/code&gt; action.&lt;/p&gt;

&lt;h3&gt;
  
  
  Trap Door Number 1 - IAM Role Permissions?
&lt;/h3&gt;

&lt;p&gt;So, what's up with that &lt;code&gt;s3:PutBucketPolicy&lt;/code&gt; permission which's pretty obviously missing. It's working locally with admin rights for the very same AWS account, after all.&lt;/p&gt;

&lt;p&gt;I double-checked the permissions for the CI role and even set &lt;code&gt;s3:*&lt;/code&gt; and eventually &lt;code&gt;*&lt;/code&gt;, but to no success. Puzzling.&lt;/p&gt;

&lt;p&gt;I decided to do some research on Google to see if I could uncover any helpful information. I found several discussions related to recent changes in AWS concerning &lt;a href="https://aws.amazon.com/about-aws/whats-new/2022/12/amazon-s3-automatically-enable-block-public-access-disable-access-control-lists-buckets-april-2023/" rel="noopener noreferrer"&gt;public buckets&lt;/a&gt;. The threads suggested that one now has to explicitly configure the &lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block" rel="noopener noreferrer"&gt;public settings&lt;/a&gt; for an S3 bucket. This seemed promising, so I checked the implementation of the Wing SDK bucket, which the website was using. It looked reasonable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isPublic&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;publicAccessBlock&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;S3BucketPublicAccessBlock&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PublicAccessBlock&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;bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;blockPublicAcls&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="na"&gt;blockPublicPolicy&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="na"&gt;ignorePublicAcls&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="na"&gt;restrictPublicBuckets&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;policy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2012-10-17&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;Statement&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="na"&gt;Effect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Allow&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;Action&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="s2"&gt;s3:GetObject&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;:&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;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&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="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;S3BucketPolicy&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PublicPolicy&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;bucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, this path seemed to be a dead end. It was time to go back to the drawing board.&lt;/p&gt;

&lt;h3&gt;
  
  
  Trap Door Number 2 - What Else Could Cause This Behavior?
&lt;/h3&gt;

&lt;p&gt;To recap, we're dealing with a situation where a Terraform application can be deployed from my local machine, where I have admin rights to the AWS account. However, when deploying the same application to the same AWS account from Github Actions, the deployment fails due to permission issues. The Github runner uses OIDC for authentication and authorization. Could it be related to this configuration? Or could organization policies disallowing public buckets be at play?&lt;/p&gt;

&lt;p&gt;To get more insight, I decided to examine the CloudTrail logs to see if anything unusual might catch my eye. The most conspicuous difference was the presence of &lt;code&gt;webIdFederationData&lt;/code&gt; in the &lt;code&gt;sessionContext&lt;/code&gt; for the CI role.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"webIdFederationData": {
    "federatedProvider": "arn:aws:iam::&amp;lt;accountId&amp;gt;:oidc-provider/token.actions.githubusercontent.com",
    "attributes": {}
},
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It seems unlikely that this should make a difference, but let's confirm this. I &lt;a href="https://www.skorfmann.com/p/e87d20c5-5663-4436-9926-1e363f62f3cf" rel="noopener noreferrer"&gt;assumed the CI role&lt;/a&gt; on my local machine and ran the AWS test again. Unsurprisingly, the operation was just as successful as before.&lt;/p&gt;

&lt;p&gt;I briefly considered the potential influence of Organizational policies or session policies, only to conclude that it's quite certain we're not dealing with a permissions issue after all. Time to move on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Trap Door Number 3 - Is It a Terraform Bug?
&lt;/h3&gt;

&lt;p&gt;The next step was to delve deeper into the actual Github Actions workflow.&lt;/p&gt;

&lt;p&gt;Could the answer lie within the Terraform trace logs? Before proceeding with this, I needed to ensure that this wouldn't expose any sensitive data given that we're working on a public repository. An &lt;a href="https://www.skorfmann.com/p/11d7a83c-a83f-497f-97e4-3c2a637116f7" rel="noopener noreferrer"&gt;alternative approach&lt;/a&gt; could be to SSH into the Github runner instead. Or perhaps use &lt;a href="https://github.com/nektos/act" rel="noopener noreferrer"&gt;act&lt;/a&gt; to simulate the situation locally? However, neither seemed feasible as getting OIDC to work or obtaining a quick shell via the command line wasn't possible. Back to the logging idea then.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F54b2q6h3rl7j6ehhkuez.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F54b2q6h3rl7j6ehhkuez.png" alt="tf trace logs" width="800" height="114"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The examination of the logs seemed safe enough for public visibility. I enlisted the help of ChatGPT to modify the workflow to print out logs when a failure occurs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- name: Execute wing test in matrix directory
  env:
    TF_LOG: info
    TF_LOG_PATH: ${{ runner.workspace }}/terraform.log
  run: cd ${{ matrix.example.directory }} &amp;amp;&amp;amp; wing test --debug -t tf-aws main.w
- name: Output Terraform log
  if: failure()
  run: cat ${{ runner.workspace }}/terraform.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alright, that should do it. Now let's analyze what we've got. As a precaution, I decided to double-check the versions of Terraform and AWS Provider. Both &lt;code&gt;terraform 1.5.x&lt;/code&gt; and &lt;code&gt;AWS Provider 4.65&lt;/code&gt; looked correct for both local and CI environments – nothing suspicious there.&lt;/p&gt;

&lt;p&gt;I then proceeded to comb through the vast log output - Terraform trace logs are indeed very detailed. In doing so, I came across a particularly intriguing find:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2023-07-19T15:49:49.105Z [WARN]  Provider "provider[\"registry.terraform.io/hashicorp/aws\"]" produced an unexpected new value for aws_s3_bucket_public_access_block.env0_cloudWebsite_PublicAccessBlock_E7BC7F4B, but we are tolerating it because it is using the legacy plugin SDK.
    The following problems may be the cause of any confusing errors from downstream operations:
      - .block_public_acls: was cty.False, but now cty.True
      - .block_public_policy: was cty.False, but now cty.True
      - .ignore_public_acls: was cty.False, but now cty.True
      - .restrict_public_buckets: was cty.False, but now cty.True
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keen observers may notice that this isn't just a &lt;code&gt;TRACE&lt;/code&gt; log; it's actually a &lt;code&gt;WARN&lt;/code&gt; statement. Hmm, what's going on here? It indeed concerns the &lt;code&gt;aws_s3_bucket_public_access_block&lt;/code&gt; resource, which we previously verified was correctly configured in the Wing SDK implementation. If this is throwing an error, and the &lt;code&gt;aws_s3_bucket_public_access_block&lt;/code&gt; values must be &lt;code&gt;false&lt;/code&gt; to define a public S3 Bucket, it's no surprise things are going awry. But wait a minute - why is it working locally again? Could it be... perhaps... a bug in Terraform?&lt;/p&gt;

&lt;h2&gt;
  
  
  Third Act - Unraveling the Mystery
&lt;/h2&gt;

&lt;p&gt;Well, at least we now have a promising lead. Some diligent googling and browsing through Github issues in the AWS provider project yielded no directly related findings. However, I did come across a &lt;a href="https://github.com/hashicorp/terraform-provider-aws/issues/29242" rel="noopener noreferrer"&gt;few&lt;/a&gt; &lt;a href="https://github.com/hashicorp/terraform-provider-aws/issues/31363" rel="noopener noreferrer"&gt;recent&lt;/a&gt; &lt;a href="https://github.com/hashicorp/terraform-provider-aws/issues/30951" rel="noopener noreferrer"&gt;bug&lt;/a&gt; reports about the recent change AWS made regarding the treatment of public buckets. And interestingly, they described precisely the behavior I was encountering.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error putting S3 policy: AccessDenied: Access Denied
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What's more, within these very issues, I stumbled upon a potential workaround.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resource "aws_s3_bucket_policy" "b" {
  ...
  depends_on = [aws_s3_bucket_public_access_block.b]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thus, the good news: it appears this isn't truly a Terraform bug – although the 'ERROR' log from earlier remains mysterious, perhaps it's just an AWS hiccup. Furthermore, it seems like this might simply be an issue with dependencies. However, what still baffles me is why this problem only surfaces in the CI environment and not locally.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fourth Act - Hooray - We've found the treasure
&lt;/h2&gt;

&lt;p&gt;It's time for the grand finale - attempting to replicate the CI behaviour locally and identify the simplest example that demonstrates the issue.&lt;/p&gt;

&lt;p&gt;Given that it seems to work on MacOS, it might be related to some specific behaviours in Linux or Docker. I've decided to use the Docker image I created for the &lt;a href="https://github.com/winglang/wing-github-action" rel="noopener noreferrer"&gt;Wing Github Action&lt;/a&gt; - &lt;code&gt;ghcr.io/winglang/wing-github-action:v0.1.0&lt;/code&gt; to start my investigation.&lt;/p&gt;

&lt;p&gt;Again, I'll double-check the Terraform versions just to be sure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Terraform v1.5.0
on linux_amd64
+ provider registry.terraform.io/hashicorp/aws v4.65.0

Terraform v1.5.2
on darwin_amd64
+ provider registry.terraform.io/hashicorp/aws v4.65.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the setup ready, it was time to initiate a test run within the Docker container.&lt;/p&gt;

&lt;p&gt;Success! We were able to reproduce the error. The next step was to trim down the Terraform configuration. After several iterations, it was narrowed down to these three critical components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;aws_s3_bucket&lt;/li&gt;
&lt;li&gt;aws_s3_bucket_policy&lt;/li&gt;
&lt;li&gt;aws_s3_bucket_public_access_block&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Interestingly, as the number of resources decreased, so did the error rate. The full Terraform application saw failure in about half of the attempts when run in the Docker container on my machine. In contrast, the slimmed-down version only failed in about one-third of the attempts. Strikingly, all builds failed when run on the Github Actions runner.&lt;/p&gt;

&lt;p&gt;To quantify this, an informal test was performed, applying and destroying the minimal setup ten times in succession:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Linux: 3/10 runs failed&lt;/li&gt;
&lt;li&gt;Mac: 0/10 runs failed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Applying a fix – an explicit &lt;code&gt;depends_on&lt;/code&gt; relation between the &lt;code&gt;aws_s3_bucket_policy&lt;/code&gt; and &lt;code&gt;aws_s3_bucket_public_access_block&lt;/code&gt; resources – successfully resolved the error on Linux. So, it appears we've found our culprit: a race condition among resources that sometimes allows for success, and other times results in failure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Failed run
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws_s3_bucket.cloudWebsite_WebsiteBucket_EB03D355: Creating...
aws_s3_bucket.cloudWebsite_WebsiteBucket_EB03D355: Creation complete after 2s [id=cloud-website-c8e58765-20230719204937669500000001]
aws_s3_bucket_policy.cloudWebsite_PublicPolicy_44BB71F3: Creating...
aws_s3_bucket_public_access_block.cloudWebsite_PublicAccessBlock_18A70311: Creating...
aws_s3_bucket_public_access_block.cloudWebsite_PublicAccessBlock_18A70311: Creation complete after 1s [id=cloud-website-c8e58765-20230719204937669500000001]
╷
│ Error: Error putting S3 policy: AccessDenied: Access Denied
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Successful run
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws_s3_bucket.cloudWebsite_WebsiteBucket_EB03D355: Creating...
aws_s3_bucket.cloudWebsite_WebsiteBucket_EB03D355: Creation complete after 2s [id=cloud-website-c8e58765-20230719205053652900000001]
aws_s3_bucket_public_access_block.cloudWebsite_PublicAccessBlock_18A70311: Creating...
aws_s3_bucket_policy.cloudWebsite_PublicPolicy_44BB71F3: Creating...
aws_s3_bucket_policy.cloudWebsite_PublicPolicy_44BB71F3: Creation complete after 1s [id=cloud-website-c8e58765-20230719205053652900000001]
aws_s3_bucket_public_access_block.cloudWebsite_PublicAccessBlock_18A70311: Creation complete after 1s [id=cloud-website-c8e58765-20230719205053652900000001]

Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The order of resource creation notably varied. For a deeper technical explanation of the fix, feel free to explore the corresponding &lt;a href="https://github.com/winglang/wing/pull/3536" rel="noopener noreferrer"&gt;pull request&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  In a nutshell
&lt;/h3&gt;

&lt;p&gt;The fix boils down to establishing the dependency outlined above, and it was achieved through a mere 2-line change. As for the discrepancies between operating systems, my guess is that it's tied to the availability of computing resources, although this is purely speculative. It might be illuminating to run the tests hundreds or even thousands of times to see if the error would appear on my local MacOS machine. But considering the problem is resolved now, I'm content with leaving the matter as is. You are welcome, though, to offer your speculations in the comments!&lt;/p&gt;

&lt;p&gt;Despite the relatively simple resolution, this bug proved to be particularly elusive. It's difficult to attribute it definitively in this instance. The Wing resource was likely developed before the changes to S3 permissions and probably on a Mac. Meanwhile, the integration tests are still in their infancy. As for Terraform, it's unclear whether the dependency was mandatory before AWS altered S3 bucket permissions. Nevertheless, helpful pointers in the resource documentation would have been appreciated (there is indeed an &lt;a href="https://github.com/hashicorp/terraform-provider-aws/issues/30951" rel="noopener noreferrer"&gt;issue&lt;/a&gt; open for this). Alternatively, a specific "public bucket policy" could be beneficial, though that's maybe a little too far fetched. All in all, a great example for moving targets in cloud engineering.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Lessons
&lt;/h2&gt;

&lt;p&gt;This episode embodied the perfect storm of challenges, with cryptic error messages, slight variations in behavior across different operating systems, and a dollop of my own confirmation bias. Yet, it serves as a striking example of the hurdles one might encounter in cloud development. I have no doubt that this particular issue alone consumed countless hours of troubleshooting time, not to mention the time spent grappling with IAM issues in general. The AWS Terraform provider repository hosts &lt;a href="https://github.com/hashicorp/terraform-provider-aws/issues/31363" rel="noopener noreferrer"&gt;numerous&lt;/a&gt; &lt;a href="https://github.com/hashicorp/terraform-provider-aws/issues/30951" rel="noopener noreferrer"&gt;examples&lt;/a&gt; of similar dilemmas, and AWS CDK is no stranger to such issues either.&lt;/p&gt;

&lt;p&gt;Developing in the cloud (like AWS) can be an absolute delight when everything slots seamlessly into place, freeing you from substantial operational burdens. However, when things go awry, it can feel like an insurmountable barrier. Frequently, we're left with limited error context, inadequate or non-existent logs, ever-changing targets, and outdated documentation or blog posts. &lt;/p&gt;

&lt;p&gt;Projects like Wing are making strides towards simplifying the complex task of developing and operating applications in the cloud. The cloud resource SDKs being developed encapsulate the collective hard-earned knowledge and experience, distilling it into reusable components. While the system might not be perfect or bug-free at this stage, there's an incredible team and an active, albeit small, &lt;a href="https://t.winglang.io/slack" rel="noopener noreferrer"&gt;community&lt;/a&gt; at the ready to offer assistance.&lt;/p&gt;

&lt;p&gt;Finally, if I could make one request of AWS, it would be to offer greater error context surrounding permission issues. If not directly in the API call error message, then at least within CloudTrail. I'm convinced that such a feature would save an unfathomable amount of engineering hours daily. &lt;/p&gt;

&lt;p&gt;Perhaps such a feature already exists, and I'm just not aware of it? Please let me know in the comments! I'm also eager to hear about your experiences with similar challenges. &lt;/p&gt;

</description>
      <category>aws</category>
      <category>iac</category>
      <category>cloud</category>
      <category>terraform</category>
    </item>
    <item>
      <title>Cloud Driven Development - Episode #001</title>
      <dc:creator>Sebastian Korfmann</dc:creator>
      <pubDate>Mon, 11 Oct 2021 20:24:30 +0000</pubDate>
      <link>https://forem.com/skorfmann/cloud-driven-development-episode-001-2mej</link>
      <guid>https://forem.com/skorfmann/cloud-driven-development-episode-001-2mej</guid>
      <description>&lt;p&gt;Last week, I launched &lt;a href="https://cdd.show" rel="noopener noreferrer"&gt;Cloud Driven Development&lt;/a&gt; (CDD) as a Youtube channel - a somewhat regular live coding series covering the topic of cloud development from various point of views. The sessions are usually in a pair programming setting, with changing guests &amp;amp; topics for each episode.&lt;/p&gt;

&lt;p&gt;With CDD I want to explore developer focused tools, services and workflows beyond "Cloud Native" (which always translates to Kubernetes in my head). So, let's embrace that vendor lock-in, forget about containers for the most part and dive into a world without a local dev or testing environment.&lt;/p&gt;

&lt;p&gt;If you're up for some casual talking / live coding about a topic which fits this category, I'd be more than happy to hear from you. I explicitly want to learn about new things outside of the CDK bubble as well and have some super exciting topics in the pipeline already.&lt;/p&gt;

&lt;h2&gt;
  
  
  Async Testing with AWS CDK
&lt;/h2&gt;

&lt;p&gt;For the first episode I was super happy to have &lt;a href="https://mattmorgan.cloud" rel="noopener noreferrer"&gt;Matt Morgan&lt;/a&gt; as a guest, who wrote a very interesting &lt;a href="https://dev.to/aws-builders/testing-the-async-cloud-with-aws-cdk-33aj"&gt;blog post&lt;/a&gt; about "Async Testing with AWS CDK".&lt;/p&gt;

&lt;p&gt;As the title suggests, Matt has been exploring cloud testing strategies. The approach we're talking about in this episode, leverages Cloudformation and Custom Resources. The best part, it's not just an academic excercise but it's actively being used at his current employer &lt;a href="https://www.powerschool.com/" rel="noopener noreferrer"&gt;PowerSchool&lt;/a&gt;.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Technical Notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/elthrasher/cdk-async-testing-example" rel="noopener noreferrer"&gt;Example&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Theodo-UK/sls-test-tools" rel="noopener noreferrer"&gt;sls-test-tools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/erezrokah/aws-testing-library" rel="noopener noreferrer"&gt;aws-testing-library&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Custom Resource
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cdk/api/latest/docs/custom-resources-readme.html" rel="noopener noreferrer"&gt;Custom Resource Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/aws/aws-cdk/tree/master/packages/%40aws-cdk/custom-resources/lib/provider-framework" rel="noopener noreferrer"&gt;Provider Framework on Github&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Next Episode
&lt;/h2&gt;

&lt;p&gt;The next episode is scheduled already, where &lt;a href="https://twitter.com/theBenForce" rel="noopener noreferrer"&gt;Ben Force&lt;/a&gt; and I will build a really simple Alexa Skill with the AWS CDK. In this context particularly interesting: How to keep iteration cycles small, get fast feedback and can tests help there?&lt;/p&gt;

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

</description>
      <category>aws</category>
      <category>cdk</category>
      <category>testing</category>
    </item>
    <item>
      <title>CDK for Terraform 0.3</title>
      <dc:creator>Sebastian Korfmann</dc:creator>
      <pubDate>Thu, 22 Apr 2021 13:29:50 +0000</pubDate>
      <link>https://forem.com/skorfmann/cdk-for-terraform-0-3-59lp</link>
      <guid>https://forem.com/skorfmann/cdk-for-terraform-0-3-59lp</guid>
      <description>&lt;p&gt;The &lt;a href="https://cdk.tf" rel="noopener noreferrer"&gt;CDK (Cloud Development Kit) for Terraform&lt;/a&gt; allows developers to use familiar programming languages to define cloud infrastructure and provision it through HashiCorp Terraform. &lt;/p&gt;

&lt;p&gt;Since the &lt;a href="https://dev.to/skorfmann/cdk-for-terraform-0-2-3b71"&gt;0.2&lt;/a&gt; release roughly a month ago, by far the biggest change was the onboarding of a newly hired full-time engineer. I'm  super happy that &lt;a href="https://twitter.com/amrtns" rel="noopener noreferrer"&gt;Ansgar Mertens&lt;/a&gt; joined our team and am delighted to see the positive impact he already had on the project. &lt;/p&gt;

&lt;h2&gt;
  
  
  CDK for Terraform 0.3
&lt;/h2&gt;

&lt;p&gt;We released &lt;a href="https://github.com/hashicorp/terraform-cdk/releases/tag/v0.3.0" rel="noopener noreferrer"&gt;CDK for Terraform 0.3&lt;/a&gt; recently. Besides from various smaller and bigger improvements, there are two major new features: Remote templates and multiple stacks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Remote Templates
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;cdktf-cli&lt;/code&gt; ships with a basic &lt;a href="https://github.com/hashicorp/terraform-cdk/tree/69e077545f82dd9ee199d8da45ba84503e59da2d/packages/cdktf-cli/templates" rel="noopener noreferrer"&gt;template&lt;/a&gt; for each target language. These templates are the blueprint for the project which gets created by running &lt;code&gt;cdktf init&lt;/code&gt;, e.g. &lt;code&gt;cdktf init --template typescript&lt;/code&gt;.   &lt;/p&gt;

&lt;p&gt;With &lt;a href="https://github.com/hashicorp/terraform-cdk/blob/69e077545f82dd9ee199d8da45ba84503e59da2d/docs/working-with-cdk-for-terraform/remote-templates.md" rel="noopener noreferrer"&gt;remote templates&lt;/a&gt;, users can now bring their own template. This enables customized starting points for &lt;code&gt;cdktf&lt;/code&gt; projects tailored to the requirements of individual users and organizations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multiple Stacks
&lt;/h3&gt;

&lt;p&gt;Up until CDK for Terraform version &lt;code&gt;0.2&lt;/code&gt; only a single stack per application was supported. Starting with version &lt;code&gt;0.3&lt;/code&gt;, we're now enabling users to model their infrastructure in multiple stacks.&lt;/p&gt;

&lt;p&gt;One stack is a collection of infrastructure which will be synthesized as a dedicated Terraform configuration. In comparison to the Terraform CLI, a Stack is equal to a dedicated working directory. Stacks are therefore a boundary to separate state within your infrastructure setup. This can be leveraged to separate a &lt;code&gt;cdktf&lt;/code&gt; app in several layers (e.g. Network, Data, Compute), or to manage different versions of the same stack like in the following example.&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;import&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="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;constructs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&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="nx"&gt;TerraformStack&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cdktf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AwsProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Instance&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@cdktf/provider-aws&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;MyStackConfig&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;environment&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="nl"&gt;region&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="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyStack&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;TerraformStack&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;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MyStackConfig&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;us-east-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;

    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AwsProvider&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="s2"&gt;aws&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;region&lt;/span&gt;&lt;span class="p"&gt;,&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;Instance&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="s2"&gt;Hello&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;ami&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ami-2757f631&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;instanceType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;t2.micro&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&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;app&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;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;MyStack&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="s2"&gt;multiple-stacks-dev&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;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dev&lt;/span&gt;&lt;span class="dl"&gt;'&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;MyStack&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="s2"&gt;multiple-stacks-staging&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;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;staging&lt;/span&gt;&lt;span class="dl"&gt;'&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;MyStack&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="s2"&gt;multiple-stacks-production-us&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;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;us-east-1&lt;/span&gt;&lt;span class="dl"&gt;'&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;MyStack&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="s2"&gt;multiple-stacks-production-eu&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;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;eu-central-1&lt;/span&gt;&lt;span class="dl"&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;/div&gt;


&lt;p&gt;There are two limitations to using multiple stacks at the moment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Operations such as &lt;code&gt;diff&lt;/code&gt;, &lt;code&gt;deploy&lt;/code&gt; and &lt;code&gt;destroy&lt;/code&gt; are limited to one stack at a time for now - See this &lt;a href="https://github.com/hashicorp/terraform-cdk/issues/650" rel="noopener noreferrer"&gt;issue&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Referencing a resource across stacks can't be done automatically, but has to be built intentionally by the user - See this &lt;a href="https://github.com/hashicorp/terraform-cdk/issues/651" rel="noopener noreferrer"&gt;issue&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both limitations will be addressed in future releases. &lt;/p&gt;
&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;Speaking of future releases, here's a &lt;a href="https://github.com/hashicorp/terraform-cdk/milestone/8" rel="noopener noreferrer"&gt;rough plan&lt;/a&gt; what we'll be working on next. This includes the highest voted issue - Support for &lt;a href="https://github.com/hashicorp/terraform-cdk/issues/368" rel="noopener noreferrer"&gt;Go&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the meantime, please checkout what we've built so far and make yourself &lt;a href="https://learn.hashicorp.com/tutorials/terraform/cdktf?in=terraform/cdktf" rel="noopener noreferrer"&gt;familiar&lt;/a&gt; with CDK for Terraform :) &lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/hashicorp" rel="noopener noreferrer"&gt;
        hashicorp
      &lt;/a&gt; / &lt;a href="https://github.com/hashicorp/terraform-cdk" rel="noopener noreferrer"&gt;
        terraform-cdk
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Define infrastructure resources using programming constructs and provision them using HashiCorp Terraform
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/hashicorp/terraform-cdk/workflows/Release/badge.svg"&gt;&lt;img src="https://github.com/hashicorp/terraform-cdk/workflows/Release/badge.svg" alt=""&gt;&lt;/a&gt;
&lt;a href="https://badge.fury.io/js/cdktf" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/d7ea763960785dc621bc3ce07dc9a664dcbd96e1770c5325fbc175033229aec6/68747470733a2f2f62616467652e667572792e696f2f6a732f63646b74662e737667" alt="npm version"&gt;&lt;/a&gt;
&lt;a href="https://badge.fury.io/py/cdktf" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/4539c8efeaf7757de49d31a9ad68259692eed3e8b597269e62ec4c6d7a05c44a/68747470733a2f2f62616467652e667572792e696f2f70792f63646b74662e737667" alt="PyPI version"&gt;&lt;/a&gt;
&lt;a href="https://badge.fury.io/nu/HashiCorp.Cdktf" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/a89db6d3a83e35553e66d92d1401308216c99ed570bca12ac50fcd8dd0927ea7/68747470733a2f2f62616467652e667572792e696f2f6e752f4861736869436f72702e43646b74662e737667" alt="NuGet version"&gt;&lt;/a&gt;
&lt;a href="https://search.maven.org/artifact/com.hashicorp/cdktf" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/ec301b177b74d3d54dd0ce08879cbf915295e98d125949e4645982ec4a1caed8/68747470733a2f2f696d672e736869656c64732e696f2f6d6176656e2d63656e7472616c2f762f636f6d2e6861736869636f72702f63646b74663f636f6c6f723d627269676874677265656e" alt="Maven Central"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;CDK for Terraform&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;Cloud Development Kit for Terraform (CDKTF) allows you to use familiar
programming languages to define cloud infrastructure and provision it through
HashiCorp Terraform. This gives you access to the entire Terraform ecosystem without learning HashiCorp Configuration Language (HCL) and lets you leverage the power of your existing toolchain for testing, dependency management, etc.&lt;/p&gt;
&lt;p&gt;We currently support TypeScript, Python, Java, C#, and Go.&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/hashicorp/terraform-cdk./docs/terraform-platform.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fhashicorp%2Fterraform-cdk.%2Fdocs%2Fterraform-platform.png" alt="terraform platform"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;CDKTF includes two packages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/hashicorp/terraform-cdk./packages/cdktf-cli" rel="noopener noreferrer"&gt;cdktf-cli&lt;/a&gt; - A CLI that allows users to run commands to initialize, import, and synthesize CDK for Terraform applications.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/hashicorp/terraform-cdk./packages/cdktf" rel="noopener noreferrer"&gt;cdktf&lt;/a&gt; - A library for defining Terraform resources using programming constructs.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Get Started&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Choose a language:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.hashicorp.com/terraform/tutorials/cdktf/cdktf-build?in=terraform%2Fcdktf&amp;amp;variants=cdk-language%3Atypescript" rel="nofollow noopener noreferrer"&gt;TypeScript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.hashicorp.com/terraform/tutorials/cdktf/cdktf-build?in=terraform%2Fcdktf&amp;amp;variants=cdk-language%3Apython" rel="nofollow noopener noreferrer"&gt;Python&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.hashicorp.com/terraform/tutorials/cdktf/cdktf-build?in=terraform%2Fcdktf&amp;amp;variants=cdk-language%3Ajava" rel="nofollow noopener noreferrer"&gt;Java&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.hashicorp.com/terraform/tutorials/cdktf/cdktf-build?in=terraform%2Fcdktf&amp;amp;variants=cdk-language%3Acsharp" rel="nofollow noopener noreferrer"&gt;C#&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.hashicorp.com/terraform/tutorials/cdktf/cdktf-build?in=terraform%2Fcdktf&amp;amp;variants=cdk-language%3Ago" rel="nofollow noopener noreferrer"&gt;Go&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Hands-on:&lt;/strong&gt; Try the tutorials in the &lt;a href="https://learn.hashicorp.com/collections/terraform/cdktf" rel="nofollow noopener noreferrer"&gt;CDK for Terraform&lt;/a&gt; collection on HashiCorp Learn.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Documentation&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Refer to the &lt;a href="https://developer.hashicorp.com/terraform/cdktf" rel="nofollow noopener noreferrer"&gt;CDKTF documentation&lt;/a&gt; for more detail about how to build and manage CDKTF applications, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.hashicorp.com/terraform/cdktf/concepts/cdktf-architecture" rel="nofollow noopener noreferrer"&gt;Application Architecture&lt;/a&gt;: Learn the tools and processes that CDKTF…&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/hashicorp/terraform-cdk" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


</description>
      <category>terraform</category>
      <category>cdk</category>
      <category>cdktf</category>
      <category>iac</category>
    </item>
    <item>
      <title>CDK for Terraform 0.2</title>
      <dc:creator>Sebastian Korfmann</dc:creator>
      <pubDate>Wed, 17 Mar 2021 09:45:14 +0000</pubDate>
      <link>https://forem.com/skorfmann/cdk-for-terraform-0-2-3b71</link>
      <guid>https://forem.com/skorfmann/cdk-for-terraform-0-2-3b71</guid>
      <description>&lt;p&gt;The &lt;a href="https://cdk.tf" rel="noopener noreferrer"&gt;CDK (Cloud Development Kit) for Terraform&lt;/a&gt; allows developers to use familiar programming languages to define cloud infrastructure and provision it through HashiCorp Terraform. &lt;/p&gt;

&lt;p&gt;We released &lt;a href="https://github.com/hashicorp/terraform-cdk/releases/tag/v0.2.0" rel="noopener noreferrer"&gt;CDK for Terraform 0.2&lt;/a&gt; recently. Besides from various smaller and bigger improvements, the focus was on iterating on the support for Terraform modules. &lt;/p&gt;

&lt;p&gt;Up until now, we did support generating code bindings for Terraform Modules which are published on the &lt;a href="https://registry.terraform.io/browse/modules" rel="noopener noreferrer"&gt;Terraform Registry&lt;/a&gt; only. The Terraform Registry API provides all &lt;a href="https://github.com/hashicorp/terraform-cdk/blob/6c3ceb1742600e0d3f552225339aea91006ccc87/packages/cdktf-cli/lib/get/registry-client.ts#L101" rel="noopener noreferrer"&gt;the metadata&lt;/a&gt; we needed to generate code straight from the API response without having to process the Terraform module sources.&lt;/p&gt;

&lt;p&gt;With the new 0.2 release, we changed the internal implementation for fetching modules to support all &lt;a href="https://www.terraform.io/docs/language/modules/sources.html" rel="noopener noreferrer"&gt;sources&lt;/a&gt; which Terraform supports. This includes Modules from Github, HTTP and local disk. Since we're not relying on the Terraform Registry API anymore, we had to come up with our own solution to extract the relevant metadata. &lt;/p&gt;

&lt;p&gt;The main challenge here was: CDK for Terraform is primarily written in Typescript, while most of the Terraform ecosystem relies on Go. To bridge this gap, &lt;a href="https://webassembly.org/" rel="noopener noreferrer"&gt;WebAssembly&lt;/a&gt; came to the rescue. It enabled us to leverage existing tools from the Go community by building a &lt;a href="https://github.com/hashicorp/terraform-cdk/tree/main/packages/%40cdktf/hcl2json" rel="noopener noreferrer"&gt;thin wrapper&lt;/a&gt; in Typescript. More details about the learnings from that journey in one of the next posts :) &lt;/p&gt;

&lt;p&gt;In the meantime, please go ahead and checkout the latest release of &lt;a href="https://cdk.tf" rel="noopener noreferrer"&gt;CDK for Terraform&lt;/a&gt;. We're always looking for feedback and ideas to drive the product forward.&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>cdktf</category>
      <category>cdk</category>
      <category>cloud</category>
    </item>
    <item>
      <title>AWS AppSync Direct Lambda vs DynamoDB Resolver</title>
      <dc:creator>Sebastian Korfmann</dc:creator>
      <pubDate>Tue, 17 Nov 2020 14:45:07 +0000</pubDate>
      <link>https://forem.com/skorfmann/aws-appsync-direct-lambda-vs-dynamodb-resolver-2596</link>
      <guid>https://forem.com/skorfmann/aws-appsync-direct-lambda-vs-dynamodb-resolver-2596</guid>
      <description>&lt;p&gt;I've been experimenting with AWS AppSync and I'm intrigued by the idea of ditching VTL entirely in favour of Direct Lambda Resolvers. Ignoring the monetary cost aspect which Lambda adds, my biggest question was the performance impact this might have. So, I ended up doing some basic real world tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;A direct lambda resolver for the field &lt;code&gt;Query.getNote&lt;/code&gt; and a VTL DynamoDB resolver for &lt;code&gt;Query.ddbGetNote&lt;/code&gt;. Both versions are performing a &lt;code&gt;GetItem&lt;/code&gt; operation on the same DynamoDB table.&lt;/p&gt;

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

&lt;p&gt;The first try with a Lambda function mostly in AWS CDK default configuration (i.e. 128 MB Ram) was quite disappointing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5nnzo46aia07aor4gvhc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5nnzo46aia07aor4gvhc.png" alt="Alt Text" width="800" height="266"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The (warm) Lambda resolver took ~ 200 ms compared to 14 ms of the VTL DynamoDB call. Interestingly enough, most time consuming in the Lambda function appears to be the DynamoDB call. This seems odd.&lt;/p&gt;

&lt;h2&gt;
  
  
  Back to the drawing board?
&lt;/h2&gt;

&lt;p&gt;A bit of research suggested, that this is an issue for underpowered Lambda functions. I've found this &lt;a href="https://pages.awscloud.com/rs/112-TZM-766/images/2020_0316-SRV_Slide-Deck.pdf" rel="noopener noreferrer"&gt;slide deck&lt;/a&gt; with a nice chart for balancing performance / costs for DynamoDB calls in Lambda functions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fnrjkdy1azqs0tq245i2o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fnrjkdy1azqs0tq245i2o.png" alt="Alt Text" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The sweet spot seems to be around 512 MB memory for a Lambda function.&lt;/p&gt;

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

&lt;p&gt;I changed the Lambda function to 512 MB and left everything else as it was. And surprise, surprise - This looks way better now. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0n2tp28qacoq3frsq94r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0n2tp28qacoq3frsq94r.png" alt="Alt Text" width="800" height="302"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now the (warm) Lambda resolver takes ~ 30 ms compared to still 14 ms of the VTL DynamoDB call. &lt;/p&gt;

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

&lt;p&gt;Skipping VTL in favour of direct Lambda resolvers seems to be reasonable for the gained flexibility, even though there are a few drawbacks. There's a bit of an overhead to call the Lambda function, which gets drastically worse on cold starts (+ ~ 600 ms). &lt;/p&gt;

&lt;p&gt;In terms of costs, 1 million invocations at 100 ms with 512 MB would clock in at roughly 1 USD. That's on top of the 4 USD per million operations in AppSync. All in all, not exactly cheap in scale but nothing I'd be concerned about for now. That's something to optimize when costs would become an issue.&lt;/p&gt;

&lt;p&gt;I'll continue to explore Direct Lambda Resolvers for all cases and report how it goes :)&lt;/p&gt;

</description>
      <category>aws</category>
      <category>appsync</category>
      <category>graphql</category>
      <category>lambda</category>
    </item>
    <item>
      <title>cdk.dev - Call for Contributors</title>
      <dc:creator>Sebastian Korfmann</dc:creator>
      <pubDate>Thu, 13 Aug 2020 14:03:32 +0000</pubDate>
      <link>https://forem.com/skorfmann/cdk-dev-call-for-contributors-4c46</link>
      <guid>https://forem.com/skorfmann/cdk-dev-call-for-contributors-4c46</guid>
      <description>&lt;p&gt;&lt;a href="https://twitter.com/mattbonig" rel="noopener noreferrer"&gt;Matthew Bonig&lt;/a&gt; (&lt;a href="https://openconstructfoundation.org/" rel="noopener noreferrer"&gt;Open Construct Foundation&lt;/a&gt;), &lt;a href="https://twitter.com/mikebild" rel="noopener noreferrer"&gt;Mike Bild&lt;/a&gt; and &lt;a href="https://twitter.com/skorfmann" rel="noopener noreferrer"&gt;I&lt;/a&gt; are joining forces to build out &lt;a href="https://cdk.dev" rel="noopener noreferrer"&gt;cdk.dev&lt;/a&gt; as a content hub around the emerging Cloud Development Kit (CDK) ecosystem. With AWS CDK, cdk8s and Terraform CDK the CDK family has now three members, and I'm sure it'll continue to grow. Not only in regards to core products, but ancillary products, tools and services as well. &lt;/p&gt;

&lt;h3&gt;
  
  
  Our Mission
&lt;/h3&gt;

&lt;p&gt;We set out to aggregate content and events, provide a home for little tools and last but not least, become a hub for ideas and questions around the CDK ecosystem.&lt;/p&gt;

&lt;p&gt;Our short term goals include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build a simple content aggregator for blog posts, tweets and events&lt;/li&gt;
&lt;li&gt;Build a dedicated section for educational content &lt;/li&gt;
&lt;li&gt;Build a project show case listing for inspiration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While we think our mission is aligned with the goals of AWS (&lt;a href="https://aws.amazon.com/cdk/" rel="noopener noreferrer"&gt;AWS-CDK&lt;/a&gt; / &lt;a href="https://cdk8s.io/" rel="noopener noreferrer"&gt;cdk8s&lt;/a&gt;) and HashiCorp (&lt;a href="https://cdk.tf" rel="noopener noreferrer"&gt;cdktf&lt;/a&gt;), this project is not affiliated with those companies. It's a private effort coming from us and whoever joins along the way.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fljppstyfzp21ss7mgxoc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fljppstyfzp21ss7mgxoc.png" alt="Alt Text" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Current State
&lt;/h3&gt;

&lt;p&gt;We created a new &lt;a href="https://github.com/cdk-dev" rel="noopener noreferrer"&gt;Github Organization&lt;/a&gt; with one &lt;a href="https://github.com/cdk-dev/base" rel="noopener noreferrer"&gt;dedicated repository&lt;/a&gt; for organizing ourselves. A simple &lt;a href="https://github.com/cdk-dev/website" rel="noopener noreferrer"&gt;landing page&lt;/a&gt; was setup and &lt;a href="https://cdk.dev/" rel="noopener noreferrer"&gt;is live&lt;/a&gt; already. &lt;/p&gt;

&lt;p&gt;Other than that, I opened up my private &lt;a href="https://cdk-dev.slack.com/" rel="noopener noreferrer"&gt;Slack channel&lt;/a&gt; - which I used as a feedback channel for early adaptors of &lt;a href="https://cdk.tf" rel="noopener noreferrer"&gt;Terraform CDK&lt;/a&gt;. Feel free to &lt;a href="https://join.slack.com/t/cdk-dev/shared_invite/zt-gff3dtkw-MsEPa5Id1Aey8HQUDEck1Q" rel="noopener noreferrer"&gt;grab an invite&lt;/a&gt; and join. The Slack chat can be used for ad-hoc discussions, whereas more thorough and detailed planning will happen in the Github repository.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/cdk-dev" rel="noopener noreferrer"&gt;
        cdk-dev
      &lt;/a&gt; / &lt;a href="https://github.com/cdk-dev/base" rel="noopener noreferrer"&gt;
        base
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Repo for organizing things around cdk.dev
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;cdk.dev - The Cloud Development Kit (CDK) Ecosystem Hub&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;This is a community driven project for the Cloud Development Kit (CDK) and will cover the entire CDK ecosystem:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/cdk/" rel="nofollow noopener noreferrer"&gt;AWS CDK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cdk8s.io" rel="nofollow noopener noreferrer"&gt;CDK for Kubernetes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cdk.tf" rel="nofollow noopener noreferrer"&gt;Terraform CDK&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It's a place to bring up and discuss ideas, plan the next steps and provide feedback.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Mission&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;We build a dedicated hub for people interested in the CDK ecosystem.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Goals&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Create a content hub for CDK topics&lt;/li&gt;
&lt;li&gt;List upcoming events&lt;/li&gt;
&lt;li&gt;Provide examples and howtos&lt;/li&gt;
&lt;li&gt;Provide a space to discuss and exchange ideas&lt;/li&gt;
&lt;li&gt;Provide helpful tools&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Website Repo&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/cdk-dev/website" rel="noopener noreferrer"&gt;https://github.com/cdk-dev/website&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/cdk-dev/base" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h3&gt;
  
  
  Join Us
&lt;/h3&gt;

&lt;p&gt;We're committing to build and steer this project in public and would love to get more people involved early on. If you're keen on joining us, &lt;a href="https://join.slack.com/t/cdk-dev/shared_invite/zt-gff3dtkw-MsEPa5Id1Aey8HQUDEck1Q" rel="noopener noreferrer"&gt;grab a Slack invite&lt;/a&gt; and get in touch :)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://join.slack.com/t/cdk-dev/shared_invite/zt-gff3dtkw-MsEPa5Id1Aey8HQUDEck1Q" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fognfk85h8vkk11x22m2g.png" alt="Alt Text" width="800" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cdk</category>
      <category>cdk8s</category>
      <category>cdktf</category>
      <category>awscdk</category>
    </item>
    <item>
      <title>A Terraform CDK Construct which doubles as native Terraform Module</title>
      <dc:creator>Sebastian Korfmann</dc:creator>
      <pubDate>Mon, 27 Jul 2020 11:12:31 +0000</pubDate>
      <link>https://forem.com/skorfmann/a-terraform-cdk-construct-which-doubles-as-native-terraform-module-447k</link>
      <guid>https://forem.com/skorfmann/a-terraform-cdk-construct-which-doubles-as-native-terraform-module-447k</guid>
      <description>&lt;p&gt;Lou &lt;a href="https://dev.to/loujaybee/should-you-use-typescript-to-write-terraform-the-terraform-cdk-cm"&gt;raised the question&lt;/a&gt; of Terraform Module interoperability in the Terraform CDK:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Module Interoperability — It seems the CDK will support regular Terraform modules, for code sharing. But it does remain to be seen if module sharing can be reversed. Can a CDK project publish a module which is then consumed by HCL? If you’d have to learn HCL to write and share a module, that mostly defeats the point of using the CDK in the first place.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's a great question, and while I was confident this is something which could be done today already, I thought this would be a good example to actually build out. So, that's what I did and I'd like to share with you.&lt;/p&gt;

&lt;h2&gt;
  
  
  General Concept
&lt;/h2&gt;

&lt;p&gt;The Terraform CDK is synthesizing from code to HCL compatible JSON. This makes it less a question of &lt;em&gt;if&lt;/em&gt; it's possible to, but more like a question of &lt;em&gt;how&lt;/em&gt; to organize the code and how to distribute it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resource vs TerraformResource
&lt;/h3&gt;

&lt;p&gt;The concept I came up with includes a &lt;a href="https://github.com/skorfmann/cdktf-hybrid-module/blob/7a84cbea62fbc3c3b7e92c00d75fcaad495cf29b/packages/cdktf-hybrid-module/lib/construct.ts" rel="noopener noreferrer"&gt;custom construct&lt;/a&gt;, which is sort of the equivalent of a native Terraform module in the Terraform CDK. That's just a Typescript class, which wraps other Terraform constructs (an EC2 &lt;code&gt;Instance&lt;/code&gt; in this case). It could contain more constructs of course, they could be nested, whatever you can imagine - it's all just Typescript in the end.&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;import&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="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;constructs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Resource&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cdktf&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Instance&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../imports/providers/aws&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;CustomInstanceProps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;instanceType&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="nl"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;{[&lt;/span&gt;&lt;span class="na"&gt;key&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="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomInstance&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Resource&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Instance&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;name&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;CustomInstanceProps&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;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;instanceType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;t3.nano&lt;/span&gt;&lt;span class="dl"&gt;"&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="o"&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="nx"&gt;instance&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;Instance&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;ubuntu2&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;ami&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ami-0ff8a91507f77f867&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;availabilityZone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;us-east-1a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;instanceType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;tags&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In contrast to the generated &lt;code&gt;Instance&lt;/code&gt; class, the &lt;code&gt;CustomInstance&lt;/code&gt; class extends &lt;a href="https://github.com/hashicorp/terraform-cdk/blob/5becfbc699180adfe920dec794200bbf56dda0a7/packages/cdktf/lib/resource.ts" rel="noopener noreferrer"&gt;Resource&lt;/a&gt; and not &lt;a href="https://github.com/hashicorp/terraform-cdk/blob/master/packages/cdktf/lib/terraform-resource.ts" rel="noopener noreferrer"&gt;TerraformResource&lt;/a&gt;. While &lt;code&gt;CustomInstance&lt;/code&gt; is still a node in the &lt;a href="https://github.com/aws/constructs" rel="noopener noreferrer"&gt;Constructs&lt;/a&gt; tree, the &lt;code&gt;CustomInstance&lt;/code&gt; class itself will be skipped during synth. Only the actual &lt;code&gt;TerraformResource&lt;/code&gt; classes will be rendered down to HCL compatible JSON. It's really just a container for actual Terraform resources.&lt;/p&gt;
&lt;h3&gt;
  
  
  Distribution
&lt;/h3&gt;
&lt;h4&gt;
  
  
  NPM Package
&lt;/h4&gt;

&lt;p&gt;Since our &lt;code&gt;CustomInstance&lt;/code&gt; is a Typescript class, distributing this as a NPM package is straightforward. Make sure to setup the following keys in your &lt;code&gt;package.json&lt;/code&gt; and off you go:&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="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"path/to/your/construct.js"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"path/to/your/construct.d.ts"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"peerDependencies"&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;"cdktf"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.0.12"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"constructs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^3.0.0"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Declaring &lt;code&gt;peerDependencies&lt;/code&gt; and not &lt;code&gt;dependencies&lt;/code&gt; is the important point here. Otherwise, you'd likely end up in dependency hell.&lt;/p&gt;
&lt;h4&gt;
  
  
  Terraform Module
&lt;/h4&gt;

&lt;p&gt;In order to distribute this as a Terraform module, this should be synthesized to JSON somehow. Let's build a Stack without a provider and that'll pretty much be synthesized to the equivalent of a Terraform module.&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;import&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="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;constructs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&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="nx"&gt;TerraformStack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TerraformOutput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Token&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cdktf&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CustomInstance&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./construct&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyStack&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;TerraformStack&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;name&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="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;name&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;custom&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;CustomInstance&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;Custom&lt;/span&gt;&lt;span class="dl"&gt;'&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;TerraformOutput&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;arn&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;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;custom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&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;Rather than ignoring the the synthesized output in Git, let's &lt;a href="https://github.com/skorfmann/cdktf-hybrid-module/tree/master/packages/cdktf-hybrid-module/module" rel="noopener noreferrer"&gt;commit this&lt;/a&gt; and configure &lt;code&gt;cdktf&lt;/code&gt; to use a nicer folder name (&lt;code&gt;module&lt;/code&gt; in this case) for its output:&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;"language"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"typescript"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"app"&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 --silent compile &amp;amp;&amp;amp; node lib/module.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"terraformProviders"&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;"aws@~&amp;gt; 2.0"&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;"codeMakerOutput"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"imports"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"output"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"module"&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;For the native Terraform module use-case it would be nice to omit the &lt;a href="https://github.com/skorfmann/cdktf-hybrid-module/blob/7a84cbea62fbc3c3b7e92c00d75fcaad495cf29b/packages/cdktf-hybrid-module/module/cdk.tf.json#L19-L32" rel="noopener noreferrer"&gt;stack traces&lt;/a&gt; in the generated JSON, since that'll change depending on where it's build. A little bit of &lt;code&gt;jq&lt;/code&gt; could be helpful here&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cat ./cdktf.out/cdk.tf.json | jq 'walk(if type == "object" then with_entries(select(.key | test("\/\/") | not)) else . end)'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Mid / long term, native support would be better though. Check out this &lt;a href="https://github.com/hashicorp/terraform-cdk/issues/251" rel="noopener noreferrer"&gt;open issue here&lt;/a&gt;.&lt;/p&gt;
&lt;h5&gt;
  
  
  Input Variables
&lt;/h5&gt;

&lt;p&gt;&lt;a href="https://www.terraform.io/docs/configuration/variables.html" rel="noopener noreferrer"&gt;Terraform Input Variables&lt;/a&gt; are not natively supported by the Terraform CDK yet. There's an &lt;a href="https://github.com/hashicorp/terraform-cdk/issues/249" rel="noopener noreferrer"&gt;open issue&lt;/a&gt; to change this. However, that's a case where &lt;a href="https://cdk.tf/escape-hatch" rel="noopener noreferrer"&gt;escape hatches&lt;/a&gt; come in handy:&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="nx"&gt;stack&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;MyStack&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;cdktf-hybrid-module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// See issue linked above, this will be natively supported&lt;/span&gt;
&lt;span class="nx"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addOverride&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;variable&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;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Tags for the instance&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;map(string)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;instance_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Instance type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;From here on, it's really useable as any other Terraform module:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&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;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"github.com/skorfmann/cdktf-hybrid-module//packages/cdktf-hybrid-module/module"&lt;/span&gt;

  &lt;span class="nx"&gt;instance_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t3.nano"&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"CDKTF"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"IS AWESOME"&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;h4&gt;
  
  
  Bonus: Terraform Module via NPM
&lt;/h4&gt;

&lt;p&gt;One thing I really like about NPM: It doesn't make any assumptions about what you're intending to ship. Let's leverage this, to use the native Terraform module via NPM :)&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm init -y
npm install cdktf-hybrid-module
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And then just reference it from &lt;code&gt;node_modules&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"instance"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"./node_modules/cdktf-hybrid-module/module"&lt;/span&gt;

  &lt;span class="nx"&gt;instance_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t3.nano"&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"CDKTF"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"IS AWESOME"&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;That could be a way, to get dependency management for native Terraform modules via NPM. I'm certainly not the first one who had this idea, I'm pretty sure I saw this in other blog posts as well. &lt;/p&gt;

&lt;p&gt;I think this could make sense in complex scenarios, where dependency management is important and manually managing this becomes a burden.&lt;/p&gt;
&lt;h2&gt;
  
  
  Current limitations
&lt;/h2&gt;

&lt;p&gt;As mentioned a few times, there are a few usability gaps at the moment: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Outputs are already fully supported by &lt;code&gt;cdktf&lt;/code&gt; but due to the random naming a bit hard to use. There' an &lt;a href="https://github.com/hashicorp/terraform-cdk/issues/247" rel="noopener noreferrer"&gt;open issue&lt;/a&gt; to address this&lt;/li&gt;
&lt;li&gt;Variables aren't natively supported in &lt;code&gt;cdktf&lt;/code&gt; yet, but can still be done with &lt;a href="https://cdkt.tf/escape-hatche" rel="noopener noreferrer"&gt;escape hatches&lt;/a&gt;. There's an &lt;a href="https://github.com/hashicorp/terraform-cdk/issues/249" rel="noopener noreferrer"&gt;open issue&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Since there aren't official prebuilt provider packages at the moment, this has to inline the &lt;a href="https://github.com/skorfmann/cdktf-hybrid-module/tree/7a84cbea62fbc3c3b7e92c00d75fcaad495cf29b/packages/cdktf-hybrid-module/imports/providers/aws" rel="noopener noreferrer"&gt;generated constructs&lt;/a&gt;. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last point is the biggest drawback at the moment from my point of view, but the work to improve this is underway - see this &lt;a href="https://github.com/hashicorp/terraform-cdk/issues/98" rel="noopener noreferrer"&gt;open issue&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I think this demonstrates that hybrid CDK packages / Terraform modules are totally possible and have lots of future potential. Would love to hear what you think about it!&lt;/p&gt;

&lt;p&gt;Check out the entire example project as well to see all of this in full context.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/skorfmann" rel="noopener noreferrer"&gt;
        skorfmann
      &lt;/a&gt; / &lt;a href="https://github.com/skorfmann/cdktf-hybrid-module" rel="noopener noreferrer"&gt;
        cdktf-hybrid-module
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A Terraform CDK Construct which is also usable as Terraform Module 
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;In the next post we'll build a Python package for our little &lt;code&gt;CustomInstance&lt;/code&gt;, stay tuned!&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>cdk</category>
      <category>cdktf</category>
      <category>devops</category>
    </item>
    <item>
      <title>The Journey from Terrastack to Terraform CDK</title>
      <dc:creator>Sebastian Korfmann</dc:creator>
      <pubDate>Thu, 23 Jul 2020 16:58:42 +0000</pubDate>
      <link>https://forem.com/skorfmann/the-journey-from-terrastack-to-terraform-cdk-5c3d</link>
      <guid>https://forem.com/skorfmann/the-journey-from-terrastack-to-terraform-cdk-5c3d</guid>
      <description>&lt;p&gt;The initial version of &lt;a href="https://skorfmann.com/blog/introducing-terrastack/" rel="noopener noreferrer"&gt;Terrastack&lt;/a&gt; was built over the course of a few days in March 2020. It was built around the same concepts as the &lt;a href="https://aws.amazon.com/cdk/" rel="noopener noreferrer"&gt;AWS CDK&lt;/a&gt; and shared a similar vision: Leveraging imperative programming languages such as Typescript, Python, Java and C# to build out infrastructure with the Terraform engine.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1237848600354254849-746" src="https://platform.twitter.com/embed/Tweet.html?id=1237848600354254849"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1237848600354254849-746');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1237848600354254849&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;While my intention with the prototype was to raise awareness and get support for further development, I didn’t anticipate pushing at an open door.&lt;/p&gt;

&lt;p&gt;Fast forward four months, the successor of Terrastack - the &lt;a href="https://github.com/hashicorp/terraform-cdk" rel="noopener noreferrer"&gt;Terraform CDK&lt;/a&gt; -  got &lt;a href="https://www.hashicorp.com/blog/cdk-for-terraform-enabling-python-and-typescript-support/" rel="noopener noreferrer"&gt;launched&lt;/a&gt; as a joint effort by Hashicorp, the AWS CDK team and me :) It turned out that Hashicorp and the AWS CDK team started to work together on the exact same idea long before I published Terrastack. They reached out to me and I’m still very grateful for the opportunity to join the ongoing project as an independent contributor.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/hashicorp" rel="noopener noreferrer"&gt;
        hashicorp
      &lt;/a&gt; / &lt;a href="https://github.com/hashicorp/terraform-cdk" rel="noopener noreferrer"&gt;
        terraform-cdk
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Define infrastructure resources using programming constructs and provision them using HashiCorp Terraform
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/hashicorp/terraform-cdk/workflows/Release/badge.svg"&gt;&lt;img src="https://github.com/hashicorp/terraform-cdk/workflows/Release/badge.svg" alt=""&gt;&lt;/a&gt;
&lt;a href="https://badge.fury.io/js/cdktf" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/d7ea763960785dc621bc3ce07dc9a664dcbd96e1770c5325fbc175033229aec6/68747470733a2f2f62616467652e667572792e696f2f6a732f63646b74662e737667" alt="npm version"&gt;&lt;/a&gt;
&lt;a href="https://badge.fury.io/py/cdktf" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/4539c8efeaf7757de49d31a9ad68259692eed3e8b597269e62ec4c6d7a05c44a/68747470733a2f2f62616467652e667572792e696f2f70792f63646b74662e737667" alt="PyPI version"&gt;&lt;/a&gt;
&lt;a href="https://badge.fury.io/nu/HashiCorp.Cdktf" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/a89db6d3a83e35553e66d92d1401308216c99ed570bca12ac50fcd8dd0927ea7/68747470733a2f2f62616467652e667572792e696f2f6e752f4861736869436f72702e43646b74662e737667" alt="NuGet version"&gt;&lt;/a&gt;
&lt;a href="https://search.maven.org/artifact/com.hashicorp/cdktf" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/ec301b177b74d3d54dd0ce08879cbf915295e98d125949e4645982ec4a1caed8/68747470733a2f2f696d672e736869656c64732e696f2f6d6176656e2d63656e7472616c2f762f636f6d2e6861736869636f72702f63646b74663f636f6c6f723d627269676874677265656e" alt="Maven Central"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;CDK for Terraform&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Cloud Development Kit for Terraform (CDKTF) allows you to use familiar
programming languages to define cloud infrastructure and provision it through
HashiCorp Terraform. This gives you access to the entire Terraform ecosystem without learning HashiCorp Configuration Language (HCL) and lets you leverage the power of your existing toolchain for testing, dependency management, etc.&lt;/p&gt;
&lt;p&gt;We currently support TypeScript, Python, Java, C#, and Go.&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/hashicorp/terraform-cdk./docs/terraform-platform.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fhashicorp%2Fterraform-cdk.%2Fdocs%2Fterraform-platform.png" alt="terraform platform"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;CDKTF includes two packages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/hashicorp/terraform-cdk./packages/cdktf-cli" rel="noopener noreferrer"&gt;cdktf-cli&lt;/a&gt; - A CLI that allows users to run commands to initialize, import, and synthesize CDK for Terraform applications.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/hashicorp/terraform-cdk./packages/cdktf" rel="noopener noreferrer"&gt;cdktf&lt;/a&gt; - A library for defining Terraform resources using programming constructs.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Get Started&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Choose a language:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.hashicorp.com/terraform/tutorials/cdktf/cdktf-build?in=terraform%2Fcdktf&amp;amp;variants=cdk-language%3Atypescript" rel="nofollow noopener noreferrer"&gt;TypeScript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.hashicorp.com/terraform/tutorials/cdktf/cdktf-build?in=terraform%2Fcdktf&amp;amp;variants=cdk-language%3Apython" rel="nofollow noopener noreferrer"&gt;Python&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.hashicorp.com/terraform/tutorials/cdktf/cdktf-build?in=terraform%2Fcdktf&amp;amp;variants=cdk-language%3Ajava" rel="nofollow noopener noreferrer"&gt;Java&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.hashicorp.com/terraform/tutorials/cdktf/cdktf-build?in=terraform%2Fcdktf&amp;amp;variants=cdk-language%3Acsharp" rel="nofollow noopener noreferrer"&gt;C#&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.hashicorp.com/terraform/tutorials/cdktf/cdktf-build?in=terraform%2Fcdktf&amp;amp;variants=cdk-language%3Ago" rel="nofollow noopener noreferrer"&gt;Go&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Hands-on:&lt;/strong&gt; Try the tutorials in the &lt;a href="https://learn.hashicorp.com/collections/terraform/cdktf" rel="nofollow noopener noreferrer"&gt;CDK for Terraform&lt;/a&gt; collection on HashiCorp Learn.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Documentation&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Refer to the &lt;a href="https://developer.hashicorp.com/terraform/cdktf" rel="nofollow noopener noreferrer"&gt;CDKTF documentation&lt;/a&gt; for more detail about how to build and manage CDKTF applications, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://developer.hashicorp.com/terraform/cdktf/concepts/cdktf-architecture" rel="nofollow noopener noreferrer"&gt;Application Architecture&lt;/a&gt;: Learn the tools and processes that CDKTF…&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/hashicorp/terraform-cdk" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The community preview of the Terraform CDK is considered an alpha version. There are lots of rough edges which we’ll iron out over the next few months. Nevertheless, the sentiment of the feedback has been quite positive so far. There are more than 600 stars on Github, a bunch of contributions to the Github repository and roughly &lt;a href="https://www.npmjs.com/package/cdktf" rel="noopener noreferrer"&gt;1500 downloads&lt;/a&gt; via NPM within one week after the launch. That’s fantastic and makes me excited about the future of the Terraform CDK.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1284820925452615686-841" src="https://platform.twitter.com/embed/Tweet.html?id=1284820925452615686"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1284820925452615686-841');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1284820925452615686&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;Here’s a little real world example, which I migrated from Terrastack to Terraform CDK. &lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/skorfmann" rel="noopener noreferrer"&gt;
        skorfmann
      &lt;/a&gt; / &lt;a href="https://github.com/skorfmann/cdkweekly" rel="noopener noreferrer"&gt;
        cdkweekly
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Terraform CDK SSL Proxy for cdkweekly.com
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Terraform CDK SSL Proxy for cdkweekly.com&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;The &lt;a href="https://www.cdkweekly.com/" rel="nofollow noopener noreferrer"&gt;CDK Weekly&lt;/a&gt; newsletter is served by &lt;a href="https://www.getrevue.co/" rel="nofollow noopener noreferrer"&gt;revue.co&lt;/a&gt;, which doesn't support SSL for custom domains.&lt;/p&gt;
&lt;p&gt;This little workaround was initially built on top of &lt;a href="https://github.com/terrastackio/terrastack" rel="noopener noreferrer"&gt;Terrastack&lt;/a&gt; and got migrated to its successor &lt;a href="https://github.com/hashicorp/terraform-cdk/" rel="noopener noreferrer"&gt;Terraform CDK&lt;/a&gt;. It's the first real world example for the Terraform CDK and is running in production for a while now.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Setup&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;npm install
cdktf get
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Deploy&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;cdktf deploy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That's where one of the &lt;a href="https://github.com/hashicorp/terraform-cdk/tree/master/examples/typescript-aws-cloudfront-proxy" rel="noopener noreferrer"&gt;Terraform CDK examples&lt;/a&gt; is stemming from.&lt;/p&gt;
&lt;p&gt;Next Up:&lt;/p&gt;
&lt;ul class="contains-task-list"&gt;
&lt;li class="task-list-item"&gt;
 Redirect naked domain -&amp;gt; www (probably including SSL via Cloudfront distribution)&lt;/li&gt;
&lt;li class="task-list-item"&gt;
 Inject Google Analytics or something similar&lt;/li&gt;
&lt;li class="task-list-item"&gt;
 Migrate state to Terraform Cloud&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/skorfmann/cdkweekly" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;I’m currently working on yet another example, which will be a bit more elaborate and demonstrate a more complex use case.&lt;/p&gt;

&lt;p&gt;When you’re interested to learn more about the Terraform CDK, here are some resources to get you started:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.hashicorp.com/terraform/cdktf/cdktf-intro" rel="noopener noreferrer"&gt;https://learn.hashicorp.com/terraform/cdktf/cdktf-intro&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hashicorp/terraform-cdk/tree/master/docs" rel="noopener noreferrer"&gt;https://github.com/hashicorp/terraform-cdk/tree/master/docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have feedback or feature requests, the &lt;a href="https://github.com/hashicorp/terraform-cdk" rel="noopener noreferrer"&gt;Github repository&lt;/a&gt; is a good starting point. We try to follow up on newly created issues quickly. I’d love to hear from you.&lt;/p&gt;




&lt;p&gt;ps: The actual &lt;a href="https://github.com/TerraStackIO/terrastack-old" rel="noopener noreferrer"&gt;first version&lt;/a&gt; of Terrastack dates back to the end of 2018 and was sort of &lt;a href="https://github.com/gruntwork-io/terragrunt" rel="noopener noreferrer"&gt;Terragrunt&lt;/a&gt; on steroids powered by Typescript. I had lots of fun building that prototype back then together with &lt;a href="https://twitter.com/andreas_litt" rel="noopener noreferrer"&gt;Andreas Litt&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;pps: If you're interested to keep up with what's happening in the CDK ecosystem, you should consider signing up to &lt;a href="https://www.cdkweekly.com/" rel="noopener noreferrer"&gt;cdkweekly&lt;/a&gt;&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>cdk</category>
      <category>cdktf</category>
      <category>aws</category>
    </item>
    <item>
      <title>Introducing Terrastack: Polyglot Terraform supercharged by the CDK</title>
      <dc:creator>Sebastian Korfmann</dc:creator>
      <pubDate>Thu, 12 Mar 2020 12:35:21 +0000</pubDate>
      <link>https://forem.com/skorfmann/introducing-terrastack-polyglot-terraform-supercharged-by-the-cdk-151a</link>
      <guid>https://forem.com/skorfmann/introducing-terrastack-polyglot-terraform-supercharged-by-the-cdk-151a</guid>
      <description>&lt;p&gt;&lt;em&gt;Note: This was published on &lt;a href="https://skorfmann.com/blog/introducing-terrastack/" rel="noopener noreferrer"&gt;my personal blog&lt;/a&gt; as well&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Terrastack enables you to keep using &lt;a href="https://terraform.io" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; as engine, while defining your resources in actual programming languages such as Typescript, Python, Java or C# - with more to come (perhaps &lt;a href="https://github.com/aws/jsii/issues/144" rel="noopener noreferrer"&gt;Ruby&lt;/a&gt;?).&lt;/p&gt;

&lt;p&gt;This is made possible by the &lt;a href="https://aws.amazon.com/cdk/" rel="noopener noreferrer"&gt;Cloud Development Kit (CDK)&lt;/a&gt; and &lt;a href="https://github.com/aws/jsii/" rel="noopener noreferrer"&gt;jsii&lt;/a&gt; for generating the polyglot libraries. While the major use-case for the CDK is generating Cloudformation configuration as YAML, it's capable of generating pretty much any configuration. &lt;/p&gt;

&lt;p&gt;The actual idea of going forward with Terrastack was inspired by yet another project which leverages the CDK / jsii combo to generate Kubernetes configuration: &lt;a href="https://github.com/awslabs/cdk8s" rel="noopener noreferrer"&gt;cdk8s&lt;/a&gt;. When I saw that project, I eventually realized that the CDK could be useful for more scenarios than just generating Cloudformation YAML.&lt;/p&gt;

&lt;p&gt;A first quick prototype was up and running in roughly two evenings. While not all types were properly generated, it was able to generate a simple but working Terraform configuration. &lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1235298514646773760-414" src="https://platform.twitter.com/embed/Tweet.html?id=1235298514646773760"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1235298514646773760-414');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1235298514646773760&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;The general workflow is: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generate a &lt;a href="https://www.terraform.io/docs/commands/providers/schema.html" rel="noopener noreferrer"&gt;JSON schema&lt;/a&gt; for a given Terraform provider (e.g. AWS or Google Cloud) with builtin Terraform command&lt;/li&gt;
&lt;li&gt;Use this schema as input for a Typescript generator, to generate classes for each Resource and Data type of that provider&lt;/li&gt;
&lt;li&gt;Compile polyglot libraries from these generated Typescript classes via jsii&lt;/li&gt;
&lt;li&gt;Define your Cloud resources in your prefered language &lt;/li&gt;
&lt;li&gt;Generate &lt;a href="https://www.terraform.io/docs/configuration/syntax-json.html" rel="noopener noreferrer"&gt;Terraform compatible JSON&lt;/a&gt; and use it as you would any other HCL based Terraform project &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When looking at these steps, that's pretty much what the CDK team does for Cloudformation. Since Terraform and Cloudformation are both declarative, it's conceptually pretty close. This makes Terraform a perfect contender for getting "CDKified".&lt;/p&gt;

&lt;p&gt;Similar to the CDK, that's how a simple Terrastack looks like right now&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


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

&lt;p&gt;Finishing up an on premise -&amp;gt; AWS migration utilising Terraform, here are the pain points we've hit on a regular basis over the last two years:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Refactoring is hard - Unless you love digging around in JSON state files&lt;/li&gt;
&lt;li&gt;Lack of good dependency management&lt;/li&gt;
&lt;li&gt;Most of the product teams, don't actually want to learn a new syntax like HCL. &lt;/li&gt;
&lt;li&gt;Terraform Modules are a primitive way to share code. Want to dynamically adapt its definition? Good luck with that.&lt;/li&gt;
&lt;li&gt;Code Distribution becomes complex in larger organisations&lt;/li&gt;
&lt;li&gt;Ensuring certain configuration standards (think of Tagging, Object Storage / VM configurations)&lt;/li&gt;
&lt;li&gt;Git Ops with small components to separate state is more complex than it should be&lt;/li&gt;
&lt;li&gt;Established Software Engineering practices like unit / integration tests are hard to implement&lt;/li&gt;
&lt;li&gt;HCL / Terraform integration in code editors such as VS Code is poor and mostly broke for us with Terraform 0.12&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Terrastack tackles all of the above by leveraging existing technologies: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Javascript ecosystem&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/cdk/" rel="noopener noreferrer"&gt;Cloud Development Kit (CDK)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/aws/jsii/" rel="noopener noreferrer"&gt;jsii&lt;/a&gt; for polyglot libraries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The engine of Terraform and its providers are quite good overall. From my point of view, most of the issues are stemming from the rather static and declarative nature of Terraform.&lt;/p&gt;

&lt;p&gt;With Terrastack we can start to treat "raw" Terraform configuration as an "assembly" artifact which we can generate in a predictable way. This enables us to tackle most of the pain points above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use existing, well established dependency management like NPM or Yarn&lt;/li&gt;
&lt;li&gt;Leverage mono repo / build tooling in the respective programming language &lt;/li&gt;
&lt;li&gt;Build upon existing testing frameworks for unit and integration tests&lt;/li&gt;
&lt;li&gt;Enjoy intellisense of your favourite language in whatever editor you're using right now
&lt;/li&gt;
&lt;li&gt;Create commonly shared packages of cloud solutions for internal, or even public usage.&lt;/li&gt;
&lt;li&gt;Share code which is aimed at the AWS CDK (e.g. Tags, IAM Policies, ECS Task Definitions, Kubernetes config?)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm still thinking about the refactoring part. My current conclusion is, that there are mainly two things are getting in the way of proper refactorings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Static naming of resources&lt;/li&gt;
&lt;li&gt;Terraform modules aka namespaces&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I strongly believe, that we can come up with a better concept in Terrastack. But that's for another blog post.&lt;/p&gt;

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

&lt;p&gt;From the initial very rough prototype, it took about a week to release a bit more polished prototype on Github.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1237848600354254849-936" src="https://platform.twitter.com/embed/Tweet.html?id=1237848600354254849"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1237848600354254849-936');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1237848600354254849&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;Please check it out and give it a try. I would love to get feedback! &lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/TerraStackIO" rel="noopener noreferrer"&gt;
        TerraStackIO
      &lt;/a&gt; / &lt;a href="https://github.com/TerraStackIO/terrastack" rel="noopener noreferrer"&gt;
        terrastack
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      This project is archived, but the idea of Terrastack lives on in the Terraform CDK. - https://github.com/hashicorp/terraform-cdk
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;This is the first post of a series regarding Terrastack. In the next post, I'll dive a bit deeper into the internals of the CDK itself and what I've learned about its mechanics so far.&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>aws</category>
      <category>cdk</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
