<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Daniel Schroeder</title>
    <description>The latest articles on DEV Community by Daniel Schroeder (@udondan).</description>
    <link>https://dev.to/udondan</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%2F258664%2F568ad13d-ecf7-45f9-a916-4c81c1954b19.jpeg</url>
      <title>DEV Community: Daniel Schroeder</title>
      <link>https://dev.to/udondan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/udondan"/>
    <language>en</language>
    <item>
      <title>Correctly defining CDK dependencies in L3 constructs</title>
      <dc:creator>Daniel Schroeder</dc:creator>
      <pubDate>Mon, 28 Dec 2020 21:25:17 +0000</pubDate>
      <link>https://dev.to/aws-builders/correctly-defining-dependencies-in-l3-cdk-constructs-45p</link>
      <guid>https://dev.to/aws-builders/correctly-defining-dependencies-in-l3-cdk-constructs-45p</guid>
      <description>&lt;p&gt;When creating L3 constructs for the &lt;a href="https://aws.amazon.com/cdk/" rel="noopener noreferrer"&gt;AWS CDK&lt;/a&gt;, the easiest thing to get wrong is defining dependencies. And with wrong dependency definitions you make it hard to impossible to use your package. This post will show how to correctly define CDK dependencies.&lt;/p&gt;

&lt;p&gt;As a CDK user, you already know, all CDK core packages have to be of the same version or you will get cryptic errors such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;unable to determine cloud assembly output directory. Assets must be defined indirectly within a "Stage" or an "App" scope
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Types of property 'node' are incompatible... Types have separate declarations of a private property 'host'.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So what you want to avoid when publishing L3 constructs, is to directly depend your package on any version of the core CDK packages. Still, this is the case in almost every L3 construct I have seen. I'm assuming this is because the core packages themselves do it like this and developers learn from reading code.&lt;/p&gt;

&lt;p&gt;Usually you find packages having either exact or caret versions in their &lt;code&gt;dependencies&lt;/code&gt;. Also, in most cases, these definitions are accompanied with the same items in the &lt;code&gt;peerDependencies&lt;/code&gt;. Let's analyze these two setups and what the result for the end-user is going to be:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dependencies with caret version&lt;/strong&gt;&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="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@aws-cdk/aws-lambda"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.23.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;"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;"@aws-cdk/aws-lambda"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.23.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="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The caret definition means, &lt;em&gt;install the latest minor compatible to the given version&lt;/em&gt;. That is everything &amp;lt; 2.0.0. So until CDK 2 drops, this will install the very latest package for &lt;code&gt;aws-lambda&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The end-user now can only install your package, when all application dependencies refer to the latest CDK packages as well. If CDK 1.70.0 or any other older version is used, there is no way the user can install your package without causing incompatibility between core packages. The solution from user perspective is to upgrade the CDK and deal with the potentially introduced breaking changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dependencies with exact version&lt;/strong&gt;&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="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@aws-cdk/aws-lambda"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.80.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;"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;"@aws-cdk/aws-lambda"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.80.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="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A package with this &lt;code&gt;dependencies&lt;/code&gt; definition of course is only compatible with exactly one version of the CDK. This means your users cannot upgrade the CDK without upgrading your package and vice versa. To make your package usable by future CDK versions you need to release new versions of your package for every CDK release. Most probably you do this automated. And either you have a mapping between CDK version and your package version in your docs or you use the same exact version as the upstream packages, which renders the information (&lt;a href="https://semver.org" rel="noopener noreferrer"&gt;semver&lt;/a&gt;) of &lt;em&gt;your&lt;/em&gt; version string useless. How do you progress your own code? How do you communicate bug-fixes or breaking changes? And let's not forget, you waste computational resources for compiling, transferring and storing your build artifacts without actual change.&lt;/p&gt;

&lt;p&gt;The problem though, is not the format of your dependency definition (exact vs. caret) - the problem simply is: &lt;strong&gt;You have dependencies&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And as simple as that problem statement, is the solution: &lt;strong&gt;Just don't&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd5jxy1yo7mu87tzyylu9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd5jxy1yo7mu87tzyylu9.png" alt="Thou shalt not list CDK core packages in thy dependencies"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Instead, list them in the &lt;code&gt;peerDependencies&lt;/code&gt; and &lt;code&gt;devDependencies&lt;/code&gt;. You should use caret versions, defining the minimum version &lt;strong&gt;required by your package&lt;/strong&gt;. Typically this should be the version when all your CDK dependencies went stable. If you don't know or don't care, use &lt;code&gt;^1.0.0&lt;/code&gt;.&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="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"devDependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@aws-cdk/aws-lambda"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.0.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;"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;"@aws-cdk/aws-lambda"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.0.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="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You need them in the &lt;code&gt;devDependencies&lt;/code&gt; to build your package with &lt;a href="https://aws.github.io/jsii/" rel="noopener noreferrer"&gt;jsii&lt;/a&gt; and you need them in the &lt;code&gt;peerDependencies&lt;/code&gt; to let the user know what packages need to be installed along with your package.&lt;/p&gt;

&lt;p&gt;Now, when a user installs your package, what happens depends on the used language and package manager. Either way, the user needs to define the peer-dependencies as dependencies of the application, e.g. in the &lt;code&gt;package.json&lt;/code&gt; or &lt;code&gt;requirements.txt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;npm version &amp;lt; 6&lt;/strong&gt; will warn about missing peer dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm WARN cdk-awesome-package@1.2.3 requires a peer of @aws-cdk/aws-lambda@^1.0.0 but none was installed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same goes for &lt;strong&gt;yarn&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;warning " &amp;gt; cdk-awesome-package@1.2.3" has unmet peer dependency "@aws-cdk/aws-lambda@^1.0.0".
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;strong&gt;npm 6&lt;/strong&gt; for some reason this is missing and the user will only know about the missing dependency when the application is ran. That's fine though, the error message is clear about what package needs to be installed. An entry in the &lt;code&gt;package.json&lt;/code&gt; needs to be made:&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="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@aws-cdk/aws-lambda"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.70.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cdk-awesome-package"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.2.3"&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="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Starting with &lt;strong&gt;npm version 7&lt;/strong&gt; the handling of peer dependencies has changed - they will be automatically installed and you cannot override the version in the &lt;code&gt;dependencies&lt;/code&gt; section. Instead the user now needs to add it to the &lt;code&gt;overrides&lt;/code&gt; section.&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;"dependencies"&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;"cdk-awesome-package"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.2.3"&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;"overrides"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@aws-cdk/aws-lambda"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.70.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively the user can also just switch back to the old behaviour by setting &lt;code&gt;legacy-peer-deps=true&lt;/code&gt; in the &lt;code&gt;.npmrc&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;All other languages supported by jsii don't support peer dependencies. For these languages they are converted to normal dependencies. pip correctly interprets the caret version definition of &lt;code&gt;^1.0.0&lt;/code&gt; and in return you would install the latest version. For the dotnet package, jsii converted &lt;code&gt;^1.0.0&lt;/code&gt; to &lt;code&gt;1.0.0&lt;/code&gt;...  which in the end really doesn't matter. Because this can and has to be overridden by the user anyway.&lt;/p&gt;

&lt;p&gt;With &lt;strong&gt;pip&lt;/strong&gt; the user can override the version of dependencies by simply adding them to the applications &lt;code&gt;requirements.txt&lt;/code&gt; or &lt;code&gt;setup.py&lt;/code&gt;, e.g.:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws-cdk-aws-lambda==1.70.0
cdk-awesome-package&amp;gt;=1.2.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In C# the user can also override the version in the project file, e.g.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"Amazon.CDK.AWS.LAMBDA"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"1.70.0"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"CDK.Awesome.Package"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"1.*"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I haven't checked Java but I assume the same can be archived in a gradle file. &lt;/p&gt;

&lt;p&gt;And there you go. A CDK L3 construct working with any version of core CDK packages.&lt;/p&gt;




&lt;h4&gt;
  
  
  Update 2021/04/17
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;But what about breaking changes?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I've seen some developers are automatically releasing new versions of their L3 constructs for every new CDK release for the reason of compatibility, especially when experimental CDK components are involved. I strongly disagree with this approach.&lt;/p&gt;

&lt;p&gt;First of all, if an L3 uses experimental features, then the L3, as a consequence, is experimental. It should be used with caution and breaking changes should be expected with every minor update. Most likely it also should not be used in production, unless the user knows what she/he's doing and pays the proper attention. Using experimental features means, the user might not be able to upgrade without destroying/recreating related infrastructure.&lt;/p&gt;

&lt;p&gt;Next: Ensuring compatibility with future versions of a framework is not the responsibility of a package author. If there are breaking changes, of course things will fail. The same would be true if the user directly used experimental CDK features. The responsibility of catching these is not with the L3 author, but with the user of the construct. Every user needs to be aware that the smallest change needs testing. Of course, when there are package changes, the app needs to be deployed in a test environment, ran with &lt;code&gt;diff&lt;/code&gt; and ideally even has proper jest tests in place, before it reaches your prod environment.&lt;/p&gt;

&lt;p&gt;You cannot solve testing for the user. The best you can catch by building a package per CDK version is a change in the API signature. Maybe an option has been removed or renamed and therefore your code is not compatible and won't compile. The real danger though lies in functional changes, that make the code run, but behave differently. A very good example for this would be &lt;a href="https://github.com/aws/aws-cdk/releases/tag/v1.75.0" rel="noopener noreferrer"&gt;CDK version 1.75.0&lt;/a&gt;, which included the following change:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;efs: &lt;code&gt;keyId&lt;/code&gt; property uses the ARN instead of the &lt;code&gt;keyId&lt;/code&gt; to support cross-account encryption key usage. &lt;strong&gt;The filesystem will be replaced.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;This&lt;/strong&gt; is the real danger with experimental packages and you cannot protect the user from this by building a new version of your package.&lt;/p&gt;




&lt;p&gt;Also, what's exactly the benefit of building new packages for every release? You ensure it still builds. So what if it doesn't? Your build fails, you'll be notified by your action/workflow and you need to adjust the code to match the new API, because building new packages does not automagically fix the problem. Let's assume you're knee-deep involved in other projects or actual life and you don't have time to fix it for some time. &lt;br&gt;
&lt;strong&gt;Consequence: No user will be able to upgrade the core CDK packages until you fixed your code and released a compatible package.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now imagine you had not auto-built new packages but instead used my suggested best practice? The package will not be compatible with the latest CDK version. &lt;br&gt;
&lt;strong&gt;Consequence: No user will be able to upgrade the core CDK packages until you fixed your code and released a compatible package.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The only difference would be, that you won't be notified by your failed pipeline/action and wouldn't be aware there is a breaking change. And this can easily be fixed by testing your package for new CDK releases.&lt;/p&gt;

&lt;p&gt;So instead of building and publishing new packages for absolutely no reason, you should &lt;strong&gt;test&lt;/strong&gt; your package against every new CDK release.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What about &lt;a href="https://github.com/projen/projen" rel="noopener noreferrer"&gt;projen&lt;/a&gt;?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/projen/projen/pull/581" rel="noopener noreferrer"&gt;Since 1st of March 2021&lt;/a&gt; you can set &lt;code&gt;cdkDependenciesAsDeps: false&lt;/code&gt; in your &lt;code&gt;.projenrc.js&lt;/code&gt; to prevent projen from adding the CDK packages as &lt;code&gt;dependencies&lt;/code&gt; and only list them as &lt;code&gt;peerDependencies&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cdk</category>
      <category>dependency</category>
    </item>
    <item>
      <title>Terraform workspace config organization</title>
      <dc:creator>Daniel Schroeder</dc:creator>
      <pubDate>Wed, 20 Nov 2019 15:23:55 +0000</pubDate>
      <link>https://dev.to/udondan/terraform-workspace-config-organization-42lh</link>
      <guid>https://dev.to/udondan/terraform-workspace-config-organization-42lh</guid>
      <description>&lt;p&gt;The suggested best practices for organizing configuration for multiple workspaces/environments is to call Terraform with &lt;code&gt;-var-file=$env&lt;/code&gt; to include a specific tfvars file.&lt;/p&gt;

&lt;p&gt;Of course this works. But it seems to be error prone if you allow to trigger an apply against any workspace with just any configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform workspace &lt;span class="k"&gt;select &lt;/span&gt;production
terraform apply &lt;span class="nt"&gt;-var-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;config/staging.tfvars
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Furthermore this cannot be used in Terraform Cloud, where you have to specify workspace related vars in the workspace configuration itself:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oMHXPiXT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/s1trk20rgji6tfgyldv5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oMHXPiXT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/s1trk20rgji6tfgyldv5.png" alt="Terraform Cloud Variable configuration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is no option for including tfvars per workspace.&lt;/p&gt;

&lt;h3&gt;
  
  
  Select config automatically based on the workspace
&lt;/h3&gt;

&lt;p&gt;Unfortunately there is no functionality to &lt;a href="https://github.com/hashicorp/terraform/issues/15966"&gt;automatically include a tfvars file based on the workspace name&lt;/a&gt; nor is there support for &lt;a href="https://github.com/hashicorp/terraform/issues/1478"&gt;conditionally including tfvars files&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So you got to build something yourself. You can access the current workspace name via &lt;code&gt;terraform.workspace&lt;/code&gt;. There are a couple of things you can do with this value.&lt;/p&gt;

&lt;p&gt;Below are 3 solutions which all have the same exact outcome. The defined config is stored in &lt;code&gt;local.config&lt;/code&gt; and can be access via &lt;code&gt;local.config.ec2_instance_type&lt;/code&gt; etc.&lt;/p&gt;

&lt;p&gt;All 3 solutions support default values, so you're not required to define every config option in every environment.&lt;/p&gt;

&lt;p&gt;All examples are available in &lt;a href="https://github.com/udondan/example-terraform-workspace-config"&gt;this repository&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Inline expressions to select correct config from a map
&lt;/h3&gt;

&lt;p&gt;All config is held is a single file &lt;a href="https://github.com/udondan/example-terraform-workspace-config/blob/master/inline-locals/config.tf"&gt;&lt;code&gt;config.tf&lt;/code&gt;&lt;/a&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;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;configs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nx"&gt;_defaults&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;ec2_instance_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t2.nano"&lt;/span&gt;
      &lt;span class="nx"&gt;regions&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;dev&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="c1"&gt;// use config from _defaults ^&lt;/span&gt;

    &lt;span class="nx"&gt;staging&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;ec2_instance_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t2.medium"&lt;/span&gt;
      &lt;span class="nx"&gt;regions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"eu-central-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;production&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;ec2_instance_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t2.xlarge"&lt;/span&gt;
      &lt;span class="nx"&gt;regions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"us-west-2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"eu-central-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"ap-east-1"&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="nx"&gt;config&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;merge&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;configs&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"_defaults"&lt;/span&gt;&lt;span class="err"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;configs&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;terraform&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workspace&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
  &lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Straight forward&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Cannot split config into separate files. Therefore could quickly get hard to maintain and compare environment config.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Load config from YAML files
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/udondan/example-terraform-workspace-config/tree/master/include-per-yaml-file"&gt;Directory structure:&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── config
│   ├── _defaults.yml
│   ├── dev.yml
│   ├── production.yml
│   └── staging.yml
├── config.tf
├── main.tf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example content of &lt;a href="https://github.com/udondan/example-terraform-workspace-config/blob/master/include-per-yaml-file/config/production.yml"&gt;&lt;code&gt;config/production.yml&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;ec2_instance_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;t2.xlarge&lt;/span&gt;

&lt;span class="na"&gt;regions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;us-east-1&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;us-west-2&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;eu-central-1&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ap-east-1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The config is loaded in &lt;a href="https://github.com/udondan/example-terraform-workspace-config/blob/master/include-per-yaml-file/config.tf"&gt;&lt;code&gt;config.yml&lt;/code&gt;&lt;/a&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;data&lt;/span&gt; &lt;span class="s2"&gt;"local_file"&lt;/span&gt; &lt;span class="s2"&gt;"defaults"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;filename&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${path.module}/config/_defaults.yml"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"local_file"&lt;/span&gt; &lt;span class="s2"&gt;"config"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;filename&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${path.module}/config/${terraform.workspace}.yml"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;locals&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;merge&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;yamldecode&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;local_file&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defaults&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="err"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;yamldecode&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;local_file&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
  &lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Straight forward&lt;/li&gt;
&lt;li&gt;Config for every environment resides in its own file&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;No HCL expressions are possible in the config itself&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Create a module per environment and return the config as an output
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/udondan/example-terraform-workspace-config/tree/master/include-per-module"&gt;Directory structure:&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── config
│   ├── _defaults
│   │   └── outputs.tf
│   ├── dev
│   │   └── outputs.tf
│   ├── main.tf
│   ├── production
│   │   └── outputs.tf
│   └── staging
│       └── outputs.tf
├── main.tf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In every &lt;code&gt;config/$env/outputs.tf&lt;/code&gt; a single output is defined like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/udondan/example-terraform-workspace-config/blob/master/include-per-module/config/_defaults/outputs.tf"&gt;&lt;code&gt;config/_defaults/outputs.tf&lt;/code&gt;&lt;/a&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;output&lt;/span&gt; &lt;span class="s2"&gt;"data"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;ec2_instance_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t2.nano"&lt;/span&gt;
    &lt;span class="nx"&gt;regions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s2"&gt;"us-east-1"&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;&lt;a href="https://github.com/udondan/example-terraform-workspace-config/blob/master/include-per-module/config/dev/outputs.tf"&gt;config/dev/outputs.tf&lt;/a&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;output&lt;/span&gt; &lt;span class="s2"&gt;"data"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="c1"&gt;// use config from _defaults&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/udondan/example-terraform-workspace-config/blob/master/include-per-module/config/staging/outputs.tf"&gt;config/staging/outputs.tf&lt;/a&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;output&lt;/span&gt; &lt;span class="s2"&gt;"data"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;ec2_instance_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t2.medium"&lt;/span&gt;
    &lt;span class="nx"&gt;regions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"eu-central-1"&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;&lt;a href="https://github.com/udondan/example-terraform-workspace-config/blob/master/include-per-module/config/production/outputs.tf"&gt;config/production/outputs.tf&lt;/a&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;output&lt;/span&gt; &lt;span class="s2"&gt;"data"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;ec2_instance_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t2.xlarge"&lt;/span&gt;
    &lt;span class="nx"&gt;regions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"us-west-2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"eu-central-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"ap-east-1"&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;Since you cannot use variables in a module &lt;code&gt;source&lt;/code&gt; parameter all 4 modules have to be defined in every environment. Furthermore you cannot directly access a module by name when the name is not hardcoded, so you need to additionally create a mapping like so:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/udondan/example-terraform-workspace-config/blob/master/include-per-module/config/main.tf"&gt;config/main.tf&lt;/a&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;"_defaults"&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;"./_defaults"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"dev"&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;"./dev"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"staging"&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;"./staging"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"production"&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;"./production"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;data_map&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;dev&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dev&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;staging&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;staging&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;production&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;production&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"data"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;merge&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_defaults&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;lookup&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data_map&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;terraform&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workspace&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
  &lt;span class="err"&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 the &lt;a href="https://github.com/udondan/example-terraform-workspace-config/blob/master/include-per-module/main.tf"&gt;&lt;code&gt;main.tf&lt;/code&gt;&lt;/a&gt; then the module needs to be loaded and for convenience the output gets registered as a local value:&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;"config"&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;"./config"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;locals&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;module&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Config for every environment resides in its own file&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Complex setup&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Using modules as config provider seems to be the best solution, as you can split the configuration into separate files which supports HCL expressions. The setup though is complex and requires some additional boilerplate code for every additional environment.&lt;/p&gt;

&lt;p&gt;If you have no need for HCL expressions, the YAML solution seems to be nice as it is easy to setup and IMHO is very readable to humans.&lt;/p&gt;

</description>
      <category>terraform</category>
    </item>
  </channel>
</rss>
