<?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: Yoshiyuki Watanabe</title>
    <description>The latest articles on DEV Community by Yoshiyuki Watanabe (@yoshiyuki-watanabe).</description>
    <link>https://dev.to/yoshiyuki-watanabe</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2804521%2F83c48365-3e38-48c6-bea1-ea740c897614.jpeg</url>
      <title>DEV Community: Yoshiyuki Watanabe</title>
      <link>https://dev.to/yoshiyuki-watanabe</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yoshiyuki-watanabe"/>
    <language>en</language>
    <item>
      <title>AWS DevOps Agent Hands-on: Isolating the Cause of ALB Target 5xx Between ECS and Aurora</title>
      <dc:creator>Yoshiyuki Watanabe</dc:creator>
      <pubDate>Sat, 24 Oct 2026 11:12:21 +0000</pubDate>
      <link>https://dev.to/aws-builders/aws-devops-agent-hands-on-isolating-the-cause-of-alb-target-5xx-between-ecs-and-aurora-2f4d</link>
      <guid>https://dev.to/aws-builders/aws-devops-agent-hands-on-isolating-the-cause-of-alb-target-5xx-between-ecs-and-aurora-2f4d</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;A 5xx alarm that goes off in the middle of the night is a good test for AWS DevOps Agent: how much of the work of isolating the cause can it take over? In this hands-on article, I use an ALB &lt;code&gt;HTTPCode_Target_5XX_Count&lt;/code&gt; alarm to find out.&lt;/p&gt;

&lt;p&gt;The question to isolate is whether the cause is in the backend (the app on ECS) or in the database (Aurora).&lt;/p&gt;

&lt;p&gt;Traditionally, the person who receives the alarm moves back and forth between ALB metrics, application logs, and Aurora metrics and Performance Insights to find the cause. I check how much of this work can be handed to AWS DevOps Agent, using two failure scenarios whose causes sit in different layers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test Environment and Scenarios
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Environment
&lt;/h3&gt;

&lt;p&gt;I assume that an Agent Space for DevOps Agent has already been created. For the steps, see the official documentation &lt;a href="https://docs.aws.amazon.com/devopsagent/latest/userguide/getting-started-with-aws-devops-agent-creating-an-agent-space.html" rel="noopener noreferrer"&gt;Creating an Agent Space&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The settings used in this article are as follows.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Agent Space Name: &lt;code&gt;alb-5xx-demo&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Agent response language: &lt;code&gt;English (US)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Configure primary account role: &lt;code&gt;Auto-create a new DevOps Agent role&lt;/code&gt; (&lt;code&gt;DevOpsAgentRole-AgentSpace-*&lt;/code&gt; is created)&lt;/li&gt;
&lt;li&gt;Enable web app &amp;gt; Configure current account role: &lt;code&gt;Auto-create a new DevOps Agent role&lt;/code&gt; (&lt;code&gt;DevOpsAgentRole-WebappAdmin-*&lt;/code&gt; is created)&lt;/li&gt;
&lt;li&gt;Everything else: default values&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The environment assumes an e-commerce site and is built as follows.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The ALB forwards requests to a Flask app on ECS Fargate&lt;/li&gt;
&lt;li&gt;The app reads the &lt;code&gt;products&lt;/code&gt; table (3 million rows) in Aurora MySQL&lt;/li&gt;
&lt;li&gt;The ALB health check path &lt;code&gt;/health&lt;/code&gt; does not look at the state of the DB; it returns 200 as long as the app can respond&lt;/li&gt;
&lt;li&gt;The app writes JSON logs to CloudWatch Logs. Aurora has Performance Insights enabled and also outputs the slow query log (&lt;code&gt;long_query_time&lt;/code&gt; set to 2 seconds)&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;HTTPCode_Target_5XX_Count&lt;/code&gt; alarm is passed to the DevOps Agent &lt;code&gt;CreateBacklogTask&lt;/code&gt; API through EventBridge and Lambda, which starts an investigation automatically&lt;/li&gt;
&lt;/ul&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxog68mhepxhjfawlazfj.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxog68mhepxhjfawlazfj.png" alt="Architecture of the test environment" width="799" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Telling Where a 5xx Comes From
&lt;/h3&gt;

&lt;p&gt;ALB 5xx responses fall into two kinds: those returned by the ALB itself (&lt;code&gt;HTTPCode_ELB_5XX_Count&lt;/code&gt;) and those returned by a target and relayed by the ALB (&lt;code&gt;HTTPCode_Target_5XX_Count&lt;/code&gt;). This article covers the latter.&lt;/p&gt;

&lt;p&gt;The main causes of 5xx responses returned by the ALB itself are summarized in the AWS documentation "Troubleshoot your Application Load Balancers".&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-troubleshooting.html" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;docs.aws.amazon.com&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  Scenarios
&lt;/h3&gt;

&lt;p&gt;Scenario A: the cause is in the backend (a latent application bug)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Root cause: a product with no price (&lt;code&gt;NULL&lt;/code&gt;) is registered, and the function that calculates the sale price (&lt;code&gt;sale_price()&lt;/code&gt;) has no guard for &lt;code&gt;NULL&lt;/code&gt;, so the price calculation on the top page raises a &lt;code&gt;TypeError&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A recent deployment is not the cause&lt;/li&gt;
&lt;li&gt;The conclusions I expect from DevOps Agent are to identify the stack trace and the ID of the product whose price is &lt;code&gt;NULL&lt;/code&gt; (&lt;code&gt;product_id&lt;/code&gt;), to find that a recent deployment is not the cause, and to find that the Aurora metrics are normal&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Scenario B: the cause is in the DB (a full scan by a search query)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Root cause: the product search query &lt;code&gt;LIKE '%keyword%' ORDER BY created_at DESC&lt;/code&gt; cannot use an index and scans all 3 million rows, which pins Aurora CPU&lt;/li&gt;
&lt;li&gt;The conclusions I expect from DevOps Agent are to confirm the CPU spike, to identify the top SQL, and to point out the missing index&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Starting a DevOps Agent Investigation Automatically from an Alarm
&lt;/h2&gt;

&lt;p&gt;When the alarm goes into &lt;code&gt;ALARM&lt;/code&gt;, an EventBridge rule calls Lambda. Lambda then creates an investigation task (&lt;code&gt;INVESTIGATION&lt;/code&gt;) through the DevOps Agent &lt;code&gt;CreateBacklogTask&lt;/code&gt; API.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://docs.aws.amazon.com/devopsagent/latest/APIReference/API_CreateBacklogTask.html" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;docs.aws.amazon.com&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;I chose this approach based on an AWS re:Post article. The reasons are that it authenticates with an IAM role, so no long-lived secret is needed, and that it is simpler than a webhook.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://repost.aws/articles/ARnrvREIynRsKAdzRwYVF1_A/automating-aws-devops-agent-investigation-from-incident-detection-and-response-alarms" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fa0.awsstatic.com%2Flibra-css%2Fimages%2Flogos%2Faws_logo_smile_1200x630.png" height="420" class="m-0" width="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://repost.aws/articles/ARnrvREIynRsKAdzRwYVF1_A/automating-aws-devops-agent-investigation-from-incident-detection-and-response-alarms" rel="noopener noreferrer" class="c-link"&gt;
            Automating AWS DevOps Agent investigation from Incident Detection and Response alarms | AWS re:Post
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Learn how to route AWS Incident Detection and Response alarms to AWS DevOps Agent for instant, autonomous investigation when an alarm activates.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Frepost.aws%2Ffavicon-32x32.png" width="32" height="32"&gt;
          repost.aws
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;A configuration that uses a webhook (HMAC signature) also exists, and the official AWS blogs introduce it more often. A webhook suits cases where an investigation is started from an external monitoring tool or a system outside AWS.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://docs.aws.amazon.com/devopsagent/latest/userguide/configuring-integrations-and-knowledge-invoking-devops-agent-through-webhook.html" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;docs.aws.amazon.com&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  How to Read Investigation Results
&lt;/h2&gt;

&lt;p&gt;The results of a DevOps Agent investigation are viewed in a dedicated web app. In the &lt;code&gt;Agent Spaces&lt;/code&gt; list, I chose &lt;code&gt;Launch via IAM&lt;/code&gt; for the target Agent Space, and the web app opened.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffpjnxrxl70w7u3fprz13.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffpjnxrxl70w7u3fprz13.png" alt="Agent Spaces list: open the web app with Launch via IAM" width="799" height="165"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The list of investigations appears under &lt;code&gt;Incidents&lt;/code&gt; in the left navigation of the web app.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4xv2x4427pkq3fp3r2o0.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4xv2x4427pkq3fp3r2o0.png" alt="Incident Response Dashboard: list of investigations" width="799" height="629"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you select an investigation, you can check the results in five tabs: &lt;code&gt;Summary&lt;/code&gt;, &lt;code&gt;Investigation timeline&lt;/code&gt;, &lt;code&gt;Root cause&lt;/code&gt;, &lt;code&gt;Mitigation plan&lt;/code&gt;, and &lt;code&gt;Review&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi21ovtby7nr3kml5w4n7.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi21ovtby7nr3kml5w4n7.png" alt="Investigation screen: five tabs" width="800" height="154"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The tabs show the following.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Summary&lt;/code&gt;: a summary of the cause and the mitigation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Investigation timeline&lt;/code&gt;: a record of the steps the Agent took in the investigation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Root cause&lt;/code&gt;: the impact, the root causes, and the key findings&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Mitigation plan&lt;/code&gt;: the steps of the mitigation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Review&lt;/code&gt;: a quality evaluation of a completed investigation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you run &lt;code&gt;Review&lt;/code&gt; with &lt;code&gt;Run review&lt;/code&gt;, you get a verdict on whether the investigation was helpful and a result for each criterion. The five criteria are &lt;code&gt;Scope Alignment&lt;/code&gt;, &lt;code&gt;Evidence Grounding&lt;/code&gt;, &lt;code&gt;Investigation Completeness&lt;/code&gt;, &lt;code&gt;Environment Impact&lt;/code&gt;, and &lt;code&gt;Investigation Accuracy&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario A: A Latent Application Bug
&lt;/h2&gt;

&lt;p&gt;I reproduce a situation where a product is registered without entering its price.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Register a product without a price (simulating a missed input at registration)&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$APP&lt;/span&gt;&lt;span class="s2"&gt;/admin/products"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Admin-Token: &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"name":"【新作】防水ワイヤレスイヤホン","discount_rate":0.1}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This product appears in "New arrivals" on the top page, which shows the 10 newest products by &lt;code&gt;id&lt;/code&gt;, so only the top page returns 500. Product detail pages keep returning 200. To simulate user traffic, I sent random requests, about 30% to the top page and about 70% to product detail pages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;seq &lt;/span&gt;1 300&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;RANDOM &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt; &lt;span class="nt"&gt;-lt&lt;/span&gt; 3 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/"&lt;/span&gt;
  &lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/products/&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;RANDOM &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="m"&gt;1000&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;fi
  &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"%{http_code} "&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$APP$path&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;sleep&lt;/span&gt; &lt;span class="s2"&gt;"0.&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;RANDOM &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After a while, the 5xx alarm went off and a DevOps Agent investigation started. The investigation finished in about 10 minutes 30 seconds.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsr4cekspr4ru9f0liz4j.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsr4cekspr4ru9f0liz4j.png" alt="Incident Response Dashboard: list of investigations" width="800" height="138"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;code&gt;Incident&lt;/code&gt;, &lt;code&gt;Cause&lt;/code&gt;, and &lt;code&gt;Mitigation&lt;/code&gt; are gathered on one screen. None of them appeared to contain errors.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Incident&lt;/code&gt;: all the 5xx responses came from the backend, and there were zero 5xx responses from the ALB itself. The problem has already subsided&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Cause&lt;/code&gt;: the root cause is the combination of a product whose price is &lt;code&gt;NULL&lt;/code&gt; (&lt;code&gt;product_id&lt;/code&gt; is 3932101) and the missing &lt;code&gt;NULL&lt;/code&gt; guard in &lt;code&gt;sale_price()&lt;/code&gt;. ECS and Aurora are ruled out as causes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Mitigation&lt;/code&gt;: the immediate action is to fix the &lt;code&gt;NULL&lt;/code&gt; price, and the permanent action is a &lt;code&gt;NULL&lt;/code&gt; fix in &lt;code&gt;sale_price()&lt;/code&gt; plus a check at registration&lt;/li&gt;
&lt;/ul&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F03w7jnmga0pghbhfcvwb.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F03w7jnmga0pghbhfcvwb.png" alt="Summary: summary of the cause and the mitigation" width="800" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The scale of the failure is shown with number cards and a graph. &lt;code&gt;Peak 5xx/min&lt;/code&gt; is 89, and &lt;code&gt;Aurora status&lt;/code&gt; is &lt;code&gt;available&lt;/code&gt;. The graph also shows that 2xx responses were returned at the same time as the 5xx responses.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffctaib5k54d4brwqqsi1.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffctaib5k54d4brwqqsi1.png" alt="Summary: number cards and graph" width="800" height="277"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Investigation timeline
&lt;/h3&gt;

&lt;p&gt;You can see DevOps Agent having sub-agents investigate in parallel. They look at the following.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The application logs&lt;/li&gt;
&lt;li&gt;The Aurora metrics (including Performance Insights), the Aurora error log, and the slow query log&lt;/li&gt;
&lt;li&gt;The ECS task metrics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ECS CPU peaked at about 10% and memory at about 7%, so a lack of ECS resources was ruled out at this point. I found no errors in this part.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl5pdyay0zzjpo2aebbmm.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl5pdyay0zzjpo2aebbmm.png" alt="Investigation timeline: parallel investigation by sub-agents, and ECS ruled out" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Root cause
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Root cause&lt;/code&gt; tab is organized into four parts. The root cause was identified correctly. The explanation of the trigger, however, was inaccurate.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Impact&lt;/code&gt;: the ALB targets returned 5xx and crossed the alarm threshold&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Root causes&lt;/code&gt;: the combination of "a product has a &lt;code&gt;NULL&lt;/code&gt; price (a data defect)" and "&lt;code&gt;sale_price()&lt;/code&gt; has no &lt;code&gt;NULL&lt;/code&gt; guard (a code defect)"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Key findings&lt;/code&gt;: Aurora was judged not to be the cause&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Investigation gaps&lt;/code&gt;: how the &lt;code&gt;NULL&lt;/code&gt; price got in. It could not be traced because no code repository or data pipeline is connected&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Across the whole investigation, the Agent went as far as the following.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;From the stack trace in the application log, it identified the &lt;code&gt;TypeError&lt;/code&gt;, product ID &lt;code&gt;3932101&lt;/code&gt;, &lt;code&gt;sale_price()&lt;/code&gt; at &lt;code&gt;/app/app.py:50&lt;/code&gt;, and the top page handler. It also showed that the 5xx count and the application error count match 1:1 minute by minute&lt;/li&gt;
&lt;li&gt;It judged that the most recent deployment, which finished about 1.5 hours earlier, was not the trigger of this failure&lt;/li&gt;
&lt;li&gt;It judged that Aurora was not the cause, based on many indicators, including connection count, &lt;code&gt;db.load.avg&lt;/code&gt;, CPU, latency, buffer cache hit ratio, and the error log and slow query log&lt;/li&gt;
&lt;li&gt;It also explained why 200 and 500 alternate: requests that do not include product &lt;code&gt;3932101&lt;/code&gt; in the new arrivals return 200&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The trigger was explained as "a traffic burst ... exposed the latent defect". The actual trigger was the registration of a product without a price.&lt;/p&gt;

&lt;p&gt;Also, there is no sign that DevOps Agent queried the DB directly. The conclusion that "a product has a &lt;code&gt;NULL&lt;/code&gt; price" was an inference from the &lt;code&gt;TypeError&lt;/code&gt; message and the logs. The conclusion was correct.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwufjxqdp9pf1fjamjw4w.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwufjxqdp9pf1fjamjw4w.png" alt="Root cause: the root cause, and Aurora ruled out" width="800" height="843"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Mitigation plan
&lt;/h3&gt;

&lt;p&gt;The SQL to repair the data and the risk warnings are written in stages (pre-check, apply, post-check, rollback). It proposed repairing the rows with a &lt;code&gt;NULL&lt;/code&gt; price and handling &lt;code&gt;NULL&lt;/code&gt; in &lt;code&gt;sale_price()&lt;/code&gt;, with an execution plan, and the approach was sound. The column name in the SQL, however, was wrong.&lt;/p&gt;

&lt;p&gt;The top of the plan has an overview. The approach is to repair the &lt;code&gt;NULL&lt;/code&gt; price and add a &lt;code&gt;NULL&lt;/code&gt; guard to &lt;code&gt;sale_price()&lt;/code&gt;. It also says that, because the data and code defects remain, the same error will recur in the next burst, and that rollback does not apply because there has been only one deployment.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0imicemi6jxpshr42g57.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0imicemi6jxpshr42g57.png" alt="Mitigation plan: overview of the proposal" width="800" height="278"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The apply step (&lt;code&gt;Step 2: Apply&lt;/code&gt;) shows SQL that updates the &lt;code&gt;NULL&lt;/code&gt; price inside a transaction. It comes with warnings: do not enter an arbitrary price but use a price confirmed by the product owner, and check that exactly one row is updated before &lt;code&gt;COMMIT&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fekf4jruiicf8g6l7xcij.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fekf4jruiicf8g6l7xcij.png" alt="Mitigation plan: SQL to repair the data" width="800" height="230"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The column name in the repair SQL was wrong. It said &lt;code&gt;WHERE product_id = 3932101&lt;/code&gt;, but the primary key of the actual table is &lt;code&gt;id&lt;/code&gt;. When I actually ran it, it failed with &lt;code&gt;ERROR 1054 (42S22): Unknown column 'product_id' in 'where clause'&lt;/code&gt;. In the application log, the product ID is output as a field named &lt;code&gt;product_id&lt;/code&gt;. The Agent probably used that field name as the column name as it was. A person needs to check the SQL before running it.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpgljxequcg5sckysrz39.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpgljxequcg5sckysrz39.png" alt="Result of running the Agent's SQL in MySQL: the column does not exist" width="788" height="441"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario A Result
&lt;/h3&gt;

&lt;p&gt;All three conclusions I expected (identifying the stack trace and the product ID, finding that a recent deployment is not the cause, and finding that Aurora is normal) were obtained. The errors were in the explanation of the trigger and in the column name of the SQL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario B: A Full Scan by a Search Query
&lt;/h2&gt;

&lt;p&gt;I put parallel load on product search (&lt;code&gt;/search&lt;/code&gt;) to pin the Aurora CPU. The search uses &lt;code&gt;name LIKE '%keyword%'&lt;/code&gt;, which starts with a wildcard, so an index cannot be used and it scans all 3 million rows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hey &lt;span class="nt"&gt;-z&lt;/span&gt; 5m &lt;span class="nt"&gt;-c&lt;/span&gt; 30 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$APP&lt;/span&gt;&lt;span class="s2"&gt;/search?q=%E3%82%B1%E3%83%BC%E3%82%B9"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As in Scenario A, after a while the 5xx alarm went off and a DevOps Agent investigation started. The investigation finished in about 15 minutes 11 seconds.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmlxpt6joog3kn55kdcxn.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmlxpt6joog3kn55kdcxn.png" alt="Incident Response Dashboard: list of investigations" width="799" height="160"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;code&gt;Incident&lt;/code&gt;, &lt;code&gt;Cause&lt;/code&gt;, and &lt;code&gt;Mitigation&lt;/code&gt; contain the following. The root cause and the mitigation were correct, but the summary included a misidentification: it treated the Lambda that starts investigations as the source of the load.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Incident&lt;/code&gt;: it says 5xx bursts repeated on a cycle of about 20 minutes. The peak was about 5,330 per minute, and about 97% of the failures were &lt;code&gt;1040&lt;/code&gt; (connection refused) and about 3% were &lt;code&gt;2013&lt;/code&gt; (read timeout). Between bursts, the targets recover on their own&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Cause&lt;/code&gt;: the root cause is a full scan of about 3 million rows by a &lt;code&gt;LIKE&lt;/code&gt; that starts with a wildcard. This one statement accounts for 100% of the DB load and causes CPU saturation and connection exhaustion at the same time&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Mitigation&lt;/code&gt;: the permanent fix is to make &lt;code&gt;/search&lt;/code&gt; index-friendly, a connection pool and a concurrency cap, and a server-side timeout. The stopgap is to add a &lt;code&gt;FULLTEXT&lt;/code&gt; index and scale up&lt;/li&gt;
&lt;/ul&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Favxnf8j3zcltr025wh42.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Favxnf8j3zcltr025wh42.png" alt="Summary: summary of the cause and the mitigation" width="800" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At the end of &lt;code&gt;Cause&lt;/code&gt;, it says that an "intentional demo load generator Lambda" is the source of the load that caused the 5xx. This is wrong.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This Lambda starts investigations and does not generate any load. The actual source was &lt;code&gt;hey&lt;/code&gt; on my own machine&lt;/li&gt;
&lt;li&gt;The Agent's role could not read the Lambda's configuration, and it appears to have mistaken the cadence of &lt;code&gt;CreateBacklogTask&lt;/code&gt; in CloudTrail for the cadence of the load&lt;/li&gt;
&lt;li&gt;The "recurring ~20-min bursts" in &lt;code&gt;Incident&lt;/code&gt; are also, in reality, the result of me running &lt;code&gt;hey&lt;/code&gt; repeatedly&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Investigation timeline
&lt;/h3&gt;

&lt;p&gt;The tab shows, in chronological order, what DevOps Agent checked and in what order. It corrected a wrong early assessment on its own, but the misidentification of the Lambda remains in the final summary.&lt;/p&gt;

&lt;p&gt;In the first 4 minutes or so, the Agent verifies the alarm and organizes the situation. It confirmed three things.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ECS is replacing tasks because of health check failures&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;RequestCount&lt;/code&gt; is almost the same as the 5xx count, and 2xx is almost 0&lt;/li&gt;
&lt;li&gt;Aurora CPU reaches 99 to 100% in every burst&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It then raises a question: "why does Aurora hit 100% CPU at only ~50-80 req/s and &amp;lt;20 connections?" It has three sub-agents investigate in parallel: the application logs (&lt;code&gt;app-logs&lt;/code&gt;), the DB internals (&lt;code&gt;aurora-db-analysis&lt;/code&gt;), and infrastructure changes (&lt;code&gt;infra-changes&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy7j44cpxmargqxpviv51.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy7j44cpxmargqxpviv51.png" alt="Investigation timeline: organizing the situation, and launching sub-agents in parallel" width="800" height="754"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The assessment at this point was "CPU saturation, not connection/memory exhaustion", because the maximum of &lt;code&gt;DatabaseConnections&lt;/code&gt; was only 16 to 19. This assessment is corrected later.&lt;/p&gt;

&lt;p&gt;At 4 minutes 26 seconds, the Agent is running four jobs in parallel (&lt;code&gt;app-logs&lt;/code&gt;, &lt;code&gt;aurora-db-analysis&lt;/code&gt;, &lt;code&gt;infra-changes&lt;/code&gt;, and &lt;code&gt;propose-mitigation&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;aurora-db-analysis&lt;/code&gt;, it had the sub-agent look at the slow query log, the Performance Insights top SQL, and the instance size. At 5 minutes 26 seconds, the following results came back.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There is one full-scan query, and the ratio of &lt;code&gt;Rows_examined&lt;/code&gt; to &lt;code&gt;Rows_sent&lt;/code&gt; is extremely large&lt;/li&gt;
&lt;li&gt;A single execution takes 16 to 114 seconds&lt;/li&gt;
&lt;li&gt;This one statement accounts for 100% of the DB load (the peak of average active sessions is about 90.9)&lt;/li&gt;
&lt;li&gt;The dominant wait is &lt;code&gt;wait/io/table/sql/handler&lt;/code&gt;, so CPU is the bottleneck because of in-memory row scanning&lt;/li&gt;
&lt;li&gt;The error log also supports this cause&lt;/li&gt;
&lt;/ul&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx6meuf3xri8wisuax9z9.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx6meuf3xri8wisuax9z9.png" alt="Investigation timeline: result of the DB analysis (root cause confirmed)" width="799" height="497"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Around the 8-minute mark, there is a hypothesis and its reconciliation. The application logs contained a large number of &lt;code&gt;1040 Too many connections&lt;/code&gt; (refused because the connection limit was reached). On the other hand, the DB-side sub-agent had concluded that there was no problem with connections, and the two conclusions conflicted.&lt;/p&gt;

&lt;p&gt;The Agent formed a hypothesis that &lt;code&gt;max_connections&lt;/code&gt; was low and checked it, but the parameter lookup came back empty, so it recorded the hypothesis as unresolved. It then reconciled the conflict between the two sub-agents on its own. The judgment was as follows.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MySQL doesn't log 1040 rejections, and rejected connections never register in &lt;code&gt;DatabaseConnections&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The DB-side sub-agent's conclusion was based on signals that are blind to this failure mode, and it over-concluded&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also organizes the flow from the full-scan query to the 5xx in four steps.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The full-scan query accounts for all of the DB load&lt;/li&gt;
&lt;li&gt;As a result, CPU saturation and connection exhaustion happen at the same time&lt;/li&gt;
&lt;li&gt;Requests that were refused a connection (&lt;code&gt;1040&lt;/code&gt;) and requests that timed out after 10 seconds (&lt;code&gt;2013&lt;/code&gt;) both become 5xx, and the alarm fires&lt;/li&gt;
&lt;li&gt;Because the load arrives intermittently, the bursts repeat. Between bursts, CPU drops and the targets recover on their own&lt;/li&gt;
&lt;/ul&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftyaqiez9nmv6poyza7st.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftyaqiez9nmv6poyza7st.png" alt="Investigation timeline: hypothesis, and reconciliation of the sub-agents' conclusions" width="800" height="564"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When I actually checked &lt;code&gt;DatabaseConnections&lt;/code&gt; in CloudWatch, the maximum was only about 10 as a 5-minute average. Meanwhile, the application logs contain a large number of &lt;code&gt;1040 Too many connections&lt;/code&gt;. Looking at this metric alone, the connections appear to have plenty of headroom, so the Agent's point was confirmed on screen as well.&lt;/p&gt;

&lt;p&gt;At about 10 minutes, the final summary appears. The following are gathered in one place.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The root cause&lt;/li&gt;
&lt;li&gt;The two failure mechanisms that occurred together (CPU saturation and connection exhaustion)&lt;/li&gt;
&lt;li&gt;The causes that were ruled out (deployment or configuration changes, memory, deadlocks, disk I/O)&lt;/li&gt;
&lt;li&gt;An unrelated observation (a remnant of the Scenario A failure)&lt;/li&gt;
&lt;li&gt;What could not be investigated&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the other hand, the &lt;code&gt;Trigger (Inputs)&lt;/code&gt; paragraph also contains the misidentification that treats the Lambda which starts investigations as the source of the load.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F841cvexrda16jo71m96w.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F841cvexrda16jo71m96w.png" alt="Investigation timeline: final summary" width="800" height="708"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From the screen, you can read an approach close to how human SREs handle a failure as a team: dividing roles and investigating in parallel, correcting an early assessment, and reconciling conflicting conclusions from team members.&lt;/p&gt;

&lt;h3&gt;
  
  
  Root cause
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Root cause&lt;/code&gt; tab is organized into three parts. The root cause was identified correctly, but the judgment about the Lambda in &lt;code&gt;Investigation gaps&lt;/code&gt; was wrong.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Impact&lt;/code&gt;: it says 5xx repeated in bursts on a cycle of about 20 minutes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Root causes&lt;/code&gt;: only one full-scan query. The Agent wrote that "This single query is 100% of DB load", that fixing the query "stops recurrence", and that "The undersized burstable instance is a secondary amplifier only"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Investigation gaps&lt;/code&gt;: it could not read the configuration of the Lambda that starts investigations (the Agent's role lacks the &lt;code&gt;lambda:GetFunction&lt;/code&gt; permission). Based on the cadence of &lt;code&gt;CreateBacklogTask&lt;/code&gt; in CloudTrail, it judged that Lambda to be the "scheduled traffic generator". As described above, this judgment is wrong&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Across the whole investigation, the Agent went as far as the following.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It organized the two symptoms, CPU saturation and connection exhaustion, as results of one root cause (the full-scan query)&lt;/li&gt;
&lt;li&gt;Based on the Performance Insights top SQL, it showed that this one statement accounts for 100% of the DB load (the peak of average active sessions is about 90.9)&lt;/li&gt;
&lt;li&gt;It showed that &lt;code&gt;Rows_examined&lt;/code&gt; is about 3,000,021 against about 20 for &lt;code&gt;Rows_sent&lt;/code&gt;, and that a single execution takes 16 to 114 seconds&lt;/li&gt;
&lt;li&gt;It did not stop at the surface symptom &lt;code&gt;Too many connections&lt;/code&gt;, and explained the causal chain in which the slow query holds each per-request connection for a long time and exhausts the connections&lt;/li&gt;
&lt;/ul&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkw8zatqbnbvqxnx5gnkh.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkw8zatqbnbvqxnx5gnkh.png" alt="Root cause: the root cause, and what could not be investigated" width="800" height="690"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Mitigation plan
&lt;/h3&gt;

&lt;p&gt;The mitigation is written in stages (record the current state, pre-check, apply, post-check, rollback), with commands and risks. The summary at the top separates a stopgap action from a permanent action. The content was mostly sound, but the explanation of the health check proposal was inaccurate.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stopgap: add an ngram &lt;code&gt;FULLTEXT&lt;/code&gt; index on &lt;code&gt;products.name&lt;/code&gt;. Scale the writer up from db.t4g.medium to db.r6g.large&lt;/li&gt;
&lt;li&gt;Permanent (application redeploy): rewrite &lt;code&gt;/search&lt;/code&gt; into an index-friendly form (&lt;code&gt;MATCH ... AGAINST&lt;/code&gt;, or a prefix match). Add a connection pool and a concurrency cap. Add a composite index that supports &lt;code&gt;ORDER BY created_at DESC&lt;/code&gt; and a server-side statement timeout. Decouple the health check from the processing that uses the DB&lt;/li&gt;
&lt;/ul&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F084j8t6v7c4x44gqsz8p.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F084j8t6v7c4x44gqsz8p.png" alt="Mitigation plan: summary at the top" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The apply stage (&lt;code&gt;Step 3: Apply&lt;/code&gt;) consists of two commands: &lt;code&gt;ALTER TABLE products ADD FULLTEXT INDEX ft_products_name (name) WITH PARSER ngram;&lt;/code&gt; and &lt;code&gt;aws rds modify-db-instance ... --db-instance-class db.r6g.large --apply-immediately&lt;/code&gt;. The Agent itself wrote the risks for each.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating the &lt;code&gt;FULLTEXT&lt;/code&gt; index on a table of about 3 million rows consumes CPU and I/O. Run it after the resize or in a low-traffic window. The index alone does not change behavior until the application query is rewritten to &lt;code&gt;MATCH ... AGAINST&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--apply-immediately&lt;/code&gt; reboots the writer, and writes stop for about 1 to 2 minutes. The cluster is a single instance with no reader, so there is no failover target. The instance class is more expensive&lt;/li&gt;
&lt;li&gt;Even if the connection limit goes up, without fixing the query, more concurrent 3-million-row scans can run and deepen CPU saturation. The Agent wrote that this is "a cushion, not a fix"&lt;/li&gt;
&lt;/ul&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp9wr5uvrtr6i0zu8mfsp.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp9wr5uvrtr6i0zu8mfsp.png" alt="Mitigation plan: Step 3 apply (FULLTEXT index and scale-up)" width="799" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The permanent fix is organized in &lt;code&gt;Agent-ready spec&lt;/code&gt; at the bottom of the screen as &lt;code&gt;Change requirements&lt;/code&gt; (three requirements). Each comes with acceptance criteria.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make the product-name search index-friendly (&lt;code&gt;EXPLAIN&lt;/code&gt; no longer shows &lt;code&gt;type=ALL&lt;/code&gt; and shows an index being used. &lt;code&gt;Rows_examined&lt;/code&gt; drops from about 3,000,021 to roughly the number of matched rows. In &lt;code&gt;Performance Insights&lt;/code&gt;, the statement no longer dominates DB load. The index is defined in a schema migration or IaC). This is the primary fix, and it says "fast queries release DB connections immediately, which also resolves the errno 1040 pileup"&lt;/li&gt;
&lt;li&gt;Introduce a bounded connection pool and a request concurrency cap, so that a slow dependency fails fast instead of exhausting DB connections&lt;/li&gt;
&lt;li&gt;An index that supports &lt;code&gt;ORDER BY created_at DESC LIMIT 20&lt;/code&gt;, a server-side statement timeout (so the DB does not keep running orphaned queries after the client disconnects), and decoupling the health check from the processing that uses the DB&lt;/li&gt;
&lt;/ul&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4bvefohpktqgux8uz0xj.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4bvefohpktqgux8uz0xj.png" alt="Mitigation plan: requirements of the Agent-ready spec" width="798" height="207"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The permanent fix was sound. The stopgap scale-up does not fix the full scan itself, and the Agent itself wrote that "a larger instance alone still saturates at higher traffic". A person still needs to check the plan before running it.&lt;/p&gt;

&lt;p&gt;The Agent also proposed decoupling &lt;code&gt;/health&lt;/code&gt; from the processing that uses the DB. In this app, &lt;code&gt;/health&lt;/code&gt; does not look at the DB in the code. During the load, however, ECS replaced tasks because of health check failures (&lt;code&gt;replaced ... due to an unhealthy status&lt;/code&gt;). The worker threads were probably filled by slow &lt;code&gt;/search&lt;/code&gt; requests, so &lt;code&gt;/health&lt;/code&gt; could not respond in time. The Agent's wording was inaccurate, but the symptom it pointed to was correct.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario B Result
&lt;/h3&gt;

&lt;p&gt;All three conclusions I expected (confirming the CPU spike, identifying the top SQL, and pointing out the missing index) were obtained. The errors were the judgment that the Lambda starting investigations was the source of the load, and the explanation of the &lt;code&gt;/health&lt;/code&gt; proposal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparison with Investigating by Hand
&lt;/h2&gt;

&lt;p&gt;Traditionally, the person who received the alarm investigated with the following steps.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use ALB metrics to check whether the 5xx comes from the ALB or from the targets&lt;/li&gt;
&lt;li&gt;Check the state of the ECS tasks and the application logs (CloudWatch Logs) for exceptions and connection errors&lt;/li&gt;
&lt;li&gt;Check the Aurora metrics (CPU, connection count) and, if needed, connect to Aurora and look at the running queries&lt;/li&gt;
&lt;li&gt;Check the Performance Insights top SQL and the slow query log&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The differences with DevOps Agent are as follows.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Switching screens: a person moves between ALB, ECS, CloudWatch Logs, Aurora, and Performance Insights. DevOps Agent puts the conclusion, the evidence, and the mitigation in one place in the web app&lt;/li&gt;
&lt;li&gt;How the investigation proceeds: a person investigates alone, one thing at a time. DevOps Agent splits the application logs, the DB internals, and infrastructure changes across sub-agents, investigates in parallel, and reconciles conflicting conclusions&lt;/li&gt;
&lt;li&gt;Speed of start: it took about 2 seconds from the alarm firing to the start of the investigation. Even if the alarm goes off at night, the investigation result and a mitigation proposal are ready by the time a person wakes up&lt;/li&gt;
&lt;li&gt;Duration of an investigation: about 10 minutes for Scenario A and about 15 minutes for Scenario B. The time varies with the complexity of the failure (other investigations varied from about 3 to 18 minutes)&lt;/li&gt;
&lt;li&gt;How evidence is presented: the conclusion comes with numerical evidence (&lt;code&gt;Rows_examined&lt;/code&gt;, average active sessions, the breakdown of errors), the flow of the investigation, and what could not be investigated. This makes it easy for a person to verify afterwards&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the other hand, DevOps Agent is not a replacement for people. Even in this test, there were several places where a person needed to check.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It misidentified the Lambda that starts investigations as the source of the traffic (Scenario B)&lt;/li&gt;
&lt;li&gt;The column name in the mitigation SQL was wrong (Scenario A)&lt;/li&gt;
&lt;li&gt;The time notation in the summary (UTC) did not match what operations expects by default&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I felt that the division of labor is quite realistic: DevOps Agent does the first-pass investigation, and a person verifies the conclusion and the evidence and then carries out the mitigation.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Refinement: Reporting Investigation Results in Japan Standard Time
&lt;/h2&gt;

&lt;p&gt;DevOps Agent seems to use UTC for times by default. The summary of the first investigation also reported the alarm time in UTC, as &lt;code&gt;22:19:58 UTC&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdf7t23wm1fh4pwo7w4qk.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdf7t23wm1fh4pwo7w4qk.png" alt="Summary reported in UTC" width="800" height="348"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Lambda that starts investigations puts the contents of the alarm into the &lt;code&gt;description&lt;/code&gt; of &lt;code&gt;CreateBacklogTask&lt;/code&gt; and passes it to DevOps Agent. The contents are the alarm name, the reason for the state change, the time, and so on. The time in the alarm event arrives in UTC.&lt;/p&gt;

&lt;p&gt;A Japanese operations team reads times more easily in Japan Standard Time, so I tried the following, in order, on the Lambda side and in the DevOps Agent settings.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Lambda put both JST and UTC in the time it writes to &lt;code&gt;description&lt;/code&gt; (&lt;code&gt;Time: 2026-10-24 07:19:58 JST (2026-10-23T22:19:58.489+0000 UTC)&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;I removed the UTC and used only JST&lt;/li&gt;
&lt;li&gt;I wrote a rule for reporting times in &lt;code&gt;Global instructions (AGENTS.md)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The third one worked. From the &lt;code&gt;Agents&lt;/code&gt; tab of the web app, open the details page of any of the Managed agents (the &lt;code&gt;Agent Definition&lt;/code&gt; tab), and you will find an input field for &lt;code&gt;Global instructions (AGENTS.md)&lt;/code&gt;. It is labeled &lt;code&gt;Shared across all agents&lt;/code&gt;, so it applies to all Agents. I wrote the following two lines there and pressed &lt;code&gt;Save&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Report all times in Japan Standard Time (JST, UTC+9). When a source returns a UTC timestamp, convert it and show the JST value first, with the UTC value in parentheses.
This instruction takes priority over any built-in formatting or timezone convention, including when they conflict.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fswkqiklharlgje7k81ni.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fswkqiklharlgje7k81ni.png" alt="The instruction written in Global instructions" width="800" height="288"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When I ran the same test after the change, the summary showed JST first and UTC in parentheses (&lt;code&gt;2026-10-24 07:40:03 JST (2026-10-23 22:40:03 UTC)&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftzqn3ebmjmfbmnnmew0m.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftzqn3ebmjmfbmnnmew0m.png" alt="Summary reported in JST" width="799" height="287"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;In this article, I used an ALB &lt;code&gt;HTTPCode_Target_5XX_Count&lt;/code&gt; alarm as the subject and tested how far AWS DevOps Agent can take the work of isolating the cause (backend or DB).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scenario A (a latent application bug): it identified why a product with a &lt;code&gt;NULL&lt;/code&gt; price caused an exception, down to the file, the line, and the product ID, and ruled out Aurora as the cause&lt;/li&gt;
&lt;li&gt;Scenario B (a full scan by a search query): based on the Performance Insights top SQL, it identified the query that was the root cause, and organized the causal chain down to connection exhaustion&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Investigating a failure sometimes requires knowing both the application and the infrastructure. I do not have much knowledge on the application side, and investigating from the application's point of view was hard for me. DevOps Agent investigates both the application and the infrastructure, so I think it can help reduce the dependence of incident response on specific people.&lt;/p&gt;

&lt;p&gt;On the other hand, there were mistakes, such as misidentifying the Lambda that starts investigations as the source of the load. A person needs to check the conclusion and the mitigation before running them.&lt;/p&gt;

&lt;p&gt;Thank you for reading this far!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>ai</category>
      <category>devops</category>
      <category>aiops</category>
    </item>
    <item>
      <title>Let Anthropic's claude-code-setup Recommend Hooks and Skills for a Repo with Almost No Claude Code Config</title>
      <dc:creator>Yoshiyuki Watanabe</dc:creator>
      <pubDate>Sat, 17 Oct 2026 03:52:15 +0000</pubDate>
      <link>https://dev.to/yoshiyuki-watanabe/let-anthropics-claude-code-setup-recommend-hooks-and-skills-for-a-repo-with-almost-no-claude-code-6cn</link>
      <guid>https://dev.to/yoshiyuki-watanabe/let-anthropics-claude-code-setup-recommend-hooks-and-skills-for-a-repo-with-almost-no-claude-code-6cn</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I tried Anthropic's official plugin &lt;code&gt;claude-code-setup&lt;/code&gt; on netflix-intro-skipper, a Chrome extension I built myself.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://claude.com/marketplace/plugins/claude-code-setup" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.claude.com%2Ff66071070c4701ea2c37fc01b3c0f83623cf29d3.jpg%3Fw%3D2400%26h%3D1260%26fit%3Dcrop" height="1260" class="m-0" width="2400"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://claude.com/marketplace/plugins/claude-code-setup" rel="noopener noreferrer" class="c-link"&gt;
            Claude Code Setup | Claude by Anthropic
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Analyze codebases and recommend tailored Claude Code automations such as hooks, skills, MCP servers, and subagents.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.claude.com%2F95a868946ac8a31e5ff832e2899f294aa368b836.png%3Fw%3D32%26h%3D32" width="32" height="32"&gt;
          claude.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;This article walks through installing the plugin and the recommendations it actually produced.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Claude Code Setup?
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;claude-code-setup&lt;/code&gt; is a plugin that analyzes your codebase and recommends Claude Code automation settings that fit the project. It is provided by Anthropic and is shown as Anthropic-verified on the marketplace.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The recommendation categories are Hooks, Subagents, Skills, MCP Servers, and Plugins&lt;/li&gt;
&lt;li&gt;It is read-only and never creates or modifies files&lt;/li&gt;
&lt;li&gt;It usually limits recommendations to 1-2 per category, and increases that to 3-5 when you ask for a specific category&lt;/li&gt;
&lt;li&gt;It reads files such as &lt;code&gt;package.json&lt;/code&gt; and the directory structure to determine the tech stack&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Target Project
&lt;/h2&gt;

&lt;p&gt;The target is netflix-intro-skipper, introduced above.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/yowatanabe" rel="noopener noreferrer"&gt;
        yowatanabe
      &lt;/a&gt; / &lt;a href="https://github.com/yowatanabe/netflix-intro-skipper" rel="noopener noreferrer"&gt;
        netflix-intro-skipper
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      NetflixのイントロをスキップするChrome拡張機能
    &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;Netflix Intro Skipper&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Netflix のイントロを自動的にスキップする Chrome 拡張機能です。&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🎯 機能&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;✅ Netflix の「イントロをスキップ」ボタンを自動検出・クリック&lt;/li&gt;
&lt;li&gt;✅ 自動スキップ機能のオン/オフ切り替え&lt;/li&gt;
&lt;li&gt;✅ 世界各国の Netflix ドメインに対応&lt;/li&gt;
&lt;li&gt;✅ 軽量で非侵入的な設計&lt;/li&gt;
&lt;li&gt;✅ 多言語対応（日本語、英語、その他）&lt;/li&gt;
&lt;li&gt;✅ セッション管理で複数動画に対応&lt;/li&gt;
&lt;li&gt;✅ 堅牢なエラーハンドリング&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🚀 インストール方法&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;: このツールは個人利用のために作成されたもので、現時点では Chrome ウェブストアで公開する予定はありません。以下の手順で手動インストールしてください。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;このプロジェクトのファイルをダウンロード&lt;/li&gt;
&lt;li&gt;Chrome を開き、&lt;code&gt;chrome://extensions/&lt;/code&gt; にアクセス&lt;/li&gt;
&lt;li&gt;右上の「デベロッパーモード」を有効にする&lt;/li&gt;
&lt;li&gt;「パッケージ化されていない拡張機能を読み込む」をクリック&lt;/li&gt;
&lt;li&gt;拡張機能のディレクトリを選択&lt;/li&gt;
&lt;li&gt;拡張機能がインストールされ、使用準備完了&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;📖 使用方法&lt;/h2&gt;

&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Netflix にアクセスして動画の視聴を開始&lt;/li&gt;
&lt;li&gt;イントロが表示されると、拡張機能が自動的にスキップ&lt;/li&gt;
&lt;li&gt;拡張機能のポップアップで自動スキップのオン/オフを切り替え可能&lt;/li&gt;
&lt;li&gt;設定はブラウザセッション間で保存されます&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🛠️ 開発&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;開発者向け（コードを修正・テストする場合）&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;拡張機能のコードを修正したり、テストを実行したい場合のみ以下が必要です：&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;環境セットアップ&lt;/h3&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; 依存関係をインストール（開発・テスト用）&lt;/span&gt;
npm install

&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; すべてのテストを実行&lt;/span&gt;
npm &lt;span class="pl-c1"&gt;test&lt;/span&gt;

&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; 特定のテストを実行&lt;/span&gt;
npm &lt;span class="pl-c1"&gt;test&lt;/span&gt; -- integration.test.js

&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; テストをウォッチモードで実行&lt;/span&gt;
npm run test:watch

&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; テストカバレッジを確認&lt;/span&gt;
npm run test:coverage&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;開発ワークフロー&lt;/h3&gt;

&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;ソースコード&lt;/strong&gt;: &lt;code&gt;src/&lt;/code&gt; フォルダ内のファイルを編集&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;テスト&lt;/strong&gt;: &lt;code&gt;tests/&lt;/code&gt; フォルダ内でテストを作成・実行&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;アイコン&lt;/strong&gt;: &lt;code&gt;assets/icons/&lt;/code&gt; でアイコンファイルを管理&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;拡張機能テスト&lt;/strong&gt;: Chrome で &lt;code&gt;chrome://extensions/&lt;/code&gt; から再読み込み&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;リリースバージョン管理&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;リリースバージョンを更新したい場合は以下のコマンドを実行すると&lt;code&gt;package.json&lt;/code&gt;と&lt;code&gt;manifest.json&lt;/code&gt;の&lt;code&gt;version&lt;/code&gt;が更新されます&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; パッチバージョンアップ（バグ修正: 1.0.1 → 1.0.2）&lt;/span&gt;
npm run version:patch

&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; マイナーバージョンアップ（新機能: 1.0.1 → 1.1.0）&lt;/span&gt;
npm run version:minor

&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; メジャーバージョンアップ（破壊的変更: 1.0.1 → 2.0.0）&lt;/span&gt;
npm run version:major&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Dry Run（事前確認）:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; 実行前に何が行われるかを確認&lt;/span&gt;
npm run version:dry-run:patch
npm run version:dry-run:minor
npm run version:dry-run:major&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;実行例:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;$ npm run version:patch
&lt;span class="pl-k"&gt;&amp;gt;&lt;/span&gt; netflix-intro-skipper@1.0.1 version:patch
&lt;/pre&gt;…
&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/yowatanabe/netflix-intro-skipper" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;It supports Manifest V3 and automatically skips Netflix intros&lt;/li&gt;
&lt;li&gt;It is plain JavaScript with no build step&lt;/li&gt;
&lt;li&gt;Tests use Jest and fast-check, with 13 test files&lt;/li&gt;
&lt;li&gt;There is no &lt;code&gt;CLAUDE.md&lt;/code&gt;, and &lt;code&gt;.claude/&lt;/code&gt; contains only &lt;code&gt;settings.local.json&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since the project has almost no Claude Code configuration, I thought it was a good fit for seeing what this plugin recommends.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trying It Out
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Install the plugin
&lt;/h3&gt;

&lt;p&gt;Launch Claude Code in the target repository and install the plugin from the marketplace.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/plugin install claude-code-setup@claude-plugins-official
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0g5iefi1nmf8sdo2hlp4.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0g5iefi1nmf8sdo2hlp4.png" alt=" " width="800" height="83"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After installation, you can confirm that &lt;code&gt;claude-code-setup&lt;/code&gt; was added in the &lt;code&gt;Installed&lt;/code&gt; tab of &lt;code&gt;/plugins&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7yn2lnjt9px056tswu0n.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7yn2lnjt9px056tswu0n.png" alt=" " width="798" height="160"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Ask for recommendations
&lt;/h3&gt;

&lt;p&gt;After installing, I entered the following prompt in English.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Analyze this project and recommend Claude Code automations (hooks, skills, MCP servers, subagents, plugins) that fit it.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Review the recommendations
&lt;/h3&gt;

&lt;p&gt;This is the output the plugin produced.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Claude Code Automation Recommendations&lt;/span&gt;

&lt;span class="gu"&gt;### Codebase Profile&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Type**&lt;/span&gt;: Chrome extension (Manifest V3) that automatically skips Netflix intros
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Language/tooling**&lt;/span&gt;: Plain JavaScript, no build step
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Testing**&lt;/span&gt;: Jest (jsdom) and fast-check (property-based testing), with 13 test files and about 6,000 lines of tests
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Versioning**&lt;/span&gt;: &lt;span class="sb"&gt;`scripts/release-version.js`&lt;/span&gt; keeps the version in &lt;span class="sb"&gt;`package.json`&lt;/span&gt;, &lt;span class="sb"&gt;`manifest.json`&lt;/span&gt;, and &lt;span class="sb"&gt;`src/version.js`&lt;/span&gt; in sync
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Size**&lt;/span&gt;: &lt;span class="sb"&gt;`src/content.js`&lt;/span&gt; is 2,720 lines and by far the largest file
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Claude config**&lt;/span&gt;: No &lt;span class="sb"&gt;`CLAUDE.md`&lt;/span&gt;. &lt;span class="sb"&gt;`.claude/`&lt;/span&gt; contains only &lt;span class="sb"&gt;`settings.local.json`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Other**&lt;/span&gt;: Specs live in &lt;span class="sb"&gt;`.kiro/specs/`&lt;/span&gt;. No CI was found
&lt;span class="p"&gt;
---
&lt;/span&gt;
&lt;span class="gu"&gt;### ⚡ Hooks&lt;/span&gt;

&lt;span class="gu"&gt;#### 1. Run related tests automatically after edits (PostToolUse)&lt;/span&gt;
&lt;span class="gs"&gt;**Why**&lt;/span&gt;: The project has a large test suite, and small changes to a big file like &lt;span class="sb"&gt;`content.js`&lt;/span&gt; can easily cause regressions. Running &lt;span class="sb"&gt;`npx jest --findRelatedTests &amp;lt;file&amp;gt;`&lt;/span&gt; after each &lt;span class="sb"&gt;`src/`&lt;/span&gt; edit catches problems right away.
&lt;span class="gs"&gt;**Where**&lt;/span&gt;: &lt;span class="sb"&gt;`.claude/settings.json`&lt;/span&gt;
&lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;json
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&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;"PostToolUse"&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;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Edit|Write"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jq -r '.tool_input.file_path' | grep -E '/src/.*&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;.js$' | xargs -r npx jest --findRelatedTests --passWithNoTests"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;```&lt;/span&gt;

&lt;span class="gu"&gt;#### 2. Block direct edits to version-managed files (PreToolUse)&lt;/span&gt;
&lt;span class="gs"&gt;**Why**&lt;/span&gt;: Versions are meant to be bumped through &lt;span class="sb"&gt;`npm run version:*`&lt;/span&gt;, which syncs three files. Hand-editing the version in &lt;span class="sb"&gt;`manifest.json`&lt;/span&gt; or &lt;span class="sb"&gt;`src/version.js`&lt;/span&gt; would desynchronize them. You could also block &lt;span class="sb"&gt;`package-lock.json`&lt;/span&gt; and &lt;span class="sb"&gt;`coverage/`&lt;/span&gt; in the same hook.
&lt;span class="p"&gt;
---
&lt;/span&gt;
&lt;span class="gu"&gt;### 🎯 Skills&lt;/span&gt;

&lt;span class="gu"&gt;#### 1. `release` (user-only)&lt;/span&gt;
&lt;span class="gs"&gt;**Why**&lt;/span&gt;: A release spans several steps: &lt;span class="sb"&gt;`version:dry-run`&lt;/span&gt;, then &lt;span class="sb"&gt;`version:*`&lt;/span&gt;, then commit, then tag. It has side effects, so it is safer to invoke manually.
&lt;span class="gs"&gt;**Create**&lt;/span&gt;: &lt;span class="sb"&gt;`.claude/skills/release/SKILL.md`&lt;/span&gt;
&lt;span class="gh"&gt;```yaml
---
&lt;/span&gt;name: release
description: Verify with a dry run, then bump the version and prepare the release
&lt;span class="gh"&gt;disable-model-invocation: true
---
&lt;/span&gt;&lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;
&lt;/span&gt;
#### 2. `netflix-dom-check` (Claude-only background knowledge)
**Why**: Changes to Netflix's DOM selectors are the most common way an extension like this breaks. A skill that collects the selector list, the requirements in `.kiro/specs`, and the tests to check after an update makes `content.js` changes faster and more accurate.
**Create**: `.claude/skills/netflix-dom-check/SKILL.md` (with `user-invocable: false`)

---

### 🤖 Subagents

#### 1. `extension-security-reviewer`
**Why**: This MV3 extension has many `host_permissions`. A periodic review is worthwhile for excessive permissions, XSS risks from DOM manipulation in `content.js`, and missing validation on message passing.
**Where**: `.claude/agents/extension-security-reviewer.md`

#### 2. `service-worker-reviewer`
**Why**: You previously fixed a Service Worker problem after PC sleep (PR #1). A reviewer focused on state persistence, alarms, and restart resilience would help prevent a recurrence when `background.js` changes.
**Where**: `.claude/agents/service-worker-reviewer.md`

---

### 🔌 MCP Servers

#### 1. Playwright MCP
**Why**: Verifying behavior in a real browser matters for an extension. Jest runs on jsdom, so it can't check the real popup UI or content-script behavior.
**Install**: `claude mcp add playwright npx @playwright/mcp@latest`

#### 2. context7
**Why**: It gives you up-to-date docs for the Chrome Extension APIs (`chrome.storage`, `chrome.alarms`, and others) and for Jest and fast-check.
**Install**: `claude mcp add context7 -- npx -y @upstash/context7-mcp`

---

### 📦 Plugins
- **commit-commands**: Streamlines commits and PR creation. It fits well because this repo uses a PR workflow (feature branch, then merge).
- **frontend-design**: Useful when improving `popup.html` and `popup.js`. Lower priority.

---

### Notes
- I'd start by **creating a `CLAUDE.md`**. Just documenting how to run `npm test`, the version-sync rules, and the role of `.kiro/specs` goes a long way. `/init` can generate a draft.
- Check that `coverage/` and `node_modules/` are both listed in `.gitignore`.

If you want more options in a specific category, or help implementing any of these, let me know.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Key Takeaways from the Recommendations
&lt;/h2&gt;

&lt;p&gt;Most of the recommendations were based on the specifics of the repository.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Because &lt;code&gt;src/content.js&lt;/code&gt; is large at 2,720 lines, it recommends a hook that runs only the related tests after each edit&lt;/li&gt;
&lt;li&gt;Because &lt;code&gt;scripts/release-version.js&lt;/code&gt; keeps the version in three files in sync, it recommends a hook that blocks direct edits to version-related files, plus a &lt;code&gt;release&lt;/code&gt; skill that bundles the release steps&lt;/li&gt;
&lt;li&gt;It even picked up the Service Worker fix from PR #1 and recommends a dedicated reviewer subagent for &lt;code&gt;background.js&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these are grounded in what is actually in the repository: file size, scripts, and PR history.&lt;/p&gt;

&lt;p&gt;The plugin only prints recommendations and does not create any files. To apply them, either place the suggested settings yourself or ask Claude Code separately to create them.&lt;/p&gt;

&lt;p&gt;The first recommendation was to create a &lt;code&gt;CLAUDE.md&lt;/code&gt;. &lt;code&gt;/init&lt;/code&gt; can generate a draft, so that looks like a good place to start.&lt;/p&gt;

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

&lt;p&gt;For a repository with almost no configuration, &lt;code&gt;claude-code-setup&lt;/code&gt; produced recommendations that took into account the version-sync script and past fix history. It is a good starting point for a project where you are not sure what to add first.&lt;/p&gt;

&lt;p&gt;Because it is read-only and has no side effects, it is easy to try even on an existing repository. Give it a try on your own project.&lt;/p&gt;

&lt;p&gt;Thank you for reading this far!&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://claude.com/marketplace/plugins/claude-code-setup" rel="noopener noreferrer"&gt;Claude Code Setup plugin (Claude marketplace)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/anthropics/claude-plugins-official/tree/main/plugins/claude-code-setup" rel="noopener noreferrer"&gt;claude-code-setup (anthropics/claude-plugins-official)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>claude</category>
      <category>ai</category>
      <category>buildinpublic</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Trying MiniStack and StackPort</title>
      <dc:creator>Yoshiyuki Watanabe</dc:creator>
      <pubDate>Tue, 13 Oct 2026 21:52:57 +0000</pubDate>
      <link>https://dev.to/aws-builders/trying-ministack-and-stackport-110i</link>
      <guid>https://dev.to/aws-builders/trying-ministack-and-stackport-110i</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I tried MiniStack, a free local AWS emulator, and StackPort, a browser-based viewer for its resources.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ministackorg" rel="noopener noreferrer"&gt;
        ministackorg
      &lt;/a&gt; / &lt;a href="https://github.com/ministackorg/ministack" rel="noopener noreferrer"&gt;
        ministack
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Ministack: Free, open-source local AWS emulator - 60+ services, Terraform compatible, real databases. Free forever. MIT   licensed.
    &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/ministackorg/ministack/ministack_logo.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fministackorg%2Fministack%2FHEAD%2Fministack_logo.png" alt="MiniStack — Free Open-Source AWS Emulator" width="400"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;MiniStack&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Free, open-source local AWS emulator. Free forever.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;60+ AWS services on a single port · Multi-account &amp;amp; multi-region · Terraform compatible · Real databases · MIT licensed&lt;/p&gt;

&lt;p&gt;
  &lt;a href="https://github.com/ministackorg/ministack/releases" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/52c908839464a777dd6a1b31c1f3028180157fd1addaff60e8923aea2ccbc9be/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f762f72656c656173652f6d696e69737461636b6f72672f6d696e69737461636b" alt="GitHub release"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/ministackorg/ministack/actions" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/766beda2e3c8cc06bb77aafd665d23ef37689c3369c88423253d043ce8f4faf8/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6d696e69737461636b6f72672f6d696e69737461636b2f63692e796d6c3f6272616e63683d6d61696e" alt="Build"&gt;&lt;/a&gt;
  &lt;a href="https://hub.docker.com/r/ministackorg/ministack" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/5116838bc9910281409a171a54931cf2d9a5e6efb95bef7d1963b3f50a86a342/68747470733a2f2f696d672e736869656c64732e696f2f646f636b65722f70756c6c732f6d696e69737461636b6f72672f6d696e69737461636b" alt="Docker Pulls"&gt;&lt;/a&gt;
  &lt;a href="https://hub.docker.com/r/ministackorg/ministack" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/c715301d18b64582eb07e0e04302c2fc549a1664f62f4b0def306e7607af715e/68747470733a2f2f696d672e736869656c64732e696f2f646f636b65722f696d6167652d73697a652f6d696e69737461636b6f72672f6d696e69737461636b2f6c6174657374" alt="Docker Image Size"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/ministackorg/ministack/blob/main/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/271a55255e48c4433890c1fd054fac1e9643d364851953b52b32422bc9bb16f4/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f6d696e69737461636b6f72672f6d696e69737461636b" alt="License"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/be14f9fa3603b94c1802258e6acb2f8f1b234ead6ae0bfc316ba428dbf7e9d34/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f707974686f6e2d253345253230332e31302d626c7565"&gt;&lt;img src="https://camo.githubusercontent.com/be14f9fa3603b94c1802258e6acb2f8f1b234ead6ae0bfc316ba428dbf7e9d34/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f707974686f6e2d253345253230332e31302d626c7565" alt="Python"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
  &lt;a href="https://ministack.org" rel="nofollow noopener noreferrer"&gt;Ministack.org&lt;/a&gt; · &lt;a href="https://hub.docker.com/r/ministackorg/ministack" rel="nofollow noopener noreferrer"&gt;Docker Hub&lt;/a&gt; · &lt;a href="https://www.linkedin.com/company/ministackorg/" rel="nofollow noopener noreferrer"&gt;LinkedIn&lt;/a&gt;
&lt;/p&gt;




&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why MiniStack?&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;LocalStack recently moved its core services behind a paid plan. If you relied on LocalStack Community for local development and CI/CD pipelines, MiniStack is your free alternative.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;60+ AWS services&lt;/strong&gt; emulated on a single port (4566)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Drop-in compatible&lt;/strong&gt; — works with &lt;code&gt;boto3&lt;/code&gt;, AWS CLI, Terraform, CDK, Pulumi, any SDK&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-account &amp;amp; multi-region&lt;/strong&gt; — a 12-digit access key becomes the account, the SigV4 region scopes the state; isolated tenants and regions on one endpoint, like real AWS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon Bedrock locally&lt;/strong&gt; — Converse / InvokeModel with AWS-exact wire shapes; point &lt;code&gt;MINISTACK_BEDROCK_PROXY_URL&lt;/code&gt; at Ollama, llama.cpp, or vLLM and get real completions through the Bedrock API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real infrastructure&lt;/strong&gt; — RDS spins up actual Postgres/MySQL containers, ElastiCache spins…&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/ministackorg/ministack" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/DaviReisVieira" rel="noopener noreferrer"&gt;
        DaviReisVieira
      &lt;/a&gt; / &lt;a href="https://github.com/DaviReisVieira/stackport" rel="noopener noreferrer"&gt;
        stackport
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Open-source universal AWS resource browser for MiniStack, Floci, LocalStack, Moto, and real AWS
    &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 nofollow" href="https://raw.githubusercontent.com/DaviReisVieira/stackport/main/docs/images/stackport_logo.svg"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FDaviReisVieira%2Fstackport%2Fmain%2Fdocs%2Fimages%2Fstackport_logo.svg" alt="StackPort — Universal AWS Resource Browser" width="150"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;StackPort&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Universal AWS resource browser for local emulators and real AWS accounts.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Browse, inspect, and manage resources across 35 AWS services with dedicated UIs for S3, DynamoDB, Lambda, SQS, IAM, EC2, CloudWatch Logs, and Secrets Manager.&lt;/p&gt;
&lt;p&gt;
  &lt;a href="https://github.com/DaviReisVieira/stackport/actions/workflows/ci.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/DaviReisVieira/stackport/actions/workflows/ci.yml/badge.svg" alt="CI"&gt;&lt;/a&gt;
  &lt;a href="https://pypi.org/project/stackport/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/37faf86b4888cdc61a642be882355406e35ac86cc35b484bc2b8584637e01d03/68747470733a2f2f696d672e736869656c64732e696f2f707970692f762f737461636b706f7274" alt="PyPI Version"&gt;&lt;/a&gt;
  &lt;a href="https://hub.docker.com/r/davireis/stackport" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/73e0499df7a4a8f5ac87f59e64393f004b2b82cec4d11d5eda3cbc56a35acd4f/68747470733a2f2f696d672e736869656c64732e696f2f646f636b65722f70756c6c732f64617669726569732f737461636b706f7274" alt="Docker Pulls"&gt;&lt;/a&gt;
  &lt;a href="https://hub.docker.com/r/davireis/stackport" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/19d80b4c38d6b577ca6b48074d362d6d086ca0d9d123bc0dd58ca1f7e5fefc0e/68747470733a2f2f696d672e736869656c64732e696f2f646f636b65722f696d6167652d73697a652f64617669726569732f737461636b706f72742f6c6174657374" alt="Docker Image Size"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/DaviReisVieira/stackport/blob/master/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/9c557d9e0bf169c335a2659db0338d7a36368430aa31d792ee441e913fca4834/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f44617669526569735669656972612f737461636b706f7274" alt="License"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/a479a6cadba72bc83a7a99ee8f32433b25b1a8d0b2d8379b666b343b5c7e5e3c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f707974686f6e2d332e31322d736c696d"&gt;&lt;img src="https://camo.githubusercontent.com/a479a6cadba72bc83a7a99ee8f32433b25b1a8d0b2d8379b666b343b5c7e5e3c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f707974686f6e2d332e31322d736c696d" alt="Python"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/DaviReisVieira/stackport/stargazers" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/ade0aff7334ec8bac5d0d1426656a3e05260c0a41512f97e6555cc442b072e20/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f73746172732f44617669526569735669656972612f737461636b706f7274" alt="GitHub stars"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Screenshots&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Dashboard&lt;/strong&gt; — Service overview with resource counts and health status
&lt;a rel="noopener noreferrer nofollow" href="https://raw.githubusercontent.com/DaviReisVieira/stackport/main/docs/images/dashboard.jpeg?v=2.1"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FDaviReisVieira%2Fstackport%2Fmain%2Fdocs%2Fimages%2Fdashboard.jpeg%3Fv%3D2.1" alt="StackPort Dashboard"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lambda Browser&lt;/strong&gt; — Function list with config, aliases, versions, event sources, and invocation panel
&lt;a rel="noopener noreferrer nofollow" href="https://raw.githubusercontent.com/DaviReisVieira/stackport/main/docs/images/lambda.jpeg?v=2.1"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FDaviReisVieira%2Fstackport%2Fmain%2Fdocs%2Fimages%2Flambda.jpeg%3Fv%3D2.1" alt="Lambda Resources"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;S3 Browser&lt;/strong&gt; — File browser with upload, download, folder navigation, and tagging
&lt;a rel="noopener noreferrer nofollow" href="https://raw.githubusercontent.com/DaviReisVieira/stackport/main/docs/images/s3.jpeg?v=2.1"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2FDaviReisVieira%2Fstackport%2Fmain%2Fdocs%2Fimages%2Fs3.jpeg%3Fv%3D2.1" alt="S3 Browser"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Browse and inspect resources across &lt;strong&gt;35 AWS services&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;8 dedicated service UIs&lt;/strong&gt; for S3, DynamoDB, Lambda, SQS, IAM, EC2, CloudWatch Logs, and Secrets Manager&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write operations&lt;/strong&gt; — upload/delete S3 objects, query DynamoDB, invoke Lambda, send/receive SQS messages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real AWS support&lt;/strong&gt; — connect to real AWS accounts with read-only mode by default&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Per-endpoint authentication&lt;/strong&gt; — default credentials, AWS profiles (SSO/AssumeRole), or static keys per endpoint&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Guided lessons&lt;/strong&gt; — hands-on AWS tutorials that run inside the console and…&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/DaviReisVieira/stackport" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;This article builds the following pipeline:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;EventBridge Scheduler invokes a Lambda function on a schedule&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Lambda function calls an API and publishes the result to SNS&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;SNS triggers a second Lambda function, which posts to a Slack Incoming Webhook&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The API key and the Webhook URL are stored as SecureString parameters in SSM Parameter Store&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Are MiniStack and StackPort?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  MiniStack
&lt;/h3&gt;

&lt;p&gt;MiniStack is a local AWS emulator under the MIT license. You can use more than 60 AWS services directly from the AWS CLI, boto3, Terraform, and CDK.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://ministack.org/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fministack.org%2Fog-image.png" height="438" class="m-0" width="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://ministack.org/" rel="noopener noreferrer" class="c-link"&gt;
            MiniStack — The Best AWS Emulator | Free Open-Source LocalStack Alternative
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            The best AWS emulator and the best alternative to LocalStack. 60+ AWS services free forever — real Postgres, Redis, Docker containers, full Terraform / CDK / Pulumi support. MIT licensed.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fministack.org%2Fministack_symbol.svg%3Fv%3D2" width="566" height="566"&gt;
          ministack.org
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  StackPort
&lt;/h3&gt;

&lt;p&gt;StackPort is a tool for browsing the resources of local emulators and real AWS accounts in a browser. It connects to emulators other than MiniStack (such as LocalStack) and to real AWS accounts.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://stackport.cloud/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstackport.cloud%2Fimages%2Fdashboard.png" height="500" class="m-0" width="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://stackport.cloud/" rel="noopener noreferrer" class="c-link"&gt;
            StackPort: open-source web console for AWS emulators
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            StackPort is a free, open-source web console for MiniStack, Floci, LocalStack, Moto and real AWS. Browse 35 services, use 13 dedicated views, and learn AWS with guided lessons. One Docker image.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstackport.cloud%2Ffavicon.svg" width="1024" height="1024"&gt;
          stackport.cloud
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;StackPort lets you browse the resources of 35 services.&lt;/p&gt;

&lt;p&gt;Eight of them have dedicated screens: S3, DynamoDB, Lambda, SQS, IAM, EC2, CloudWatch Logs, and Secrets Manager. For example, CloudWatch Logs lets you drill down from log groups to log streams to log events.&lt;/p&gt;

&lt;p&gt;The other services (such as SSM in this article) are shown as a searchable resource table with a JSON detail view.&lt;/p&gt;

&lt;p&gt;Note that StackPort does not support EventBridge Scheduler. The &lt;code&gt;events&lt;/code&gt; service only shows EventBridge rules and event buses, so I check the schedule with the CLI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;Locally, I run a mock server (&lt;code&gt;mock-api&lt;/code&gt;) in place of the external API. For Slack, I use a real Incoming Webhook, with separate channels for the local and AWS environments.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl8ylejsuc9hrv18bwtl2.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl8ylejsuc9hrv18bwtl2.png" alt="Local environment architecture" width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Two things differ between the local environment and real AWS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;External API: &lt;code&gt;mock-api&lt;/code&gt; locally, API Gateway (MOCK integration) on real AWS. Both return &lt;code&gt;GET /v1/status&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Slack destination: &lt;code&gt;#notify-demo-local&lt;/code&gt; locally, &lt;code&gt;#notify-demo-aws&lt;/code&gt; on real AWS&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The directory structure is as follows. Terraform is split into a shared module (&lt;code&gt;modules/notify&lt;/code&gt;) and one directory per environment (&lt;code&gt;environments/local&lt;/code&gt; and &lt;code&gt;environments/aws&lt;/code&gt;). Each environment has its own state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;code/
├── lambda/                          # Shared: the same code runs locally and on real AWS
│   ├── fetch_status/
│   │   └── app.py
│   └── notify_slack/
│       └── app.py
├── mock-api/                        # Local only: mock server standing in for the external API
│   ├── Dockerfile
│   └── server.py
├── scripts/
│   ├── put-parameters-aws.sh        # AWS only: registers parameters in the real SSM
│   └── put-parameters.sh            # Local only: registers parameters in MiniStack's SSM
├── terraform/
│   ├── environments/
│   │   ├── aws/                     # For real AWS: points at real AWS
│   │   │   ├── main.tf
│   │   │   ├── outputs.tf
│   │   │   ├── providers.tf
│   │   │   ├── terraform.tfvars
│   │   │   ├── terraform.tfvars.example
│   │   │   └── variables.tf
│   │   └── local/                   # For local: points at MiniStack
│   │       ├── main.tf
│   │       ├── outputs.tf
│   │       ├── providers.tf
│   │       ├── terraform.tfvars
│   │       ├── terraform.tfvars.example
│   │       └── variables.tf
│   └── modules/
│       └── notify/                  # Shared: Lambda, SNS, Scheduler, IAM, and more
│           ├── main.tf
│           ├── outputs.tf
│           ├── variables.tf
│           └── versions.tf
└── docker-compose.yml               # Local only: starts MiniStack, StackPort, and mock-api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Building the Local Environment
&lt;/h2&gt;

&lt;p&gt;You need Docker (Docker Desktop, OrbStack, and so on), Terraform, and the AWS CLI.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Start MiniStack, StackPort, and the Mock Server
&lt;/h3&gt;

&lt;p&gt;Start three containers with &lt;code&gt;docker-compose.yml&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;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ministack&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ministackorg/ministack:latest&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;4566:4566"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;MINISTACK_REGION=ap-northeast-1&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;[&lt;/span&gt;
          &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
          &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;python"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
          &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-c"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
          &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;import&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;urllib.request;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;urllib.request.urlopen('http://localhost:4566/_ministack/health')"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;3s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;

  &lt;span class="na"&gt;stackport&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;davireis/stackport:latest&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8080:8080"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWS_ENDPOINT_URL=http://ministack:4566&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWS_REGION=ap-northeast-1&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWS_ACCESS_KEY_ID=test&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;AWS_SECRET_ACCESS_KEY=test&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;[&lt;/span&gt;
          &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
          &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;python"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
          &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-c"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
          &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;import&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;urllib.request;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;urllib.request.urlopen('http://localhost:8080/api/health')"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;3s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ministack&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_healthy&lt;/span&gt;

  &lt;span class="c1"&gt;# Mock server standing in for the external API&lt;/span&gt;
  &lt;span class="na"&gt;mock-api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./mock-api&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8000:8000"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;EXPECTED_API_KEY=local-dummy-api-key&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The three containers are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ministack&lt;/code&gt;: the local AWS emulator. It accepts AWS API calls on port &lt;code&gt;4566&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;stackport&lt;/code&gt;: a web UI for browsing MiniStack's resources. It opens on port &lt;code&gt;8080&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mock-api&lt;/code&gt;: a mock server standing in for the external API. It runs on port &lt;code&gt;8000&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;mock-api&lt;/code&gt; is written with only the Python standard library. It has one endpoint that validates the &lt;code&gt;x-api-key&lt;/code&gt; header and returns a status.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/v1/status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;x-api-key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;EXPECTED_API_KEY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&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="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Forbidden&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&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="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&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="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;not found&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start the containers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;code
docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://localhost:8080&lt;/code&gt; in a browser to see the StackPort dashboard.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9u9nkz5jd6hku83hei47.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9u9nkz5jd6hku83hei47.png" alt="StackPort dashboard" width="800" height="1338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Register SecureString Parameters in SSM Parameter Store
&lt;/h3&gt;

&lt;p&gt;I register the API key and the Webhook URL outside Terraform.&lt;/p&gt;

&lt;p&gt;If you create them as &lt;code&gt;aws_ssm_parameter&lt;/code&gt; resources in Terraform, the values remain in the state file in plain text. Write-only arguments (&lt;code&gt;value_wo&lt;/code&gt;) in Terraform 1.11 and later keep them out of the state, but I don't use them here.&lt;/p&gt;

&lt;p&gt;I prepared a script (&lt;code&gt;scripts/put-parameters.sh&lt;/code&gt;) to register them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

: &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SLACK_WEBHOOK_URL&lt;/span&gt;:?Set&lt;span class="p"&gt; SLACK_WEBHOOK_URL&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;test
export &lt;/span&gt;&lt;span class="nv"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;test
export &lt;/span&gt;&lt;span class="nv"&gt;AWS_DEFAULT_REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ap-northeast-1
&lt;span class="nv"&gt;ENDPOINT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:4566

aws &lt;span class="nt"&gt;--endpoint-url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ENDPOINT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; ssm put-parameter &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; /notify-demo/api-key &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--type&lt;/span&gt; SecureString &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--value&lt;/span&gt; local-dummy-api-key &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--overwrite&lt;/span&gt;

aws &lt;span class="nt"&gt;--endpoint-url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ENDPOINT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; ssm put-parameter &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; /notify-demo/slack-webhook-url &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--type&lt;/span&gt; SecureString &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--value&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SLACK_WEBHOOK_URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--overwrite&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the Webhook URL, I use an Incoming Webhook issued for a Slack channel dedicated to local testing (&lt;code&gt;#notify-demo-local&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;SLACK_WEBHOOK_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;Webhook URL of the &lt;span class="nb"&gt;local &lt;/span&gt;channel&amp;gt; ./scripts/put-parameters.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running the script adds the parameters.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faktqrwbcogqpo9s0eltb.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faktqrwbcogqpo9s0eltb.png" alt="StackPort SSM parameter list" width="800" height="604"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The displayed value is &lt;code&gt;ENCRYPTED:&lt;/code&gt; followed by a base64 string. It is not encrypted with KMS, and anyone can decode it back to the original value, so be careful when sharing screenshots.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frhbpiswuymm2qd271inx.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frhbpiswuymm2qd271inx.png" alt="A SecureString value as seen in StackPort" width="799" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Write the Lambda Code
&lt;/h3&gt;

&lt;p&gt;This is the Lambda function that calls the API (&lt;code&gt;fetch_status&lt;/code&gt;). It gets the API key from SSM and publishes the API response to SNS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urllib.request&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;

&lt;span class="n"&gt;ssm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ssm&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;sns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sns&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;API_BASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;API_BASE_URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;API_KEY_PARAM&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;API_KEY_PARAM&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;RESULT_TOPIC_ARN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;RESULT_TOPIC_ARN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;_api_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_api_key&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# Fetch from SSM only on a cold start; use the cache afterwards
&lt;/span&gt;    &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;_api_key&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;_api_key&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ssm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_parameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;API_KEY_PARAM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WithDecryption&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;_api_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Parameter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_api_key&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;API_BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/v1/status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;x-api-key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;get_api_key&lt;/span&gt;&lt;span class="p"&gt;()},&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# 4xx/5xx raise HTTPError, which makes the Lambda invocation fail
&lt;/span&gt;    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;API status: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;sns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;TopicArn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;RESULT_TOPIC_ARN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Subject&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;API status check&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ensure_ascii&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the Lambda function for Slack notifications (&lt;code&gt;notify_slack&lt;/code&gt;). It extracts the SNS message and posts it to the Webhook.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Records&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sns&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&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="nf"&gt;astimezone&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;%Y-%m-%d %H:%M:%S %Z&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;:white_check_mark: API status: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

        &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nf"&gt;get_webhook_url&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&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;with&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;pass&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Neither piece of code contains any MiniStack-specific endpoint configuration. MiniStack injects &lt;code&gt;AWS_ENDPOINT_URL&lt;/code&gt; into the Lambda execution environment, so boto3 automatically points at MiniStack. The same code can be deployed to real AWS as is.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Create the Resources with Terraform
&lt;/h3&gt;

&lt;p&gt;The resources live in the shared module (&lt;code&gt;terraform/modules/notify/main.tf&lt;/code&gt;). I don't show every resource. Instead, I excerpt the points I designed to work on both MiniStack and real AWS.&lt;/p&gt;

&lt;p&gt;The external API URL is passed through the module variable &lt;code&gt;api_base_url&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;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_lambda_function"&lt;/span&gt; &lt;span class="s2"&gt;"fetch_status"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;function_name&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetch_name&lt;/span&gt;
  &lt;span class="nx"&gt;role&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetch_status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
  &lt;span class="nx"&gt;runtime&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"python3.14"&lt;/span&gt;
  &lt;span class="nx"&gt;handler&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"app.handler"&lt;/span&gt;
  &lt;span class="nx"&gt;filename&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;archive_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetch_status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;output_path&lt;/span&gt;
  &lt;span class="nx"&gt;source_code_hash&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;archive_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetch_status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;output_base64sha256&lt;/span&gt;
  &lt;span class="nx"&gt;timeout&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;

  &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;variables&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;API_BASE_URL&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api_base_url&lt;/span&gt;
      &lt;span class="nx"&gt;API_KEY_PARAM&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api_key_param&lt;/span&gt;
      &lt;span class="nx"&gt;RESULT_TOPIC_ARN&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_sns_topic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;result&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="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_cloudwatch_log_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetch_status&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;code&gt;terraform/environments/&lt;/code&gt; contains one directory per environment (&lt;code&gt;local&lt;/code&gt; and &lt;code&gt;aws&lt;/code&gt;). Each has a &lt;code&gt;main.tf&lt;/code&gt; that calls the shared module and its own environment-specific settings.&lt;/p&gt;

&lt;p&gt;The only differences are &lt;code&gt;providers.tf&lt;/code&gt;, which decides the connection target, and &lt;code&gt;terraform.tfvars&lt;/code&gt;, which decides the external API URL.&lt;/p&gt;

&lt;p&gt;Because the directories are separate, the state is also separate for each environment.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;terraform/environments/local/providers.tf&lt;/code&gt;, the provider points only the services I use at MiniStack.&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;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;access_key&lt;/span&gt;                  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"test"&lt;/span&gt;
  &lt;span class="nx"&gt;secret_key&lt;/span&gt;                  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"test"&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt;                      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ap-northeast-1"&lt;/span&gt;
  &lt;span class="nx"&gt;skip_credentials_validation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;skip_metadata_api_check&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;skip_requesting_account_id&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="nx"&gt;endpoints&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;iam&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"http://localhost:4566"&lt;/span&gt;
    &lt;span class="nx"&gt;lambda&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"http://localhost:4566"&lt;/span&gt;
    &lt;span class="nx"&gt;logs&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"http://localhost:4566"&lt;/span&gt;
    &lt;span class="nx"&gt;scheduler&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"http://localhost:4566"&lt;/span&gt;
    &lt;span class="nx"&gt;sns&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"http://localhost:4566"&lt;/span&gt;
    &lt;span class="nx"&gt;ssm&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"http://localhost:4566"&lt;/span&gt;
    &lt;span class="nx"&gt;sts&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"http://localhost:4566"&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;These are the settings that make it work with MiniStack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;access_key&lt;/code&gt;, &lt;code&gt;secret_key&lt;/code&gt;: dummy values (&lt;code&gt;test&lt;/code&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;region&lt;/code&gt;: the region to deploy to&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;skip_credentials_validation&lt;/code&gt;: skips validating credentials through the STS API&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;skip_metadata_api_check&lt;/code&gt;: skips the check against the AWS metadata API&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;skip_requesting_account_id&lt;/code&gt;: skips retrieving the account ID&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;endpoints&lt;/code&gt;: points each service at MiniStack. Specify only the services you use&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The local directory (&lt;code&gt;terraform/environments/local/main.tf&lt;/code&gt;) calls the module. The external API URL is passed through the variable &lt;code&gt;api_base_url&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;"notify"&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;"../../modules/notify"&lt;/span&gt;

  &lt;span class="nx"&gt;region&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ap-northeast-1"&lt;/span&gt;
  &lt;span class="nx"&gt;api_base_url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api_base_url&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The variable's value goes in &lt;code&gt;terraform/environments/local/terraform.tfvars&lt;/code&gt;. Because this file is not managed in Git, create it by copying the template (&lt;code&gt;terraform.tfvars.example&lt;/code&gt;). The Lambda function runs inside the MiniStack container, so it reaches &lt;code&gt;mock-api&lt;/code&gt; by its Compose service name.&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;api_base_url&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"http://mock-api:8000"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;terraform/environments/local
&lt;span class="nb"&gt;cp &lt;/span&gt;terraform.tfvars.example terraform.tfvars
&lt;span class="c"&gt;# Edit terraform.tfvars if needed&lt;/span&gt;
terraform init
terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the result. The resources are created in MiniStack's default account ID (&lt;code&gt;000000000000&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;terraform apply
module.notify.data.archive_file.notify_slack: Reading...
module.notify.data.archive_file.fetch_status: Reading...
module.notify.data.archive_file.notify_slack: Read &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;68fb7c506afbeeab169fb65494efc63dd8acba90]
module.notify.data.archive_file.fetch_status: Read &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;013b111cb8285884b651e501fd6f84ad373f787a]
module.notify.data.aws_iam_policy_document.lambda_assume: Reading...
module.notify.data.aws_caller_identity.current: Reading...
module.notify.data.aws_iam_policy_document.lambda_assume: Read &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2690255455]
module.notify.data.aws_caller_identity.current: Read &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;000000000000]
module.notify.data.aws_iam_policy_document.scheduler_assume: Reading...
module.notify.data.aws_iam_policy_document.notify_slack: Reading...
module.notify.data.aws_iam_policy_document.scheduler_assume: Read &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;644537899]
module.notify.data.aws_iam_policy_document.notify_slack: Read &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2364181876]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create
 &amp;lt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;data resources&lt;span class="o"&gt;)&lt;/span&gt;

Terraform will perform the following actions:

  &lt;span class="c"&gt;# module.notify.data.aws_iam_policy_document.fetch_status will be read during apply&lt;/span&gt;
  &lt;span class="c"&gt;# (config refers to values not yet known)&lt;/span&gt;
 &amp;lt;&lt;span class="o"&gt;=&lt;/span&gt; data &lt;span class="s2"&gt;"aws_iam_policy_document"&lt;/span&gt; &lt;span class="s2"&gt;"fetch_status"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;            &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + json          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + minified_json &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;

      + statement &lt;span class="o"&gt;{&lt;/span&gt;
          + actions   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
              + &lt;span class="s2"&gt;"ssm:GetParameter"&lt;/span&gt;,
            &lt;span class="o"&gt;]&lt;/span&gt;
          + resources &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
              + &lt;span class="s2"&gt;"arn:aws:ssm:ap-northeast-1:000000000000:parameter/notify-demo/api-key"&lt;/span&gt;,
            &lt;span class="o"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
      + statement &lt;span class="o"&gt;{&lt;/span&gt;
          + actions   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
              + &lt;span class="s2"&gt;"sns:Publish"&lt;/span&gt;,
            &lt;span class="o"&gt;]&lt;/span&gt;
          + resources &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
              + &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;,
            &lt;span class="o"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.data.aws_iam_policy_document.scheduler will be read during apply&lt;/span&gt;
  &lt;span class="c"&gt;# (config refers to values not yet known)&lt;/span&gt;
 &amp;lt;&lt;span class="o"&gt;=&lt;/span&gt; data &lt;span class="s2"&gt;"aws_iam_policy_document"&lt;/span&gt; &lt;span class="s2"&gt;"scheduler"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;            &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + json          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + minified_json &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;

      + statement &lt;span class="o"&gt;{&lt;/span&gt;
          + actions   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
              + &lt;span class="s2"&gt;"lambda:InvokeFunction"&lt;/span&gt;,
            &lt;span class="o"&gt;]&lt;/span&gt;
          + resources &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
              + &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;,
            &lt;span class="o"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.aws_cloudwatch_log_group.fetch_status will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_cloudwatch_log_group"&lt;/span&gt; &lt;span class="s2"&gt;"fetch_status"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + arn                         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + deletion_protection_enabled &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;                          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + log_group_class             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + name                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/aws/lambda/notify-demo-fetch-status"&lt;/span&gt;
      + name_prefix                 &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + region                      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ap-northeast-1"&lt;/span&gt;
      + retention_in_days           &lt;span class="o"&gt;=&lt;/span&gt; 7
      + skip_destroy                &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + tags_all                    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.aws_cloudwatch_log_group.notify_slack will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_cloudwatch_log_group"&lt;/span&gt; &lt;span class="s2"&gt;"notify_slack"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + arn                         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + deletion_protection_enabled &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;                          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + log_group_class             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + name                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/aws/lambda/notify-demo-notify-slack"&lt;/span&gt;
      + name_prefix                 &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + region                      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ap-northeast-1"&lt;/span&gt;
      + retention_in_days           &lt;span class="o"&gt;=&lt;/span&gt; 7
      + skip_destroy                &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + tags_all                    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.aws_iam_role.fetch_status will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_iam_role"&lt;/span&gt; &lt;span class="s2"&gt;"fetch_status"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + arn                   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + assume_role_policy    &lt;span class="o"&gt;=&lt;/span&gt; jsonencode&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="o"&gt;{&lt;/span&gt;
              + Statement &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
                  + &lt;span class="o"&gt;{&lt;/span&gt;
                      + Action    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sts:AssumeRole"&lt;/span&gt;
                      + Effect    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;
                      + Principal &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                          + Service &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"lambda.amazonaws.com"&lt;/span&gt;
                        &lt;span class="o"&gt;}&lt;/span&gt;
                    &lt;span class="o"&gt;}&lt;/span&gt;,
                &lt;span class="o"&gt;]&lt;/span&gt;
              + Version   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;)&lt;/span&gt;
      + create_date           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + force_detach_policies &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;                    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + managed_policy_arns   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + max_session_duration  &lt;span class="o"&gt;=&lt;/span&gt; 3600
      + name                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"notify-demo-fetch-status-role"&lt;/span&gt;
      + name_prefix           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + path                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/"&lt;/span&gt;
      + tags_all              &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + unique_id             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;

      + inline_policy &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.aws_iam_role.notify_slack will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_iam_role"&lt;/span&gt; &lt;span class="s2"&gt;"notify_slack"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + arn                   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + assume_role_policy    &lt;span class="o"&gt;=&lt;/span&gt; jsonencode&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="o"&gt;{&lt;/span&gt;
              + Statement &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
                  + &lt;span class="o"&gt;{&lt;/span&gt;
                      + Action    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sts:AssumeRole"&lt;/span&gt;
                      + Effect    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;
                      + Principal &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                          + Service &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"lambda.amazonaws.com"&lt;/span&gt;
                        &lt;span class="o"&gt;}&lt;/span&gt;
                    &lt;span class="o"&gt;}&lt;/span&gt;,
                &lt;span class="o"&gt;]&lt;/span&gt;
              + Version   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;)&lt;/span&gt;
      + create_date           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + force_detach_policies &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;                    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + managed_policy_arns   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + max_session_duration  &lt;span class="o"&gt;=&lt;/span&gt; 3600
      + name                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"notify-demo-notify-slack-role"&lt;/span&gt;
      + name_prefix           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + path                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/"&lt;/span&gt;
      + tags_all              &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + unique_id             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;

      + inline_policy &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.aws_iam_role.scheduler will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_iam_role"&lt;/span&gt; &lt;span class="s2"&gt;"scheduler"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + arn                   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + assume_role_policy    &lt;span class="o"&gt;=&lt;/span&gt; jsonencode&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="o"&gt;{&lt;/span&gt;
              + Statement &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
                  + &lt;span class="o"&gt;{&lt;/span&gt;
                      + Action    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sts:AssumeRole"&lt;/span&gt;
                      + Condition &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                          + StringEquals &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                              + &lt;span class="s2"&gt;"aws:SourceAccount"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"000000000000"&lt;/span&gt;
                            &lt;span class="o"&gt;}&lt;/span&gt;
                        &lt;span class="o"&gt;}&lt;/span&gt;
                      + Effect    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;
                      + Principal &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                          + Service &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"scheduler.amazonaws.com"&lt;/span&gt;
                        &lt;span class="o"&gt;}&lt;/span&gt;
                    &lt;span class="o"&gt;}&lt;/span&gt;,
                &lt;span class="o"&gt;]&lt;/span&gt;
              + Version   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;)&lt;/span&gt;
      + create_date           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + force_detach_policies &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;                    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + managed_policy_arns   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + max_session_duration  &lt;span class="o"&gt;=&lt;/span&gt; 3600
      + name                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"notify-demo-scheduler-role"&lt;/span&gt;
      + name_prefix           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + path                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/"&lt;/span&gt;
      + tags_all              &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + unique_id             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;

      + inline_policy &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.aws_iam_role_policy.fetch_status will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_iam_role_policy"&lt;/span&gt; &lt;span class="s2"&gt;"fetch_status"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + name        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + name_prefix &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + policy      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + role        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.aws_iam_role_policy.notify_slack will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_iam_role_policy"&lt;/span&gt; &lt;span class="s2"&gt;"notify_slack"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + name        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + name_prefix &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + policy      &lt;span class="o"&gt;=&lt;/span&gt; jsonencode&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="o"&gt;{&lt;/span&gt;
              + Statement &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
                  + &lt;span class="o"&gt;{&lt;/span&gt;
                      + Action   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ssm:GetParameter"&lt;/span&gt;
                      + Effect   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;
                      + Resource &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"arn:aws:ssm:ap-northeast-1:000000000000:parameter/notify-demo/slack-webhook-url"&lt;/span&gt;
                    &lt;span class="o"&gt;}&lt;/span&gt;,
                &lt;span class="o"&gt;]&lt;/span&gt;
              + Version   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;)&lt;/span&gt;
      + role        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.aws_iam_role_policy.scheduler will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_iam_role_policy"&lt;/span&gt; &lt;span class="s2"&gt;"scheduler"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + name        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + name_prefix &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + policy      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + role        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.aws_iam_role_policy_attachment.fetch_status_logs will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_iam_role_policy_attachment"&lt;/span&gt; &lt;span class="s2"&gt;"fetch_status_logs"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + policy_arn &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"&lt;/span&gt;
      + role       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"notify-demo-fetch-status-role"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.aws_iam_role_policy_attachment.notify_slack_logs will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_iam_role_policy_attachment"&lt;/span&gt; &lt;span class="s2"&gt;"notify_slack_logs"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + policy_arn &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"&lt;/span&gt;
      + role       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"notify-demo-notify-slack-role"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.aws_lambda_function.fetch_status will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_lambda_function"&lt;/span&gt; &lt;span class="s2"&gt;"fetch_status"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + architectures                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + arn                            &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + code_sha256                    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + filename                       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"./build/fetch_status.zip"&lt;/span&gt;
      + function_name                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"notify-demo-fetch-status"&lt;/span&gt;
      + handler                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"app.handler"&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;                             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + invoke_arn                     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + last_modified                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + memory_size                    &lt;span class="o"&gt;=&lt;/span&gt; 128
      + package_type                   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Zip"&lt;/span&gt;
      + publish                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + qualified_arn                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + qualified_invoke_arn           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + region                         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ap-northeast-1"&lt;/span&gt;
      + reserved_concurrent_executions &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;
      + response_streaming_invoke_arn  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + role                           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + runtime                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"python3.14"&lt;/span&gt;
      + signing_job_arn                &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + signing_profile_version_arn    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + skip_destroy                   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + source_code_hash               &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"So5oKi3b/bEDekVIzzKxoKwo1aO2n/JdG+Ioj2nhtVw="&lt;/span&gt;
      + source_code_size               &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + tags_all                       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + &lt;span class="nb"&gt;timeout&lt;/span&gt;                        &lt;span class="o"&gt;=&lt;/span&gt; 30
      + version                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;

      + environment &lt;span class="o"&gt;{&lt;/span&gt;
          + variables &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

      + ephemeral_storage &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;

      + logging_config &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;

      + tracing_config &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.aws_lambda_function.notify_slack will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_lambda_function"&lt;/span&gt; &lt;span class="s2"&gt;"notify_slack"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + architectures                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + arn                            &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + code_sha256                    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + filename                       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"./build/notify_slack.zip"&lt;/span&gt;
      + function_name                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"notify-demo-notify-slack"&lt;/span&gt;
      + handler                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"app.handler"&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;                             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + invoke_arn                     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + last_modified                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + memory_size                    &lt;span class="o"&gt;=&lt;/span&gt; 128
      + package_type                   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Zip"&lt;/span&gt;
      + publish                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + qualified_arn                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + qualified_invoke_arn           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + region                         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ap-northeast-1"&lt;/span&gt;
      + reserved_concurrent_executions &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;
      + response_streaming_invoke_arn  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + role                           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + runtime                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"python3.14"&lt;/span&gt;
      + signing_job_arn                &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + signing_profile_version_arn    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + skip_destroy                   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + source_code_hash               &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"M+ENHhkFRJqThFhPFiREbrEpiqqEf0cJLzdvTNzrMUY="&lt;/span&gt;
      + source_code_size               &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + tags_all                       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + &lt;span class="nb"&gt;timeout&lt;/span&gt;                        &lt;span class="o"&gt;=&lt;/span&gt; 30
      + version                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;

      + environment &lt;span class="o"&gt;{&lt;/span&gt;
          + variables &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
              + &lt;span class="s2"&gt;"TZ"&lt;/span&gt;                &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Asia/Tokyo"&lt;/span&gt;
              + &lt;span class="s2"&gt;"WEBHOOK_URL_PARAM"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/notify-demo/slack-webhook-url"&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

      + ephemeral_storage &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;

      + logging_config &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;

      + tracing_config &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.aws_lambda_permission.notify_slack_from_sns will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_lambda_permission"&lt;/span&gt; &lt;span class="s2"&gt;"notify_slack_from_sns"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + action              &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"lambda:InvokeFunction"&lt;/span&gt;
      + function_name       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"notify-demo-notify-slack"&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + principal           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sns.amazonaws.com"&lt;/span&gt;
      + region              &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ap-northeast-1"&lt;/span&gt;
      + source_arn          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + statement_id        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AllowExecutionFromSNS"&lt;/span&gt;
      + statement_id_prefix &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.aws_scheduler_schedule.fetch_status will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_scheduler_schedule"&lt;/span&gt; &lt;span class="s2"&gt;"fetch_status"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + action_after_completion      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + arn                          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + group_name                   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;                           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + name                         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"notify-demo-fetch-status"&lt;/span&gt;
      + name_prefix                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + region                       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ap-northeast-1"&lt;/span&gt;
      + schedule_expression          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"rate(1 minute)"&lt;/span&gt;
      + schedule_expression_timezone &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"UTC"&lt;/span&gt;
      + state                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ENABLED"&lt;/span&gt;

      + flexible_time_window &lt;span class="o"&gt;{&lt;/span&gt;
          + mode &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"OFF"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

      + target &lt;span class="o"&gt;{&lt;/span&gt;
          + arn      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
          + input    &lt;span class="o"&gt;=&lt;/span&gt; jsonencode&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="o"&gt;{&lt;/span&gt;
                  + &lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"scheduler"&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;)&lt;/span&gt;
          + role_arn &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.aws_sns_topic.result will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_sns_topic"&lt;/span&gt; &lt;span class="s2"&gt;"result"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + arn                         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + beginning_archive_time      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + content_based_deduplication &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + fifo_throughput_scope       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + fifo_topic                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;                          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + maximum_message_size        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + name                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"notify-demo-result"&lt;/span&gt;
      + name_prefix                 &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + owner                       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + policy                      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + region                      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ap-northeast-1"&lt;/span&gt;
      + signature_version           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + tags_all                    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + tracing_config              &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.aws_sns_topic_subscription.result_to_notify_slack will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_sns_topic_subscription"&lt;/span&gt; &lt;span class="s2"&gt;"result_to_notify_slack"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + arn                             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + confirmation_timeout_in_minutes &lt;span class="o"&gt;=&lt;/span&gt; 1
      + confirmation_was_authenticated  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + endpoint                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + endpoint_auto_confirms          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + filter_policy_scope             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;                              &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + owner_id                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + pending_confirmation            &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + protocol                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"lambda"&lt;/span&gt;
      + raw_message_delivery            &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + region                          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ap-northeast-1"&lt;/span&gt;
      + topic_arn                       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

Plan: 16 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + fetch_status_function_name &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"notify-demo-fetch-status"&lt;/span&gt;

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only &lt;span class="s1"&gt;'yes'&lt;/span&gt; will be accepted to approve.

  Enter a value: &lt;span class="nb"&gt;yes

&lt;/span&gt;module.notify.aws_cloudwatch_log_group.notify_slack: Creating...
module.notify.aws_cloudwatch_log_group.fetch_status: Creating...
module.notify.aws_iam_role.fetch_status: Creating...
module.notify.aws_iam_role.scheduler: Creating...
module.notify.aws_sns_topic.result: Creating...
module.notify.aws_iam_role.notify_slack: Creating...
module.notify.aws_iam_role.fetch_status: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;notify-demo-fetch-status-role]
module.notify.aws_iam_role.scheduler: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;notify-demo-scheduler-role]
module.notify.aws_iam_role.notify_slack: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;notify-demo-notify-slack-role]
module.notify.aws_cloudwatch_log_group.fetch_status: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/aws/lambda/notify-demo-fetch-status]
module.notify.aws_cloudwatch_log_group.notify_slack: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/aws/lambda/notify-demo-notify-slack]
module.notify.aws_sns_topic.result: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;arn:aws:sns:ap-northeast-1:000000000000:notify-demo-result]
module.notify.aws_iam_role_policy_attachment.fetch_status_logs: Creating...
module.notify.data.aws_iam_policy_document.fetch_status: Reading...
module.notify.aws_iam_role_policy_attachment.notify_slack_logs: Creating...
module.notify.aws_iam_role_policy.notify_slack: Creating...
module.notify.data.aws_iam_policy_document.fetch_status: Read &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;193309822]
module.notify.aws_lambda_function.notify_slack: Creating...
module.notify.aws_lambda_function.fetch_status: Creating...
module.notify.aws_iam_role_policy_attachment.fetch_status_logs: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;notify-demo-fetch-status-role/arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]
module.notify.aws_iam_role_policy.notify_slack: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;notify-demo-notify-slack-role:terraform-0c334b43e9f77ddf68c66755f5]
module.notify.aws_iam_role_policy_attachment.notify_slack_logs: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;notify-demo-notify-slack-role/arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]
module.notify.aws_iam_role_policy.fetch_status: Creating...
module.notify.aws_iam_role_policy.fetch_status: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;notify-demo-fetch-status-role:terraform-5e557969d0acd65274a217f306]
module.notify.aws_lambda_function.notify_slack: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 5s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;notify-demo-notify-slack]
module.notify.aws_lambda_permission.notify_slack_from_sns: Creating...
module.notify.aws_lambda_permission.notify_slack_from_sns: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;AllowExecutionFromSNS]
module.notify.aws_sns_topic_subscription.result_to_notify_slack: Creating...
module.notify.aws_sns_topic_subscription.result_to_notify_slack: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;arn:aws:sns:ap-northeast-1:000000000000:notify-demo-result:8876cdbc-24f6-49f7-b09e-14fb507ff76e]
module.notify.aws_lambda_function.fetch_status: Still creating... &lt;span class="o"&gt;[&lt;/span&gt;00m10s elapsed]
module.notify.aws_lambda_function.fetch_status: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 10s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;notify-demo-fetch-status]
module.notify.data.aws_iam_policy_document.scheduler: Reading...
module.notify.data.aws_iam_policy_document.scheduler: Read &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1455079044]
module.notify.aws_scheduler_schedule.fetch_status: Creating...
module.notify.aws_iam_role_policy.scheduler: Creating...
module.notify.aws_scheduler_schedule.fetch_status: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;default/notify-demo-fetch-status]
module.notify.aws_iam_role_policy.scheduler: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;notify-demo-scheduler-role:terraform-db943e5a1f8c21b3a1a29f7c69]

Apply &lt;span class="nb"&gt;complete&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; Resources: 16 added, 0 changed, 0 destroyed.

Outputs:

fetch_status_function_name &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"notify-demo-fetch-status"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two functions appear on the StackPort Lambda screen.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpx7aj4p4o4l6i3smjnra.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpx7aj4p4o4l6i3smjnra.png" alt="StackPort Lambda function list" width="800" height="604"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;StackPort does not show schedules, so I check them with the CLI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;aws &lt;span class="nt"&gt;--endpoint-url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:4566 &lt;span class="nt"&gt;--region&lt;/span&gt; ap-northeast-1 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;   scheduler list-schedules
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"Schedules"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
        &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"Arn"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:scheduler:ap-northeast-1:000000000000:schedule/default/notify-demo-fetch-status"&lt;/span&gt;,
            &lt;span class="s2"&gt;"CreationDate"&lt;/span&gt;: &lt;span class="s2"&gt;"2026-10-12T09:56:36.343454+09:00"&lt;/span&gt;,
            &lt;span class="s2"&gt;"GroupName"&lt;/span&gt;: &lt;span class="s2"&gt;"default"&lt;/span&gt;,
            &lt;span class="s2"&gt;"LastModificationDate"&lt;/span&gt;: &lt;span class="s2"&gt;"2026-10-12T09:56:36.343454+09:00"&lt;/span&gt;,
            &lt;span class="s2"&gt;"Name"&lt;/span&gt;: &lt;span class="s2"&gt;"notify-demo-fetch-status"&lt;/span&gt;,
            &lt;span class="s2"&gt;"State"&lt;/span&gt;: &lt;span class="s2"&gt;"ENABLED"&lt;/span&gt;,
            &lt;span class="s2"&gt;"Target"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="s2"&gt;"Arn"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:lambda:ap-northeast-1:000000000000:function:notify-demo-fetch-status"&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&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;h3&gt;
  
  
  Step 5: Verify the Behavior
&lt;/h3&gt;

&lt;p&gt;After a short wait, &lt;code&gt;API status: ok&lt;/code&gt; is posted to &lt;code&gt;#notify-demo-local&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9rd1thwjdcza8k12fjt5.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9rd1thwjdcza8k12fjt5.png" alt="Notification posted to Slack from the local environment" width="517" height="236"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can check the Lambda execution logs on the StackPort CloudWatch Logs screen. First, the log group list shows &lt;code&gt;/aws/lambda/notify-demo-notify-slack&lt;/code&gt; and &lt;code&gt;/aws/lambda/notify-demo-fetch-status&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn2o24azci7bx590mg4rl.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn2o24azci7bx590mg4rl.png" alt="StackPort log group list" width="800" height="604"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Opening &lt;code&gt;/aws/lambda/notify-demo-notify-slack&lt;/code&gt; shows a list of log streams, one per execution.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmxahzvfdhmf1u3akkax9.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmxahzvfdhmf1u3akkax9.png" alt="StackPort log stream list" width="800" height="604"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Opening a log stream shows &lt;code&gt;START&lt;/code&gt;, &lt;code&gt;END&lt;/code&gt;, and &lt;code&gt;REPORT&lt;/code&gt;, plus what the Lambda function printed: the same &lt;code&gt;API status: ok (…)&lt;/code&gt; that went to Slack.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa5pxsbuhlp80kc8b4nid.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa5pxsbuhlp80kc8b4nid.png" alt="StackPort log events" width="800" height="604"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying to Real AWS and Verifying the Same Behavior
&lt;/h2&gt;

&lt;p&gt;I deploy the pipeline I verified locally to a real AWS account and check that it behaves the same way.&lt;/p&gt;

&lt;p&gt;The Lambda code and the Terraform module are shared with the local environment. Only the environment directory (&lt;code&gt;environments/aws&lt;/code&gt;) and the values registered in SSM change.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg4rly1gl2qoy4r8qqxn6.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg4rly1gl2qoy4r8qqxn6.png" alt="Real AWS architecture" width="800" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The thick red borders show what differs from the local environment. An API Gateway created in advance stands in for the external API, and Slack notifications go to the AWS channel (&lt;code&gt;#notify-demo-aws&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Register the SSM Parameters
&lt;/h3&gt;

&lt;p&gt;The API key was generated by the API Gateway. I retrieve its value and register it in SSM as a SecureString, together with the Incoming Webhook URL of the AWS channel.&lt;/p&gt;

&lt;p&gt;Apart from retrieving the API key, the script is the same as the local one, so I omit its contents.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;API_KEY_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;API key ID&amp;gt; &lt;span class="nv"&gt;SLACK_WEBHOOK_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;Webhook URL of the AWS channel&amp;gt; ./scripts/put-parameters-aws.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the Parameter Store in the AWS console after registration.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpc7427c8iifofsvkd28c.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpc7427c8iifofsvkd28c.png" alt="AWS SSM parameter list" width="800" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Opening a parameter shows its SecureString value as &lt;code&gt;********&lt;/code&gt;. Turn on "Show decrypted value" to see the value.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3zjbko0oeqhurn69pi72.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3zjbko0oeqhurn69pi72.png" alt="AWS SSM parameter details" width="800" height="325"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Deploy
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;terraform/environments/aws/providers.tf&lt;/code&gt; has no MiniStack-specific settings.&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;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ap-northeast-1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The module call (&lt;code&gt;terraform/environments/aws/main.tf&lt;/code&gt;) is the same as the local one.&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;"notify"&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;"../../modules/notify"&lt;/span&gt;

  &lt;span class="nx"&gt;region&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ap-northeast-1"&lt;/span&gt;
  &lt;span class="nx"&gt;api_base_url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api_base_url&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only the external API URL changes, in &lt;code&gt;terraform/environments/aws/terraform.tfvars&lt;/code&gt;. This file is not managed in Git either, so create it by copying the template (&lt;code&gt;terraform.tfvars.example&lt;/code&gt;) and rewrite it with the URL of the API Gateway you created in advance.&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;api_base_url&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://&amp;lt;API ID&amp;gt;.execute-api.ap-northeast-1.amazonaws.com/prod"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;terraform/environments/aws
&lt;span class="nb"&gt;cp &lt;/span&gt;terraform.tfvars.example terraform.tfvars
&lt;span class="c"&gt;# Rewrite &amp;lt;API ID&amp;gt; in terraform.tfvars&lt;/span&gt;
terraform init
terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For comparison with the &lt;code&gt;apply&lt;/code&gt; against MiniStack, here is the result. The account ID is masked.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;terraform apply
module.notify.data.archive_file.fetch_status: Reading...
module.notify.data.archive_file.notify_slack: Reading...
module.notify.data.archive_file.notify_slack: Read &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;68fb7c506afbeeab169fb65494efc63dd8acba90]
module.notify.data.archive_file.fetch_status: Read &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;013b111cb8285884b651e501fd6f84ad373f787a]
module.notify.data.aws_iam_policy_document.lambda_assume: Reading...
module.notify.data.aws_caller_identity.current: Reading...
module.notify.data.aws_iam_policy_document.lambda_assume: Read &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2690255455]
module.notify.data.aws_caller_identity.current: Read &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;123456789012]
module.notify.data.aws_iam_policy_document.scheduler_assume: Reading...
module.notify.data.aws_iam_policy_document.notify_slack: Reading...
module.notify.data.aws_iam_policy_document.notify_slack: Read &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3754217979]
module.notify.data.aws_iam_policy_document.scheduler_assume: Read &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;991516426]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create
 &amp;lt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;data resources&lt;span class="o"&gt;)&lt;/span&gt;

Terraform will perform the following actions:

  &lt;span class="c"&gt;# module.notify.data.aws_iam_policy_document.fetch_status will be read during apply&lt;/span&gt;
  &lt;span class="c"&gt;# (config refers to values not yet known)&lt;/span&gt;
 &amp;lt;&lt;span class="o"&gt;=&lt;/span&gt; data &lt;span class="s2"&gt;"aws_iam_policy_document"&lt;/span&gt; &lt;span class="s2"&gt;"fetch_status"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;            &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + json          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + minified_json &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;

      + statement &lt;span class="o"&gt;{&lt;/span&gt;
          + actions   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
              + &lt;span class="s2"&gt;"ssm:GetParameter"&lt;/span&gt;,
            &lt;span class="o"&gt;]&lt;/span&gt;
          + resources &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
              + &lt;span class="s2"&gt;"arn:aws:ssm:ap-northeast-1:123456789012:parameter/notify-demo/api-key"&lt;/span&gt;,
            &lt;span class="o"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
      + statement &lt;span class="o"&gt;{&lt;/span&gt;
          + actions   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
              + &lt;span class="s2"&gt;"sns:Publish"&lt;/span&gt;,
            &lt;span class="o"&gt;]&lt;/span&gt;
          + resources &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
              + &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;,
            &lt;span class="o"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.data.aws_iam_policy_document.scheduler will be read during apply&lt;/span&gt;
  &lt;span class="c"&gt;# (config refers to values not yet known)&lt;/span&gt;
 &amp;lt;&lt;span class="o"&gt;=&lt;/span&gt; data &lt;span class="s2"&gt;"aws_iam_policy_document"&lt;/span&gt; &lt;span class="s2"&gt;"scheduler"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;            &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + json          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + minified_json &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;

      + statement &lt;span class="o"&gt;{&lt;/span&gt;
          + actions   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
              + &lt;span class="s2"&gt;"lambda:InvokeFunction"&lt;/span&gt;,
            &lt;span class="o"&gt;]&lt;/span&gt;
          + resources &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
              + &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;,
            &lt;span class="o"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.aws_cloudwatch_log_group.fetch_status will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_cloudwatch_log_group"&lt;/span&gt; &lt;span class="s2"&gt;"fetch_status"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + arn                         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + deletion_protection_enabled &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;                          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + log_group_class             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + name                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/aws/lambda/notify-demo-fetch-status"&lt;/span&gt;
      + name_prefix                 &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + region                      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ap-northeast-1"&lt;/span&gt;
      + retention_in_days           &lt;span class="o"&gt;=&lt;/span&gt; 7
      + skip_destroy                &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + tags_all                    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.aws_cloudwatch_log_group.notify_slack will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_cloudwatch_log_group"&lt;/span&gt; &lt;span class="s2"&gt;"notify_slack"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + arn                         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + deletion_protection_enabled &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;                          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + log_group_class             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + name                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/aws/lambda/notify-demo-notify-slack"&lt;/span&gt;
      + name_prefix                 &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + region                      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ap-northeast-1"&lt;/span&gt;
      + retention_in_days           &lt;span class="o"&gt;=&lt;/span&gt; 7
      + skip_destroy                &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + tags_all                    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.aws_iam_role.fetch_status will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_iam_role"&lt;/span&gt; &lt;span class="s2"&gt;"fetch_status"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + arn                   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + assume_role_policy    &lt;span class="o"&gt;=&lt;/span&gt; jsonencode&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="o"&gt;{&lt;/span&gt;
              + Statement &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
                  + &lt;span class="o"&gt;{&lt;/span&gt;
                      + Action    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sts:AssumeRole"&lt;/span&gt;
                      + Effect    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;
                      + Principal &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                          + Service &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"lambda.amazonaws.com"&lt;/span&gt;
                        &lt;span class="o"&gt;}&lt;/span&gt;
                    &lt;span class="o"&gt;}&lt;/span&gt;,
                &lt;span class="o"&gt;]&lt;/span&gt;
              + Version   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;)&lt;/span&gt;
      + create_date           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + force_detach_policies &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;                    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + managed_policy_arns   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + max_session_duration  &lt;span class="o"&gt;=&lt;/span&gt; 3600
      + name                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"notify-demo-fetch-status-role"&lt;/span&gt;
      + name_prefix           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + path                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/"&lt;/span&gt;
      + tags_all              &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + unique_id             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;

      + inline_policy &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.aws_iam_role.notify_slack will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_iam_role"&lt;/span&gt; &lt;span class="s2"&gt;"notify_slack"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + arn                   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + assume_role_policy    &lt;span class="o"&gt;=&lt;/span&gt; jsonencode&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="o"&gt;{&lt;/span&gt;
              + Statement &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
                  + &lt;span class="o"&gt;{&lt;/span&gt;
                      + Action    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sts:AssumeRole"&lt;/span&gt;
                      + Effect    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;
                      + Principal &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                          + Service &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"lambda.amazonaws.com"&lt;/span&gt;
                        &lt;span class="o"&gt;}&lt;/span&gt;
                    &lt;span class="o"&gt;}&lt;/span&gt;,
                &lt;span class="o"&gt;]&lt;/span&gt;
              + Version   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;)&lt;/span&gt;
      + create_date           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + force_detach_policies &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;                    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + managed_policy_arns   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + max_session_duration  &lt;span class="o"&gt;=&lt;/span&gt; 3600
      + name                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"notify-demo-notify-slack-role"&lt;/span&gt;
      + name_prefix           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + path                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/"&lt;/span&gt;
      + tags_all              &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + unique_id             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;

      + inline_policy &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.aws_iam_role.scheduler will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_iam_role"&lt;/span&gt; &lt;span class="s2"&gt;"scheduler"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + arn                   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + assume_role_policy    &lt;span class="o"&gt;=&lt;/span&gt; jsonencode&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="o"&gt;{&lt;/span&gt;
              + Statement &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
                  + &lt;span class="o"&gt;{&lt;/span&gt;
                      + Action    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sts:AssumeRole"&lt;/span&gt;
                      + Condition &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                          + StringEquals &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                              + &lt;span class="s2"&gt;"aws:SourceAccount"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"123456789012"&lt;/span&gt;
                            &lt;span class="o"&gt;}&lt;/span&gt;
                        &lt;span class="o"&gt;}&lt;/span&gt;
                      + Effect    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;
                      + Principal &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                          + Service &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"scheduler.amazonaws.com"&lt;/span&gt;
                        &lt;span class="o"&gt;}&lt;/span&gt;
                    &lt;span class="o"&gt;}&lt;/span&gt;,
                &lt;span class="o"&gt;]&lt;/span&gt;
              + Version   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;)&lt;/span&gt;
      + create_date           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + force_detach_policies &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;                    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + managed_policy_arns   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + max_session_duration  &lt;span class="o"&gt;=&lt;/span&gt; 3600
      + name                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"notify-demo-scheduler-role"&lt;/span&gt;
      + name_prefix           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + path                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/"&lt;/span&gt;
      + tags_all              &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + unique_id             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;

      + inline_policy &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.aws_iam_role_policy.fetch_status will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_iam_role_policy"&lt;/span&gt; &lt;span class="s2"&gt;"fetch_status"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + name        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + name_prefix &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + policy      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + role        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.aws_iam_role_policy.notify_slack will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_iam_role_policy"&lt;/span&gt; &lt;span class="s2"&gt;"notify_slack"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + name        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + name_prefix &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + policy      &lt;span class="o"&gt;=&lt;/span&gt; jsonencode&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="o"&gt;{&lt;/span&gt;
              + Statement &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
                  + &lt;span class="o"&gt;{&lt;/span&gt;
                      + Action   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ssm:GetParameter"&lt;/span&gt;
                      + Effect   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;
                      + Resource &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"arn:aws:ssm:ap-northeast-1:123456789012:parameter/notify-demo/slack-webhook-url"&lt;/span&gt;
                    &lt;span class="o"&gt;}&lt;/span&gt;,
                &lt;span class="o"&gt;]&lt;/span&gt;
              + Version   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;)&lt;/span&gt;
      + role        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.aws_iam_role_policy.scheduler will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_iam_role_policy"&lt;/span&gt; &lt;span class="s2"&gt;"scheduler"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + name        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + name_prefix &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + policy      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + role        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.aws_iam_role_policy_attachment.fetch_status_logs will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_iam_role_policy_attachment"&lt;/span&gt; &lt;span class="s2"&gt;"fetch_status_logs"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + policy_arn &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"&lt;/span&gt;
      + role       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"notify-demo-fetch-status-role"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.aws_iam_role_policy_attachment.notify_slack_logs will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_iam_role_policy_attachment"&lt;/span&gt; &lt;span class="s2"&gt;"notify_slack_logs"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + policy_arn &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"&lt;/span&gt;
      + role       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"notify-demo-notify-slack-role"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.aws_lambda_function.fetch_status will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_lambda_function"&lt;/span&gt; &lt;span class="s2"&gt;"fetch_status"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + architectures                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + arn                            &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + code_sha256                    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + filename                       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"./build/fetch_status.zip"&lt;/span&gt;
      + function_name                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"notify-demo-fetch-status"&lt;/span&gt;
      + handler                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"app.handler"&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;                             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + invoke_arn                     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + last_modified                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + memory_size                    &lt;span class="o"&gt;=&lt;/span&gt; 128
      + package_type                   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Zip"&lt;/span&gt;
      + publish                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + qualified_arn                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + qualified_invoke_arn           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + region                         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ap-northeast-1"&lt;/span&gt;
      + reserved_concurrent_executions &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;
      + response_streaming_invoke_arn  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + role                           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + runtime                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"python3.14"&lt;/span&gt;
      + signing_job_arn                &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + signing_profile_version_arn    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + skip_destroy                   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + source_code_hash               &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"So5oKi3b/bEDekVIzzKxoKwo1aO2n/JdG+Ioj2nhtVw="&lt;/span&gt;
      + source_code_size               &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + tags_all                       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + &lt;span class="nb"&gt;timeout&lt;/span&gt;                        &lt;span class="o"&gt;=&lt;/span&gt; 30
      + version                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;

      + environment &lt;span class="o"&gt;{&lt;/span&gt;
          + variables &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

      + ephemeral_storage &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;

      + logging_config &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;

      + tracing_config &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.aws_lambda_function.notify_slack will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_lambda_function"&lt;/span&gt; &lt;span class="s2"&gt;"notify_slack"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + architectures                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + arn                            &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + code_sha256                    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + filename                       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"./build/notify_slack.zip"&lt;/span&gt;
      + function_name                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"notify-demo-notify-slack"&lt;/span&gt;
      + handler                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"app.handler"&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;                             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + invoke_arn                     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + last_modified                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + memory_size                    &lt;span class="o"&gt;=&lt;/span&gt; 128
      + package_type                   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Zip"&lt;/span&gt;
      + publish                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + qualified_arn                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + qualified_invoke_arn           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + region                         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ap-northeast-1"&lt;/span&gt;
      + reserved_concurrent_executions &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;
      + response_streaming_invoke_arn  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + role                           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + runtime                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"python3.14"&lt;/span&gt;
      + signing_job_arn                &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + signing_profile_version_arn    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + skip_destroy                   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + source_code_hash               &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"M+ENHhkFRJqThFhPFiREbrEpiqqEf0cJLzdvTNzrMUY="&lt;/span&gt;
      + source_code_size               &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + tags_all                       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + &lt;span class="nb"&gt;timeout&lt;/span&gt;                        &lt;span class="o"&gt;=&lt;/span&gt; 30
      + version                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;

      + environment &lt;span class="o"&gt;{&lt;/span&gt;
          + variables &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
              + &lt;span class="s2"&gt;"TZ"&lt;/span&gt;                &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Asia/Tokyo"&lt;/span&gt;
              + &lt;span class="s2"&gt;"WEBHOOK_URL_PARAM"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/notify-demo/slack-webhook-url"&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

      + ephemeral_storage &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;

      + logging_config &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;

      + tracing_config &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.aws_lambda_permission.notify_slack_from_sns will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_lambda_permission"&lt;/span&gt; &lt;span class="s2"&gt;"notify_slack_from_sns"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + action              &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"lambda:InvokeFunction"&lt;/span&gt;
      + function_name       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"notify-demo-notify-slack"&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + principal           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sns.amazonaws.com"&lt;/span&gt;
      + region              &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ap-northeast-1"&lt;/span&gt;
      + source_arn          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + statement_id        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AllowExecutionFromSNS"&lt;/span&gt;
      + statement_id_prefix &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.aws_scheduler_schedule.fetch_status will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_scheduler_schedule"&lt;/span&gt; &lt;span class="s2"&gt;"fetch_status"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + action_after_completion      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + arn                          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + group_name                   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;                           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + name                         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"notify-demo-fetch-status"&lt;/span&gt;
      + name_prefix                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + region                       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ap-northeast-1"&lt;/span&gt;
      + schedule_expression          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"rate(1 minute)"&lt;/span&gt;
      + schedule_expression_timezone &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"UTC"&lt;/span&gt;
      + state                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ENABLED"&lt;/span&gt;

      + flexible_time_window &lt;span class="o"&gt;{&lt;/span&gt;
          + mode &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"OFF"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

      + target &lt;span class="o"&gt;{&lt;/span&gt;
          + arn      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
          + input    &lt;span class="o"&gt;=&lt;/span&gt; jsonencode&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="o"&gt;{&lt;/span&gt;
                  + &lt;span class="nb"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"scheduler"&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;)&lt;/span&gt;
          + role_arn &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.aws_sns_topic.result will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_sns_topic"&lt;/span&gt; &lt;span class="s2"&gt;"result"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + arn                         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + beginning_archive_time      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + content_based_deduplication &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + fifo_throughput_scope       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + fifo_topic                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;                          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + maximum_message_size        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + name                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"notify-demo-result"&lt;/span&gt;
      + name_prefix                 &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + owner                       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + policy                      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + region                      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ap-northeast-1"&lt;/span&gt;
      + signature_version           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + tags_all                    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + tracing_config              &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c"&gt;# module.notify.aws_sns_topic_subscription.result_to_notify_slack will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_sns_topic_subscription"&lt;/span&gt; &lt;span class="s2"&gt;"result_to_notify_slack"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + arn                             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + confirmation_timeout_in_minutes &lt;span class="o"&gt;=&lt;/span&gt; 1
      + confirmation_was_authenticated  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + endpoint                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + endpoint_auto_confirms          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + filter_policy_scope             &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;                              &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + owner_id                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + pending_confirmation            &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + protocol                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"lambda"&lt;/span&gt;
      + raw_message_delivery            &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + region                          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ap-northeast-1"&lt;/span&gt;
      + topic_arn                       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

Plan: 16 to add, 0 to change, 0 to destroy.

Changes to Outputs:
  + fetch_status_function_name &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"notify-demo-fetch-status"&lt;/span&gt;

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only &lt;span class="s1"&gt;'yes'&lt;/span&gt; will be accepted to approve.

  Enter a value: &lt;span class="nb"&gt;yes

&lt;/span&gt;module.notify.aws_cloudwatch_log_group.fetch_status: Creating...
module.notify.aws_iam_role.fetch_status: Creating...
module.notify.aws_iam_role.notify_slack: Creating...
module.notify.aws_cloudwatch_log_group.notify_slack: Creating...
module.notify.aws_sns_topic.result: Creating...
module.notify.aws_iam_role.scheduler: Creating...
module.notify.aws_cloudwatch_log_group.fetch_status: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/aws/lambda/notify-demo-fetch-status]
module.notify.aws_cloudwatch_log_group.notify_slack: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/aws/lambda/notify-demo-notify-slack]
module.notify.aws_sns_topic.result: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;arn:aws:sns:ap-northeast-1:123456789012:notify-demo-result]
module.notify.data.aws_iam_policy_document.fetch_status: Reading...
module.notify.data.aws_iam_policy_document.fetch_status: Read &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3535791877]
module.notify.aws_iam_role.notify_slack: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 1s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;notify-demo-notify-slack-role]
module.notify.aws_iam_role.scheduler: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 1s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;notify-demo-scheduler-role]
module.notify.aws_iam_role_policy_attachment.notify_slack_logs: Creating...
module.notify.aws_iam_role_policy.notify_slack: Creating...
module.notify.aws_lambda_function.notify_slack: Creating...
module.notify.aws_iam_role.fetch_status: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 1s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;notify-demo-fetch-status-role]
module.notify.aws_iam_role_policy_attachment.fetch_status_logs: Creating...
module.notify.aws_iam_role_policy.fetch_status: Creating...
module.notify.aws_lambda_function.fetch_status: Creating...
module.notify.aws_iam_role_policy_attachment.notify_slack_logs: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 1s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;notify-demo-notify-slack-role/arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]
module.notify.aws_iam_role_policy_attachment.fetch_status_logs: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 1s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;notify-demo-fetch-status-role/arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole]
module.notify.aws_iam_role_policy.notify_slack: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 1s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;notify-demo-notify-slack-role:terraform-22dd2cc9194bee349a41b83b49]
module.notify.aws_iam_role_policy.fetch_status: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 1s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;notify-demo-fetch-status-role:terraform-70ee3cf4543eabb9406936afc3]
module.notify.aws_lambda_function.notify_slack: Still creating... &lt;span class="o"&gt;[&lt;/span&gt;00m10s elapsed]
module.notify.aws_lambda_function.fetch_status: Still creating... &lt;span class="o"&gt;[&lt;/span&gt;00m10s elapsed]
module.notify.aws_lambda_function.notify_slack: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 15s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;notify-demo-notify-slack]
module.notify.aws_lambda_permission.notify_slack_from_sns: Creating...
module.notify.aws_lambda_permission.notify_slack_from_sns: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;AllowExecutionFromSNS]
module.notify.aws_sns_topic_subscription.result_to_notify_slack: Creating...
module.notify.aws_sns_topic_subscription.result_to_notify_slack: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;arn:aws:sns:ap-northeast-1:123456789012:notify-demo-result:c13350e2-427f-446b-b1ff-341576727510]
module.notify.aws_lambda_function.fetch_status: Still creating... &lt;span class="o"&gt;[&lt;/span&gt;00m20s elapsed]
module.notify.aws_lambda_function.fetch_status: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 21s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;notify-demo-fetch-status]
module.notify.data.aws_iam_policy_document.scheduler: Reading...
module.notify.data.aws_iam_policy_document.scheduler: Read &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2990400390]
module.notify.aws_scheduler_schedule.fetch_status: Creating...
module.notify.aws_iam_role_policy.scheduler: Creating...
module.notify.aws_scheduler_schedule.fetch_status: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;default/notify-demo-fetch-status]
module.notify.aws_iam_role_policy.scheduler: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 0s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;notify-demo-scheduler-role:terraform-229c8ea50921716c957135f6a7]

Apply &lt;span class="nb"&gt;complete&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; Resources: 16 added, 0 changed, 0 destroyed.

Outputs:

fetch_status_function_name &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"notify-demo-fetch-status"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I check the schedule with the CLI, as I did locally. The account ID is masked.&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="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;aws&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;--region&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ap-northeast&lt;/span&gt;&lt;span class="mi"&gt;-1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;scheduler&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;list-schedules&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;"Schedules"&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;span class="nl"&gt;"Arn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:scheduler:ap-northeast-1:123456789012:schedule/default/notify-demo-fetch-status"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"CreationDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-12T10:28:05.265000+09:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"GroupName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"LastModificationDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-12T10:28:05.265000+09:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"notify-demo-fetch-status"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"State"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ENABLED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"Target"&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;"Arn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:lambda:ap-northeast-1:123456789012:function:notify-demo-fetch-status"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="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;h3&gt;
  
  
  Step 3: Verify the Behavior
&lt;/h3&gt;

&lt;p&gt;After waiting about a minute from the first invocation, &lt;code&gt;API status: ok&lt;/code&gt; is posted to &lt;code&gt;#notify-demo-aws&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftlmpe8pezjales1q158a.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftlmpe8pezjales1q158a.png" alt="Notification posted to Slack" width="513" height="168"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can check the Lambda logs in CloudWatch Logs. In the console, open the log group, the log stream, and the log events in that order.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv4nxzuprhmj36ezzqbvd.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv4nxzuprhmj36ezzqbvd.png" alt="AWS log group list" width="800" height="263"&gt;&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fakicunijbjhfbc4f4nn9.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fakicunijbjhfbc4f4nn9.png" alt="AWS log stream list" width="800" height="279"&gt;&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkcnqw50sqrldc2jvor7e.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkcnqw50sqrldc2jvor7e.png" alt="AWS log events" width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;I built the pipeline from Scheduler through Lambda and SNS to Slack notifications locally with MiniStack. The Lambda code is shared with real AWS, and Terraform uses a shared module with one directory per environment, so the pipeline could be deployed to real AWS just by switching directories.&lt;/li&gt;
&lt;li&gt;With StackPort, you can follow Lambda logs and SSM parameters on screen, which makes it easier to see what is going on than with the CLI alone.&lt;/li&gt;
&lt;li&gt;That said, MiniStack and StackPort do not simulate AWS completely. For example, SecureString values are not encrypted, and StackPort cannot display Scheduler. I recommend understanding what you can test locally, and then verifying on real AWS as well.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thank you for reading this far!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>ministack</category>
      <category>stackport</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Why S3 list-buckets Returns a CreationDate That Doesn't Match the Actual Bucket Creation Date</title>
      <dc:creator>Yoshiyuki Watanabe</dc:creator>
      <pubDate>Sat, 08 Aug 2026 00:18:03 +0000</pubDate>
      <link>https://dev.to/aws-builders/why-s3-list-buckets-returns-a-creationdate-that-doesnt-match-the-actual-bucket-creation-date-a99</link>
      <guid>https://dev.to/aws-builders/why-s3-list-buckets-returns-a-creationdate-that-doesnt-match-the-actual-bucket-creation-date-a99</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I manage multiple AWS accounts, and while taking inventory of S3 buckets and their creation dates across accounts with &lt;code&gt;list-buckets&lt;/code&gt;, I noticed that the &lt;code&gt;CreationDate&lt;/code&gt; returned by &lt;code&gt;aws s3api list-buckets&lt;/code&gt; didn't match the actual bucket creation date shown in the S3 console.&lt;/p&gt;

&lt;p&gt;I asked about this on AWS re:Post and got solid answers from the community.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://repost.aws/ja/questions/QUI9bguausRtiUasW3rfV_Cw/why-does-listbuckets-return-incorrect-creationdate-when-called-from-a-non-us-east-1-endpoint" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fa0.awsstatic.com%2Flibra-css%2Fimages%2Flogos%2Faws_logo_smile_1200x630.png" height="420" class="m-0" width="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://repost.aws/ja/questions/QUI9bguausRtiUasW3rfV_Cw/why-does-listbuckets-return-incorrect-creationdate-when-called-from-a-non-us-east-1-endpoint" rel="noopener noreferrer" class="c-link"&gt;
            Why does ListBuckets return incorrect CreationDate when called from a non-us-east-1 endpoint? | AWS re:Post
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            When I call `aws s3api list-buckets` from a regional endpoint other than `us-east-1` (e.g., `ap-northeast-1`), the `CreationDate` field returns an incorrect date that does not match the actual buck...
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Frepost.aws%2Ffavicon-32x32.png" width="32" height="32"&gt;
          repost.aws
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Digging further, I found a GitHub Issue on &lt;code&gt;aws-cli&lt;/code&gt; with information about this exact issue.&lt;/p&gt;

&lt;p&gt;This article cross-references the re:Post answers with that Issue to lay out the cause and the workaround.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Symptom: CreationDate Changes When Called from a Non-us-east-1 Region
&lt;/h2&gt;

&lt;p&gt;Calling &lt;code&gt;list-buckets&lt;/code&gt; against the same bucket returns a different &lt;code&gt;CreationDate&lt;/code&gt; depending on which regional endpoint you use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Called via the ap-northeast-1 endpoint&lt;/span&gt;
aws s3api list-buckets &lt;span class="nt"&gt;--region&lt;/span&gt; ap-northeast-1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Buckets[?Name==`my-bucket`].CreationDate'&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; text
&lt;span class="c"&gt;# 2026-06-04T02:48:47+00:00&lt;/span&gt;

&lt;span class="c"&gt;# Called via the us-east-1 endpoint&lt;/span&gt;
aws s3api list-buckets &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Buckets[?Name==`my-bucket`].CreationDate'&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; text
&lt;span class="c"&gt;# 2023-07-05T08:39:18+00:00&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This bucket was created in ap-northeast-1, and the S3 console shows its creation date as &lt;code&gt;July 5, 2023, 17:39:18 (UTC+09:00)&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9tb4146km4hql6he3hlz.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9tb4146km4hql6he3hlz.png" alt="Creation date shown in the Creation date column of the S3 console bucket list" width="797" height="83"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In other words, the &lt;code&gt;us-east-1&lt;/code&gt; result matches the console, while only the &lt;code&gt;ap-northeast-1&lt;/code&gt; result is off from the actual creation date.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Cause: CreationDate Isn't the "Creation Date" — It's the "Last Replication Time in That Region"
&lt;/h2&gt;

&lt;p&gt;The investigation revealed that the meaning of the &lt;code&gt;CreationDate&lt;/code&gt; returned by &lt;code&gt;list-buckets&lt;/code&gt; depends on which regional endpoint you call it from.&lt;/p&gt;

&lt;p&gt;All S3 bucket creation operations appear to be mastered in &lt;code&gt;us-east-1&lt;/code&gt; and then replicated globally.&lt;/p&gt;

&lt;p&gt;When you call &lt;code&gt;list-buckets&lt;/code&gt; against an endpoint other than &lt;code&gt;us-east-1&lt;/code&gt;, the &lt;code&gt;CreationDate&lt;/code&gt; you get back isn't the actual creation date — it's &lt;strong&gt;the last replication time for that region&lt;/strong&gt; (i.e., the timestamp of the most recent sync, such as a bucket policy change).&lt;/p&gt;

&lt;p&gt;Only when you call the &lt;code&gt;us-east-1&lt;/code&gt; endpoint do you get the original creation date registered at the master. The S3 console also reads from the &lt;code&gt;us-east-1&lt;/code&gt; data, which is why the console date always matches the &lt;code&gt;us-east-1&lt;/code&gt; endpoint's result.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;aws-cli&lt;/code&gt; GitHub Issue includes the following explanation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;All bucket creations are mastered in us-east-1, then replicated on a global scale - the resulting difference is that there are no "replication" events to the us-east-1 region.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;When using an endpoint other than us-east-1, the &lt;code&gt;CreationDate&lt;/code&gt; you receive is actually the last modified time according to the bucket's last replication time in this region. This date can change when making changes to your bucket, such as editing its bucket policy.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Source: &lt;a href="https://github.com/aws/aws-cli/issues/3597#issuecomment-424167129" rel="noopener noreferrer"&gt;aws/aws-cli#3597&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The official AWS documentation for the &lt;code&gt;Bucket&lt;/code&gt; data type's &lt;code&gt;CreationDate&lt;/code&gt; field also includes this line:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Date the bucket was created. This date can change when making changes to your bucket, such as editing its bucket policy.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Source: &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/API/API_Bucket.html" rel="noopener noreferrer"&gt;Amazon S3 API Reference - Bucket&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;However, this sentence alone doesn't make it clear that the behavior varies by region — it's an easy detail for many users to miss.&lt;/p&gt;

&lt;p&gt;I confirmed this myself: after editing a bucket policy, only the &lt;code&gt;CreationDate&lt;/code&gt; returned via the &lt;code&gt;ap-northeast-1&lt;/code&gt; endpoint changed. Changing &lt;code&gt;Block public access&lt;/code&gt; produced the same result.&lt;/p&gt;

&lt;p&gt;It seems any bucket-level configuration change — not just bucket policy edits — can trigger this replication timestamp update.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# After the configuration change&lt;/span&gt;
aws s3api list-buckets &lt;span class="nt"&gt;--region&lt;/span&gt; ap-northeast-1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Buckets[?Name==`my-bucket`].CreationDate'&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; text
&lt;span class="c"&gt;# 2026-08-06T21:20:49+00:00&lt;/span&gt;

aws s3api list-buckets &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Buckets[?Name==`my-bucket`].CreationDate'&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; text
&lt;span class="c"&gt;# 2023-07-05T08:39:18+00:00&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Workaround: Explicitly Specify the us-east-1 Endpoint
&lt;/h2&gt;

&lt;p&gt;To get the accurate creation date, explicitly call the API with &lt;code&gt;--region us-east-1&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3api list-buckets &lt;span class="nt"&gt;--region&lt;/span&gt; us-east-1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Buckets[?Name==`my-bucket`].CreationDate'&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; text
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This result matches the creation date shown in the S3 console.&lt;/p&gt;

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

&lt;p&gt;The &lt;code&gt;CreationDate&lt;/code&gt; returned by &lt;code&gt;list-buckets&lt;/code&gt; has a quirk: calling it from any endpoint other than &lt;code&gt;us-east-1&lt;/code&gt; returns the last replication time for that region, not the actual bucket creation date.&lt;/p&gt;

&lt;p&gt;If you need the accurate creation date, explicitly specify &lt;code&gt;--region us-east-1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I lost some time myself before figuring this out. I hope this article saves someone else from the same trap.&lt;/p&gt;

&lt;p&gt;Thank you for reading this far!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>s3</category>
      <category>beginners</category>
      <category>sre</category>
    </item>
  </channel>
</rss>
