<?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: Mathabo Motaung</title>
    <description>The latest articles on Forem by Mathabo Motaung (@codebymathabo).</description>
    <link>https://forem.com/codebymathabo</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%2F3695140%2F9dd94e2f-a00a-4dee-85a8-ccb2e4e2082f.jpeg</url>
      <title>Forem: Mathabo Motaung</title>
      <link>https://forem.com/codebymathabo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/codebymathabo"/>
    <language>en</language>
    <item>
      <title>Decoding Base64 With PowerShell</title>
      <dc:creator>Mathabo Motaung</dc:creator>
      <pubDate>Wed, 07 Jan 2026 00:35:04 +0000</pubDate>
      <link>https://forem.com/codebymathabo/decoding-base64-with-powershell-1188</link>
      <guid>https://forem.com/codebymathabo/decoding-base64-with-powershell-1188</guid>
      <description>&lt;h2&gt;
  
  
  Using Windows PowerShell to decode base64
&lt;/h2&gt;

&lt;p&gt;Developers often encounter &lt;code&gt;Base64&lt;/code&gt; strings. You might see them in configuration files or API responses. They represent binary data in a text format.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comparison
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Linux&lt;/strong&gt; users decode these strings easily. They use the terminal command &lt;code&gt;base64&lt;/code&gt; with the decode flag. It is quick and native.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Windows&lt;/strong&gt; users often struggle with this task. &lt;code&gt;PowerShell&lt;/code&gt; does not have a direct equivalent to the &lt;code&gt;Linux command&lt;/code&gt;. You might try to find a specific &lt;code&gt;cmdlet&lt;/code&gt;. You will not find one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fact
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;PowerShell&lt;/code&gt; relies on the &lt;code&gt;.NET&lt;/code&gt; framework. This is a strength. It gives you access to powerful system libraries. You can use these libraries to manipulate data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Knowledge
&lt;/h3&gt;

&lt;p&gt;Here is the logic. &lt;code&gt;Base64&lt;/code&gt; is a string. You must convert this string into a byte array. Then you convert that byte array into readable text.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Follow these steps to decode a string:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Define your &lt;code&gt;Base64&lt;/code&gt; string. Assign it to a variable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Convert the string to bytes. Use the &lt;code&gt;System.Convert&lt;/code&gt; class. It has a static method called &lt;code&gt;FromBase64String&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$Bytes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;System.Convert&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;FromBase64String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$EncodedString&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;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; Decode the bytes. Use the &lt;code&gt;System.Text.Encoding&lt;/code&gt; class. The &lt;code&gt;UTF8 property&lt;/code&gt; works for most modern text. Use the &lt;code&gt;GetString&lt;/code&gt; method on the byte array.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;The output will be the decoded text. In this example, it is Hello World.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;This method is reliable. It works on all modern versions of &lt;code&gt;PowerShell&lt;/code&gt;. It does not require external tools. It scripts well for automation tasks.&lt;/p&gt;

&lt;p&gt;Understanding the underlying types helps you. You are not just running a black box command. You are manipulating data types directly. This is the &lt;code&gt;PowerShell&lt;/code&gt; way.&lt;/p&gt;

</description>
      <category>developer</category>
      <category>microsoft</category>
      <category>cli</category>
    </item>
    <item>
      <title>Debugging PowerShell vs. Bash for Local API Testing</title>
      <dc:creator>Mathabo Motaung</dc:creator>
      <pubDate>Tue, 06 Jan 2026 12:15:46 +0000</pubDate>
      <link>https://forem.com/codebymathabo/debugging-powershell-vs-bash-for-local-api-testing-3eh2</link>
      <guid>https://forem.com/codebymathabo/debugging-powershell-vs-bash-for-local-api-testing-3eh2</guid>
      <description>&lt;h2&gt;
  
  
  Why your curl commands fail on Windows, and how to fix them without installing WSL.
&lt;/h2&gt;

&lt;p&gt;If you are a Java developer moving between Linux servers and a local Windows environment, you have probably hit this wall. You copy a perfectly valid curl command from a tutorial or documentation, paste it into your PowerShell terminal, and get a wall of red text.&lt;/p&gt;

&lt;p&gt;I ran into this while testing S3entinel, my Spring Boot file ingestion service. I wanted to manually test my new Controller endpoint, but my terminal refused to cooperate.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Setup
&lt;/h3&gt;

&lt;p&gt;I had just deployed a FileController in Spring Boot that accepts multipart/form-data uploads.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@PostMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/upload"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;uploadFile&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@RequestParam&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;MultipartFile&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ... logic to handle file bytes&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;accepted&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"File accepted for processing."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To test it quickly without opening Postman, I ran this standard command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="s2"&gt;"file=@wb.jpg"&lt;/span&gt; http://localhost:8080/api/v1/files/upload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Error
&lt;/h3&gt;

&lt;p&gt;Instead of a 202 Accepted response, PowerShell threw an exception I didn't expect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Invoke-WebRequest : A parameter cannot be found that matches parameter name 'X'.
At line:1 char:6
+ curl -X POST -F "file=@wb.jpg" ...
+      ~~
    + CategoryInfo          : InvalidArgument: (:) [Invoke-WebRequest], ParameterBindingException
    + FullyQualifiedErrorId : NamedParameterNotFound,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The "Aha!" Moment
&lt;/h3&gt;

&lt;p&gt;The error message &lt;code&gt;Invoke-WebRequest&lt;/code&gt; gives it away.&lt;/p&gt;

&lt;p&gt;In PowerShell, &lt;code&gt;curl&lt;/code&gt; is not the actual cURL binary we know and love from Linux. It is an Alias for a native Windows cmdlet called &lt;code&gt;Invoke-WebRequest&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;While &lt;code&gt;Invoke-WebRequest&lt;/code&gt; is powerful, it does not share the same syntax flags as standard cURL:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It doesn't use &lt;code&gt;-X&lt;/code&gt; for methods (it uses &lt;code&gt;-Method&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;It doesn't use &lt;code&gt;-F&lt;/code&gt; for form data (it uses &lt;code&gt;-Form&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, when I typed &lt;code&gt;curl -X&lt;/code&gt;, PowerShell thought I was passing a parameter named &lt;code&gt;-X&lt;/code&gt; to &lt;code&gt;Invoke-WebRequest&lt;/code&gt;, which doesn't exist.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Fix: Be Explicit
&lt;/h3&gt;

&lt;p&gt;You don't need to rewrite your command into PowerShell syntax (which is verbose). You just need to tell PowerShell to ignore its alias and use the actual executable installed on your system.&lt;/p&gt;

&lt;p&gt;By appending &lt;code&gt;.exe&lt;/code&gt;, you bypass the alias lookup and run the program directly.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Working Command
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;curl.exe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-v&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-X&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;POST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"file=@wb.jpg"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;http://localhost:8080/api/v1/files/upload&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Why this works:
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;curl&lt;/code&gt;: PowerShell sees an alias for &lt;code&gt;Invoke-WebRequest&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;curl.exe&lt;/code&gt;: PowerShell looks for an executable file in your System PATH. Since Git Bash or Windows System32 usually installs the real cURL, it finds the Linux-compatible tool.&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;When working in hybrid environments (Windows Local + Linux Cloud), "simple" commands often break due to shell differences.&lt;/p&gt;

&lt;p&gt;If you see &lt;code&gt;Invoke-WebRequest&lt;/code&gt; errors when you meant to run &lt;code&gt;curl&lt;/code&gt;, remember: &lt;strong&gt;When in doubt, add&lt;/strong&gt; &lt;code&gt;.exe&lt;/code&gt;.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Code Reference:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/CodeByMathabo/S3entinel.git" rel="noopener noreferrer"&gt;GitHub Repository: S3entinel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/CodeByMathabo/S3entinel/blob/main/src/main/java/com/codebymathabo/s3entinel/controller/FileController.java" rel="noopener noreferrer"&gt;FileController.java&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>java</category>
      <category>api</category>
      <category>testing</category>
      <category>architecture</category>
    </item>
    <item>
      <title>How to Fix CloudFormation Circular Dependencies in AWS SAM</title>
      <dc:creator>Mathabo Motaung</dc:creator>
      <pubDate>Tue, 06 Jan 2026 00:03:13 +0000</pubDate>
      <link>https://forem.com/codebymathabo/how-to-fix-cloudformation-circular-dependencies-in-aws-sam-f8l</link>
      <guid>https://forem.com/codebymathabo/how-to-fix-cloudformation-circular-dependencies-in-aws-sam-f8l</guid>
      <description>&lt;h2&gt;
  
  
  The "Chicken and Egg" problem when connecting S3 Buckets to Lambda Triggers.
&lt;/h2&gt;

&lt;p&gt;If you have ever tried to build an Event-Driven architecture with AWS SAM, you have likely seen this error message. It usually appears 5 minutes into a deployment, just when you think everything is working:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Error: Failed to create changeset for the stack: s3entinel-stack&lt;br&gt;
Status: FAILED. Reason: Circular dependency between resources: &lt;br&gt;
[ImageProcessorFunctionRole, ImageProcessorFunction, S3entinelBucket, ImageProcessorFunctionFileUploadPermission]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I hit this wall while building S3entinel, a Spring Boot + Serverless hybrid application. Here is exactly why this happens and the simple Sub-trick I used to fix it.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Architecture
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;The goal was simple:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;S3 Bucket (S3entinelBucket) receives a file.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Lambda Function (ImageProcessorFunction) gets triggered automatically.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Lambda reads the file metadata and logs it.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;To make this work, two things must happen in the infrastructure:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The Bucket needs to know the Lambda's ARN to send the notification.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Lambda needs permission (IAM Policy) to read from The Bucket.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  The Problem: Chicken vs. Egg
&lt;/h3&gt;

&lt;p&gt;In CloudFormation (the engine underneath SAM), resources are created in a specific dependency order.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To create the Bucket Notification, AWS needs the Lambda Function to exist first.&lt;/li&gt;
&lt;li&gt;To create the Lambda Function, AWS needs to create its IAM Role.&lt;/li&gt;
&lt;li&gt;If you reference the Bucket inside that IAM Role (to give it Read permissions), the Role now waits for the Bucket.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Cycle:&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;Bucket waits for Lambda → Lambda waits for Role → Role waits for Bucket.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The "Bad" Code (What Causes the Loop)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here is the intuitive (but wrong) way to write the template.yaml. Notice how I tried to rely on !Ref S3entinelBucket inside the function's policy.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;In YAML file:&lt;/code&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="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;S3entinelBucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::S3::Bucket&lt;/span&gt;

  &lt;span class="na"&gt;ImageProcessorFunction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless::Function&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;CodeUri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
      &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;com.codebymathabo.s3entinel.lambda.ImageProcessor::handleRequest&lt;/span&gt;
      &lt;span class="na"&gt;Events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;FileUpload&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;S3&lt;/span&gt;
          &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;S3entinelBucket&lt;/span&gt; &lt;span class="c1"&gt;# Lambda needs Bucket for Event&lt;/span&gt;
            &lt;span class="na"&gt;Events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;s3:ObjectCreated:*&lt;/span&gt;
      &lt;span class="na"&gt;Policies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;S3ReadPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;BucketName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;S3entinelBucket&lt;/span&gt; &lt;span class="c1"&gt;# Role needs Bucket for Permissions&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;This creates the deadlock. CloudFormation cannot resolve the dependency graph.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Fix: Break the Chain with Strings
&lt;/h3&gt;

&lt;p&gt;To solve this, we must remove one of the hard dependencies. We can’t change the Event trigger (that requires a real resource reference), but we can cheat on the IAM Policy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Instead of asking CloudFormation to "Wait for the Bucket to exist and then give me its name" (!Ref)&lt;/strong&gt;, we can construct the bucket name manually using a deterministic naming pattern.&lt;/p&gt;

&lt;p&gt;In AWS, if you know the Region and Account ID, you can predict the bucket name before it is even created.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The "Good" Code&lt;/strong&gt;&lt;br&gt;
Here is the fix I implemented in S3entinel. I replaced the !Ref in the Policy section with a !Sub string interpolation.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;In YAML file:&lt;/code&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="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Define the Bucket Name explicitly using ID and Region&lt;/span&gt;
  &lt;span class="na"&gt;S3entinelBucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::S3::Bucket&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;BucketName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3entinel-iac-${AWS::Region}-${AWS::AccountId}'&lt;/span&gt;

  &lt;span class="na"&gt;ImageProcessorFunction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Serverless::Function&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;CodeUri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
      &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;com.codebymathabo.s3entinel.lambda.ImageProcessor::handleRequest&lt;/span&gt;

      &lt;span class="c1"&gt;# Reconstruct the name string manually.&lt;/span&gt;
      &lt;span class="c1"&gt;# This grants permission to "what the bucket WILL be named"&lt;/span&gt;
      &lt;span class="c1"&gt;# without forcing the IAM Role to wait for the Bucket resource.&lt;/span&gt;
      &lt;span class="na"&gt;Policies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;S3ReadPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;BucketName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3entinel-iac-${AWS::Region}-${AWS::AccountId}'&lt;/span&gt;

      &lt;span class="na"&gt;Events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;FileUpload&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;S3&lt;/span&gt;
          &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
           &lt;span class="c1"&gt;# Keep the hard link for the trigger&lt;/span&gt;
            &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;S3entinelBucket&lt;/span&gt; 
            &lt;span class="na"&gt;Events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;s3:ObjectCreated:*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why This Works
&lt;/h3&gt;

&lt;p&gt;By using !Sub 's3entinel-iac-${AWS::Region}-${AWS::AccountId}', we are giving the IAM Role a string of text, not a resource dependency.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;CloudFormation creates the IAM Role immediately (it only needs the string, not the bucket).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It creates the Lambda Function using that Role.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Finally, it creates the S3 Bucket (which depends on the Lambda for the notification trigger).&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;The loop is broken, the stack deploys, and the Circular Dependency error disappears.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Code Reference:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/CodeByMathabo/S3entinel.git" rel="noopener noreferrer"&gt;Github Repository: S3entinel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/CodeByMathabo/S3entinel/blob/main/template.yaml" rel="noopener noreferrer"&gt;Full template.yaml file&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>java</category>
      <category>backend</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
