DocsAWS 101Blog
← Back to Blog

MWAA, CloudWatch alarm actions, Lambda metrics

May 16, 2026 · v1.3.42

Four ships in this release: MWAA (real Apache Airflow containers, both v2 and v3), CloudWatch alarms that actually publish to SNS, Lambda emitting the four canonical observability metrics on every invocation, and the AWS::ApiGateway::Account CFN resource that unblocks a very common CDK pattern.

MWAA — real Airflow containers for both 2.x and 3.x

boto3.client("mwaa") now works against MiniStack. CreateEnvironment spins up a real apache/airflow:<version> container in standalone mode on the same Docker network as MiniStack, syncs DAGs from your SourceBucketArn + DagS3Path into /opt/airflow/dags/ once the container reaches AVAILABLE, and forces an aggressive DAG scan interval so newly synced files become visible to Airflow within seconds.

The full lifecycle is covered: CreateEnvironment / GetEnvironment / UpdateEnvironment / ListEnvironments / DeleteEnvironment (stops + removes the container, releases the host port). Token helpers CreateWebLoginToken and CreateCliToken return the correct field names per the boto3 MWAA model. InvokeRestApi proxies straight through to the running Airflow REST API:

import boto3, json

mwaa = boto3.client("mwaa", endpoint_url="http://localhost:4566")
s3   = boto3.client("s3",   endpoint_url="http://localhost:4566")

s3.create_bucket(Bucket="my-dags")
s3.put_object(
    Bucket="my-dags",
    Key="dags/hello.py",
    Body=b"""
from datetime import datetime
from airflow import DAG
from airflow.operators.empty import EmptyOperator
with DAG('hello', start_date=datetime(2026,1,1), schedule=None):
    EmptyOperator(task_id='say_hi')
""",
)

mwaa.create_environment(
    Name="local-airflow",
    AirflowVersion="3.0.6",
    DagS3Path="dags/",
    SourceBucketArn="arn:aws:s3:::my-dags",
    ExecutionRoleArn="arn:aws:iam::000000000000:role/r",
    NetworkConfiguration={"SubnetIds": ["s1","s2"], "SecurityGroupIds": ["sg-1"]},
)

# Poll until AVAILABLE, then list DAGs via the InvokeRestApi proxy
resp = mwaa.invoke_rest_api(Name="local-airflow", Method="GET", Path="/dags")
print(resp["RestApiStatusCode"], resp["RestApiResponse"])  # 200, dag list

End-to-end verified locally with real DAGs visible on both apache/airflow:2.10.4 and apache/airflow:3.0.6. Mount the Docker socket into MiniStack (-v /var/run/docker.sock:/var/run/docker.sock) and put both containers on the same network so the spawned Airflow can reach MiniStack S3 and vice versa.

CloudWatch alarm actions actually publish to SNS

Alarms stored AlarmActions for ages but never dispatched them on state transition. That changes now. On every OKALARMINSUFFICIENT_DATA transition, the matching AlarmActions / OKActions / InsufficientDataActions list is consulted and SNS topic ARNs are published with the AWS-shaped payload:

{
  "AlarmName": "errors-too-high",
  "AlarmDescription": "...",
  "AWSAccountId": "000000000000",
  "NewStateValue": "ALARM",
  "NewStateReason": "Threshold Crossed: Sum 5 GreaterThanThreshold 0.0",
  "StateChangeTime": "2026-05-16T18:00:00Z",
  "Region": "us-east-1",
  "OldStateValue": "OK",
  "Trigger": { "MetricName": "Errors", "Namespace": "MyApp",
               "Statistic": "Sum", "Period": 60,
               "EvaluationPeriods": 1, "ComparisonOperator": "GreaterThanThreshold",
               "Threshold": 0.0, "Dimensions": [] }
}

Fires on both SetAlarmState (manual flip) and the auto-evaluation path triggered by PutMetricData. ActionsEnabled=False (or DisableAlarmActions) suppresses dispatch. Closes the single largest inter-service integration gap — alerting-driven test scenarios now work end-to-end against MiniStack.

Lambda emits canonical CloudWatch metrics

Every Lambda invocation now publishes the four canonical AWS/Lambda metrics, dimensioned by FunctionName:

lam.invoke(FunctionName="my-fn", Payload=b"{}")
lam.invoke(FunctionName="my-fn", Payload=b"{}")

stats = cw.get_metric_statistics(
    Namespace="AWS/Lambda",
    MetricName="Invocations",
    Dimensions=[{"Name": "FunctionName", "Value": "my-fn"}],
    StartTime=time.time() - 600, EndTime=time.time(),
    Period=60, Statistics=["Sum"],
)
sum(p["Sum"] for p in stats["Datapoints"])  # → 2.0

Same shape and granularity real CloudWatch emits, so alarms set on AWS/Lambda namespaces now evaluate against real datapoints — and combined with the alarm actions fix above, end-to-end Lambda observability tests (invoke → error → alarm transitions to ALARM → SNS notification arrives) work in one local run.

AWS::ApiGateway::Account unblocks CDK cloudWatchRole: true

The singleton CFN resource that stores CloudWatchRoleArn for the API Gateway account was missing from the supported resource map, so any CDK stack using new RestApi({ cloudWatchRole: true }) failed with Unsupported resource type: AWS::ApiGateway::Account. The new handler writes the role ARN into the same store the runtime UpdateAccount / GetAccount API reads from, so it round-trips end-to-end — CDK stacks reach CREATE_COMPLETE and aws apigateway get-account reflects the value.

Upgrade

docker pull ministackorg/ministack:1.3.42
docker run -d -p 4566:4566 ministackorg/ministack:1.3.42

Or pin in compose.yaml:

services:
  ministack:
    image: ministackorg/ministack:1.3.42
    ports:
      - "4566:4566"

For MWAA, mount the Docker socket and (if you're running MiniStack inside a container) put MiniStack and the Airflow containers on a shared network:

services:
  ministack:
    image: ministackorg/ministack:1.3.42
    ports:
      - "4566:4566"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - DOCKER_NETWORK=ministack-net
    networks: [ministack-net]
networks:
  ministack-net:
    name: ministack-net

Stay in sync

Issues and PRs welcome on GitHub. Discussion on r/ministack.