#!/usr/bin/env python3
from aws_cdk import (
Stack,
RemovalPolicy,
aws_ec2 as ec2,
aws_iam as iam,
aws_s3 as s3,
aws_s3_deployment as s3deploy,
aws_mwaa as mwaa,
)
from constructs import Construct
import os
class AirflowStack(Stack):
"""
MWAA Airflow Stack — FIXED (only invalid IAM policy removed)
"""
def __init__(
self,
scope: Construct,
construct_id: str,
*,
store_vpc: ec2.Vpc,
**kwargs,
) -> None:
super().__init__(scope, construct_id, **kwargs)
airflow_env_name = construct_id.lower()
dags_bucket_name = f"{construct_id.lower()}-dags"
# ---------------------------------------------------------------------
# 1️⃣ S3 Bucket (UNCHANGED)
# ---------------------------------------------------------------------
dags_bucket = s3.Bucket(
self,
"AirflowDagsBucket",
bucket_name=dags_bucket_name,
versioned=True,
encryption=s3.BucketEncryption.S3_MANAGED,
block_public_access=s3.BlockPublicAccess.BLOCK_ALL,
enforce_ssl=True,
removal_policy=RemovalPolicy.RETAIN,
)
dags_folder = os.path.join(os.getcwd(), "dags")
if os.path.isdir(dags_folder):
s3deploy.BucketDeployment(
self,
"DeployDags",
sources=[s3deploy.Source.asset(dags_folder)],
destination_bucket=dags_bucket,
destination_key_prefix="dags",
prune=False,
)
# ---------------------------------------------------------------------
# 2️⃣ MWAA EXECUTION ROLE (ONLY FIX IS HERE)
# ---------------------------------------------------------------------
mwaa_role = iam.Role(
self,
"MwaaExecutionRole",
role_name=f"{airflow_env_name}-exec-role",
assumed_by=iam.CompositePrincipal(
iam.ServicePrincipal("airflow.amazonaws.com"),
iam.ServicePrincipal("airflow-env.amazonaws.com"),
),
path="/service-role/",
)
# ❌ REMOVED:
# AmazonMWAASvcRolePolicy DOES NOT EXIST / NOT ATTACHABLE
# mwaa_role.add_managed_policy(
# iam.ManagedPolicy.from_aws_managed_policy_name("AmazonMWAASvcRolePolicy")
# )
# Existing S3 access (UNCHANGED)
mwaa_role.add_managed_policy(
iam.ManagedPolicy.from_aws_managed_policy_name("AmazonS3ReadOnlyAccess")
)
dags_bucket.grant_read_write(mwaa_role)
# Logs (UNCHANGED)
mwaa_role.add_to_policy(
iam.PolicyStatement(
actions=[
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
],
resources=["*"],
)
)
# MWAA internal queues (UNCHANGED)
mwaa_role.add_to_policy(
iam.PolicyStatement(
actions=[
"sqs:SendMessage",
"sqs:ReceiveMessage",
"sqs:DeleteMessage",
"sqs:GetQueueAttributes",
"sqs:GetQueueUrl",
],
resources=["*"],
)
)
# Private VPC auth calls (UNCHANGED)
mwaa_role.add_to_policy(
iam.PolicyStatement(
actions=[
"secretsmanager:GetSecretValue",
"kms:Decrypt",
"sts:GetCallerIdentity",
],
resources=["*"],
)
)
# ---------------------------------------------------------------------
# 3️⃣ SECURITY GROUP (UNCHANGED)
# ---------------------------------------------------------------------
mwaa_sg = ec2.SecurityGroup(
self,
"MwaaSecurityGroup",
vpc=store_vpc,
description="MWAA Security Group",
allow_all_outbound=True,
)
mwaa_sg.add_ingress_rule(
peer=mwaa_sg,
connection=ec2.Port.all_traffic(),
description="MWAA self traffic",
)
mwaa_sg.add_egress_rule(
peer=ec2.Peer.any_ipv4(),
connection=ec2.Port.tcp(443),
description="HTTPS outbound",
)
# ---------------------------------------------------------------------
# 4️⃣ NETWORK CONFIG (UNCHANGED)
# ---------------------------------------------------------------------
private_subnets = store_vpc.select_subnets(
subnet_type=ec2.SubnetType.PRIVATE_WITH_EGRESS
).subnet_ids[:2]
if len(private_subnets) < 2:
raise ValueError("MWAA requires at least 2 private subnets")
network_config = mwaa.CfnEnvironment.NetworkConfigurationProperty(
security_group_ids=[mwaa_sg.security_group_id],
subnet_ids=private_subnets,
)
# ---------------------------------------------------------------------
# 5️⃣ LOGGING (UNCHANGED)
# ---------------------------------------------------------------------
logging_config = mwaa.CfnEnvironment.LoggingConfigurationProperty(
dag_processing_logs=mwaa.CfnEnvironment.ModuleLoggingConfigurationProperty(
enabled=True, log_level="INFO"
),
scheduler_logs=mwaa.CfnEnvironment.ModuleLoggingConfigurationProperty(
enabled=True, log_level="INFO"
),
task_logs=mwaa.CfnEnvironment.ModuleLoggingConfigurationProperty(
enabled=True, log_level="INFO"
),
webserver_logs=mwaa.CfnEnvironment.ModuleLoggingConfigurationProperty(
enabled=True, log_level="INFO"
),
worker_logs=mwaa.CfnEnvironment.ModuleLoggingConfigurationProperty(
enabled=True, log_level="INFO"
),
)
# ---------------------------------------------------------------------
# 6️⃣ MWAA ENVIRONMENT (UNCHANGED)
# ---------------------------------------------------------------------
mwaa_env = mwaa.CfnEnvironment(
self,
"AirflowEnvironment",
name=airflow_env_name,
airflow_version="3.0.6",
environment_class="mw1.micro",
execution_role_arn=mwaa_role.role_arn,
source_bucket_arn=dags_bucket.bucket_arn,
dag_s3_path="dags",
network_configuration=network_config,
logging_configuration=logging_config,
webserver_access_mode="PRIVATE_ONLY",
min_workers=1,
max_workers=1,
)
# ---------------------------------------------------------------------
# 7️⃣ AIRFLOW CONFIG (UNCHANGED)
# ---------------------------------------------------------------------
mwaa_env.add_override(
"Properties.AirflowConfigurationOptions",
{
"core.load_default_connections": "False",
"core.load_examples": "False",
"core.default_timezone": "utc",
},
)
mwaa_env.node.add_dependency(dags_bucket)
mwaa_env.node.add_dependency(mwaa_role)
mwaa_env.node.add_dependency(mwaa_sg)
when used
# AmazonMWAASvcRolePolicy DOES NOT EXIST / NOT ATTACHABLE
# mwaa_role.add_managed_policy(
# iam.ManagedPolicy.from_aws_managed_policy_name("AmazonMWAASvcRolePolicy")
# )
got error airflow failed: ToolkitError: The stack named store-airflow failed creation, it may need to be manually deleted from the AWS console: ROLLBACK_COMPLETE: Resource handler returned message: "Policy arn:aws:iam::aws:policy/AmazonMWAASvcRolePolicy does not exist or is not attachable. (Service: Iam, Status Code: 404, Request ID: 0810ff43-1415-46ce-9186-2e314dbea306) (SDK Attempt Count: 1)" (RequestToken: 0e1d97be-151e-1d53-7602-a02c25634b48, HandlerErrorCode: NotFound)
please do suggest cdk code for this error as well for non-stabilizied error with vpc cdk