Esempio n. 1
0
def test_http_api_custom_domain(stack: Stack) -> None:
    """Test basic HTTP API with custom domain."""
    stack.s3_bucket = "cfn_bucket"
    stack.s3_key = "templates/"

    lambda_fun = Py38Function(
        name="mypylambda",
        description="this is a test",
        role="somearn",
        code_dir="my_code_dir",
        handler="app.main",
    )
    stack.add(lambda_fun)
    stack.add(
        HttpApi(
            name="testapi",
            description="this is a test",
            lambda_arn=lambda_fun.ref,
            domain_name="api.example.com",
            hosted_zone_id="ABCDEFG",
            route_list=[GET(route="/api1"),
                        POST(route="/api2")],
        ))
    with open(os.path.join(TEST_DIR,
                           "apigateway_test_custom_domain.json")) as fd:
        expected = json.load(fd)

    assert stack.export()["Resources"] == expected
Esempio n. 2
0
def test_internet_gateway(stack: Stack) -> None:
    """Test InternetGateway construct."""
    vpc = ec2.VPC(
        name_to_id("vpc-test"),
        CidrBlock="10.0.0.0/16",
        EnableDnsHostnames="true",
        EnableDnsSupport="true",
    )

    subnets = [
        ec2.Subnet(
            name_to_id(f"{zone}-subnet"),
            CidrBlock="10.0.0.0/20",
            AvailabilityZone="eu-west-1b",
            VpcId=Ref(vpc),
            MapPublicIpOnLaunch="true",
        ) for zone, ip in zip(["eu-west-1a", "eu-west-1b"],
                              ["10.0.0.0/20", "10.0.16.0/20"])
    ]

    igw = InternetGateway(name_prefix="test", vpc=vpc, subnets=subnets)

    for el in (vpc, *subnets, igw):
        stack.add(el)

    assert stack.export()["Resources"] == EXPECTED_TEMPLATE
Esempio n. 3
0
def test_fargate_task_definition(stack: Stack) -> None:
    """Test fargate task definition creation."""
    docker_container_def = ecs.ContainerDefinition(
        Image="image-uri",
        Name="container-def-name",
        LogConfiguration=ecs.LogConfiguration(
            LogDriver="awslogs",
            Options={
                "awslogs-group": "test-log-group",
                "awslogs-create-group": "true",
                "awslogs-region": "eu-west-1",
                "awslogs-stream-prefix": "test-prefix",
            },
        ),
    )

    stack.add(
        FargateTaskDefinition(
            name="test-fargate-task-definition",
            container_definitions=[docker_container_def],
            task_role_arn="task-role-name",
            cpu="1024",
            memory="4096",
        ))
    assert stack.export()["Resources"] == EXPECTED_FARGATE_TASK_DEFINITION
Esempio n. 4
0
def test_config_rules(stack: Stack) -> None:
    """Test config rules creation."""
    for config_rule in (
        S3BucketPublicWriteProhibited,
        S3BucketPublicReadProhibited,
        S3BucketServerSideEncryptionEnabled,
        S3BucketSSLRequestsOnly,
        IAMUserNoPoliciesCheck,
    ):
        stack.add(config_rule)

    assert stack.export()["Resources"] == EXPECTED_RULES
Esempio n. 5
0
def test_bucket(stack: Stack) -> None:
    """Test bucket creation."""
    stack.s3_bucket = "cfn_bucket"
    stack.s3_key = "templates/"

    topic_test = Topic(name="test-topic")
    queue_test = Queue(name="test-queue")
    lambda_test = Py38Function(
        name="mypylambda",
        description="this is a test",
        role="somearn",
        code_dir="my_code_dir",
        handler="app.main",
    )

    stack.add(topic_test)
    stack.add(lambda_test)
    stack.add(queue_test)

    bucket = Bucket(name="test-bucket")
    bucket.add_notification_configuration(event="s3:ObjectCreated:*",
                                          target=topic_test,
                                          permission_suffix="TpUpload")
    bucket.add_notification_configuration(event="s3:ObjectCreated:*",
                                          target=lambda_test,
                                          permission_suffix="TpUpload")
    bucket.add_notification_configuration(event="s3:ObjectCreated:*",
                                          target=queue_test,
                                          permission_suffix="FileEvent")
    stack.add(bucket)

    with open(os.path.join(TEST_DIR, "bucket.json")) as fd:
        expected_template = json.load(fd)

    assert stack.export()["Resources"] == expected_template
Esempio n. 6
0
def test_role(stack: Stack) -> None:
    """Test IAM role creation.

    Creating a Role also tests PolicyDocument and Policystatement classes.
    """
    stack.add(
        Role(
            name="TestRole",
            description="TestRole description",
            max_session_duration=7200,
            trust={"Service": "test"},
            tags={"TestTagKey": "TestTagValue"},
        ))
    assert stack.export()["Resources"] == EXPECTED_ROLE
Esempio n. 7
0
def test_awslambda(stack: Stack) -> None:
    """Test config recorder creation."""
    stack.s3_bucket = "cfn_bucket"
    stack.s3_key = "templates/"
    stack.add(
        Py38Function(
            name="mypylambda",
            description="this is a test",
            role="somearn",
            code_dir="my_code_dir",
            handler="app.main",
        ))

    assert stack.export()["Resources"] == EXPECTED_TEMPLATE
Esempio n. 8
0
def test_fargate_scheduled_rule(stack: Stack) -> None:
    """Test fargate scheduled rule creation."""
    stack.add(
        FargateScheduledTaskRule(
            description="This is a test rule",
            ecs_cluster=FargateCluster(name="test-cluster"),
            name="test-rule",
            schedule_expression="cron(0/15 * * * ? *)",
            task_names=["test-task"],
            vpc=EcsVPC(name="test-vpc", region="eu-west-1"),
            state="DISABLED",
        ))
    print(stack.export()["Resources"])
    assert stack.export()["Resources"] == EXPECTED_FARGATE_SCHEDULED_RULE
Esempio n. 9
0
def test_bucket_multi_encryption(stack: Stack) -> None:
    """Test bucket accepting multiple types of encryptions and without default."""
    bucket = Bucket(
        name="test-bucket",
        default_bucket_encryption=None,
        authorized_encryptions=[
            EncryptionAlgorithm.AES256, EncryptionAlgorithm.KMS
        ],
    )
    stack.add(bucket)

    with open(os.path.join(TEST_DIR, "bucket_multi_encryption.json")) as fd:
        expected_template = json.load(fd)

    assert stack.export()["Resources"] == expected_template
Esempio n. 10
0
def test_s3_website_distribution(stack: Stack) -> None:
    """Test Cloudfront S3WebsiteDistribution construct."""
    stack.add(
        S3WebsiteDistribution(
            name="test-s3w-dist",
            aliases=["test.s3w.com"],
            bucket_name="host-bucket",
            certificate_arn="acm_arn",
            default_ttl=360,
            lambda_edge_function_arns=["lamba_arn"],
            r53_route_from=[("hosted_zone_id", "test.s3w.com")],
        )
    )

    with open(os.path.join(TEST_DIR, "s3websitedistribution.json")) as fd:
        expected_template = json.load(fd)

    assert stack.export()["Resources"] == expected_template
Esempio n. 11
0
def test_docker_function(stack: Stack) -> None:
    """Test adding docker function to stack."""
    aws_env = AWSEnv(regions=["us-east-1"], stub=True)
    stubber_ecr = aws_env.stub("ecr")

    stubber_ecr.add_response(
        "get_authorization_token",
        {
            "authorizationData": [{
                "authorizationToken":
                base64.b64encode(b"test_user:test_pwd").decode(),
                "proxyEndpoint":
                "test_endpoint",
            }]
        },
        {},
    )

    stack.deploy_session = aws_env
    docker_function = DockerFunction(
        name="dockerfunction",
        description="this is a test",
        role="somearn",
        source_dir=SOURCE_DIR,
        repository_name="e3_aws_test_repository",
        image_tag="test_tag",
    )
    client = docker.from_env()
    try:
        stack.add(docker_function)
    except docker.errors.APIError:
        # Push is expected to fail
        pass
    finally:
        # Always try to remove local test image
        client.images.remove(
            f"e3_aws_test_repository:{docker_function.image_tag}")

    # Add resources without trying to push the image to ECR
    stack.dry_run = True
    stack.add(docker_function)

    assert stack.export()["Resources"] == EXPECTED_DOCKER_FUNCTION
Esempio n. 12
0
def test_stackset(stack: Stack) -> None:
    """test Cloudformation stack set creation."""
    stack.s3_bucket = "cfn_bucket"
    stack.s3_key = "templates/"

    stack_set = StackSet(
        name="stack-set-test",
        description="this is a test",
        regions=["eu-west-1"],
        ous=["test-ou"],
    )

    stack_set.add(
        Role(
            name="TestRole",
            description="TestRole description",
            trust={"Service": "test"},
        )
    )
    stack_set.add_condition("", Equals(AccountId, "test_account_id"))
    stack.add(stack_set)
    assert stack.export()["Resources"] == EXPECTED_TEMPLATE
Esempio n. 13
0
def test_http_api_custom_domain(stack: Stack) -> None:
    """Test basic HTTP API with custom domain."""
    stack.s3_bucket = "cfn_bucket"
    stack.s3_key = "templates/"

    lambda_fun = Py38Function(
        name="mypylambda",
        description="this is a test",
        role="somearn",
        code_dir="my_code_dir",
        handler="app.main",
    )
    stack.add(lambda_fun)
    http_api = HttpApi(
        name="testapi",
        description="this is a test",
        lambda_arn=lambda_fun.ref,
        domain_name="api.example.com",
        hosted_zone_id="ABCDEFG",
        route_list=[
            GET(route="/api1"),
            POST(route="/api2"),
            GET("/api3", auth=JWT_AUTH, authorizer_name="testauthorizer"),
        ],
    )
    http_api.add_jwt_authorizer(
        name="testauthorizer",
        audience=["testaudience"],
        issuer="https://cognito-idp.eu-west-1.amazonaws.com/eu-west-1_test",
    )
    stack.add(http_api)

    with open(os.path.join(TEST_DIR,
                           "apigateway_test_custom_domain.json")) as fd:
        expected = json.load(fd)

    print(stack.export()["Resources"])
    assert stack.export()["Resources"] == expected
Esempio n. 14
0
def test_http_api(stack: Stack) -> None:
    """Test basic HTTP API."""
    stack.s3_bucket = "cfn_bucket"
    stack.s3_key = "templates/"

    lambda_fun = Py38Function(
        name="mypylambda",
        description="this is a test",
        role="somearn",
        code_dir="my_code_dir",
        handler="app.main",
    )
    stack.add(lambda_fun)
    stack.add(
        HttpApi(
            name="testapi",
            description="this is a test",
            lambda_arn=lambda_fun.ref,
            route_list=[GET(route="/api1"),
                        POST(route="/api2")],
        ))

    assert stack.export()["Resources"] == EXPECTED_TEMPLATE
Esempio n. 15
0
def test_vpc(stack: Stack) -> None:
    """Test VPC creation."""
    ecr_endpoint_pd = PolicyDocument(
        statements=[
            Allow(
                action=[
                    "ecr:BatchGetImage",
                    "ecr:GetAuthorizationToken",
                    "ecr:GetDownloadUrlForLayer",
                ],
                resource="*",
                principal="*",
            )
        ]
    )
    s3_endpoint_pd = PolicyDocument(
        statements=[
            Allow(action=["s3:PutObject", "s3:GetObject"], resource="*", principal="*"),
            Allow(action="s3:ListBucket", resource="*", principal="*"),
        ]
    )
    cloudwatch_endpoint_pd = PolicyDocument(
        statements=[
            Allow(
                action=[
                    "logs:CreateLogStream",
                    "logs:CreateLogGroup",
                    "logs:PutLogEvents",
                ],
                resource="*",
                principal="*",
            )
        ]
    )
    sm_endpoint_pd = PolicyDocument(
        statements=[
            Allow(
                action=[
                    "secretsmanager:GetResourcePolicy",
                    "secretsmanager:GetSecretValue",
                    "secretsmanager:DescribeSecret",
                    "secretsmanager:ListSecretVersionIds",
                ],
                resource=["this_is_a_secret_arn"],
                principal="*",
            )
        ]
    )
    stack.add(
        VPC(
            name="TestVPC",
            region="eu-west-1",
            internet_gateway=True,
            nat_gateway=True,
            s3_endpoint_policy_document=s3_endpoint_pd,
            interface_endpoints=[
                ("logs", cloudwatch_endpoint_pd),
                ("ecr.api", ecr_endpoint_pd),
                ("ecr.dkr", ecr_endpoint_pd),
                ("sts", None),
                ("secretsmanager", sm_endpoint_pd),
            ],
        )
    )
    with open(os.path.join(TEST_DIR, "vpc.json")) as fd:
        expected_template = json.load(fd)

    assert stack.export()["Resources"] == expected_template
Esempio n. 16
0
def test_ecs_vpc(stack: Stack) -> None:
    """Test ECS VPC creation."""
    stack.add(EcsVPC(name="test-vpc", region="eu-west-1"))
    assert stack.export()["Resources"] == EXPECTED_ECS_VPC
Esempio n. 17
0
def test_fargate_cluster(stack: Stack) -> None:
    """Test Fargate cluster creation."""
    stack.add(FargateCluster(name="test-cluster"))
    assert stack.export()["Resources"] == EXPECTED_FARGATE_CLUSTER
Esempio n. 18
0
def test_add_and_get_item() -> None:
    """Test adding a construct and retrieving an AWSObject from a stack."""
    stack = Stack("test-stack", "this is a test stack")
    stack.add(Bucket("my-bucket"))
    my_bucket = stack["my-bucket"]
    assert my_bucket
Esempio n. 19
0
def test_ecr_repository(stack: Stack) -> None:
    """Test ECR repository scheduled rule creation."""
    stack.add(Repository(name="test-repository"))
    assert stack.export()["Resources"] == EXPECTED_REPOSITORY
Esempio n. 20
0
class StackSet(Construct):
    """A CloudFormation service managed stack set."""
    def __init__(
        self,
        name: str,
        description: str,
        regions: list[str],
        ous: Optional[list[str]] = None,
        accounts: Optional[list[str]] = None,
        failure_tolerance_count: int = 0,
        max_concurrent_count: int = 1,
    ):
        """Initialize a CloudFormation service managed stack set.

        :param name: stack set name
        :param description: stack set description
        :param regions: list of regions where to deploy stack set stack instances
        :param ous: OrganizationalUnitIds for which to create stack instances
            in the specified Regions. Note that if both ous and accounts parameters
            are None then the stack set is deployed into the whole organisation
        :param accounts: list of accounts where to deploy stack set stack instances
        :param failure_tolerance_count: The number of accounts, per Region, for which
            the stackset deployment operation can fail before AWS CloudFormation stops
            the operation in that region
        :param max_conccurent_count: The maximum number of accounts in which to perform
            stackset deployment operations at one time. It is at most one more than the
            FailureToleranceCount.
        """
        self.name = name
        self.description = description
        self.stack = Stack(stack_name=f"{self.name}-stack",
                           cfn_role_arn="stackset")
        self.template_filename = f"{self.name}-template.yaml"
        self.regions = regions
        self.ous = ous
        self.accounts = accounts

        self.operation_preferences = cloudformation.OperationPreferences(
            FailureToleranceCount=failure_tolerance_count,
            MaxConcurrentCount=max_concurrent_count,
        )

    def add(self, element: AWSObject | Construct | Stack) -> None:
        """Add resource to the stackset stack.

        :param element: resource to add to the stackset stack
        """
        self.stack.add(element)

    def add_condition(self, condition_name: str,
                      condition: AWSHelperFn) -> None:
        """Add condition to stackset stack.

        :param condition_name: name of the condition to add
        :param condition: condition to add
        """
        self.stack.add_condition(condition_name, condition)

    def resources(self, stack: Stack) -> list[AWSObject]:
        """Return list of AWSObject associated with the construct."""
        stack_set_args = {
            "AutoDeployment":
            cloudformation.AutoDeployment(Enabled=True,
                                          RetainStacksOnAccountRemoval=False),
            "CallAs":
            "SELF",
            "Capabilities": ["CAPABILITY_NAMED_IAM"],
            "Description":
            self.description,
            "OperationPreferences":
            self.operation_preferences,
            "PermissionModel":
            "SERVICE_MANAGED",
            "StackSetName":
            self.name,
            "TemplateURL":
            (f"https://{stack.s3_bucket}.s3.amazonaws.com/{stack.s3_key}"
             f"{self.template_filename}"),
        }

        if self.ous is not None or self.accounts is not None:
            stack_instances_group = []

            if self.ous is not None:
                stack_instances_group.append(
                    cloudformation.StackInstances(
                        DeploymentTargets=cloudformation.DeploymentTargets(
                            OrganizationalUnitIds=self.ous),
                        Regions=self.regions,
                    ))

            if self.accounts is not None:

                stack_instances_group.append(
                    cloudformation.StackInstances(
                        DeploymentTargets=cloudformation.DeploymentTargets(
                            Accounts=self.accounts),
                        Regions=self.regions,
                    ))

            stack_set_args["StackInstancesGroup"] = stack_instances_group
        return [
            cloudformation.StackSet(name_to_id(self.name), **stack_set_args)
        ]

    def create_data_dir(self, root_dir: str) -> None:
        """Create data to be pushed to bucket used by cloudformation for resources."""
        template_path = os.path.join(root_dir, self.template_filename)
        with open(template_path, "w") as template_f:
            template_f.write(self.stack.body)
Esempio n. 21
0
def test_config_recorder(stack: Stack) -> None:
    """Test config recorder creation."""
    stack.add(ConfigurationRecorder(bucket_name="config-test-bucket"))
    assert stack.export()["Resources"] == EXPECTED_RECORDER
Esempio n. 22
0
def test_internet_gateway(stack: Stack, route_table_provided: bool) -> None:
    """Test InternetGateway construct."""
    vpc = ec2.VPC(
        name_to_id("vpc-test"),
        CidrBlock="10.0.0.0/16",
        EnableDnsHostnames="true",
        EnableDnsSupport="true",
    )

    subnets = [
        ec2.Subnet(
            name_to_id(f"{zone}-subnet"),
            CidrBlock="10.0.0.0/20",
            AvailabilityZone="eu-west-1b",
            VpcId=Ref(vpc),
            MapPublicIpOnLaunch="true",
        ) for zone, ip in zip(["eu-west-1a", "eu-west-1b"],
                              ["10.0.0.0/20", "10.0.16.0/20"])
    ]

    if route_table_provided:
        route_table = ec2.RouteTable(name_to_id("TestIgwRouteTable"),
                                     VpcId=Ref(vpc))
    else:
        route_table = None

    igw = InternetGateway(name_prefix="test",
                          vpc=vpc,
                          subnets=subnets,
                          route_table=route_table)

    for el in (vpc, *subnets, igw):
        stack.add(el)

    template = dict(EXPECTED_TEMPLATE)

    if not route_table_provided:
        template["TestIgwRouteTable"] = {
            "Properties": {
                "VpcId": {
                    "Ref": "VpcTest"
                }
            },
            "Type": "AWS::EC2::RouteTable",
        }
        template["Test0"] = {
            "Properties": {
                "RouteTableId": {
                    "Ref": "TestIgwRouteTable"
                },
                "SubnetId": {
                    "Ref": "EuWest1aSubnet"
                },
            },
            "Type": "AWS::EC2::SubnetRouteTableAssociation",
        }
        template["Test1"] = {
            "Properties": {
                "RouteTableId": {
                    "Ref": "TestIgwRouteTable"
                },
                "SubnetId": {
                    "Ref": "EuWest1bSubnet"
                },
            },
            "Type": "AWS::EC2::SubnetRouteTableAssociation",
        }

    assert stack.export()["Resources"] == template
Esempio n. 23
0
class StackSet(Construct):
    """A CloudFormation service managed stack set."""
    def __init__(
        self,
        name: str,
        description: str,
        regions: list[str],
        ous: Optional[list[str]] = None,
    ):
        """Initialize a CloudFormation service managed stack set.

        :param name: stack set name
        :param description: stack set description
        :param regions: list of regions where to deploy stack set stack instances
        :param ous: OrganizationalUnitIds for which to create stack instances
            in the specified Regions.
        """
        self.name = name
        self.description = description
        self.stack = Stack(stack_name=f"{self.name}-stack",
                           cfn_role_arn="stackset")
        self.template_filename = f"{self.name}-template.yaml"
        self.regions = regions
        self.organizational_units = ous

    def add(self, element: AWSObject | Construct | Stack) -> None:
        """Add resource to the stackset stack.

        :param element: resource to add to the stackset stack
        """
        self.stack.add(element)

    def add_condition(self, condition_name: str,
                      condition: AWSHelperFn) -> None:
        """Add condition to stackset stack.

        :param condition_name: name of the condition to add
        :param condition: condition to add
        """
        self.stack.add_condition(condition_name, condition)

    def resources(self, stack: Stack) -> list[AWSObject]:
        """Return list of AWSObject associated with the construct."""
        return [
            cloudformation.StackSet(
                name_to_id(self.name),
                AutoDeployment=cloudformation.AutoDeployment(
                    Enabled=True, RetainStacksOnAccountRemoval=False),
                CallAs="SELF",
                Capabilities=["CAPABILITY_NAMED_IAM"],
                Description=self.description,
                PermissionModel="SERVICE_MANAGED",
                StackSetName=self.name,
                StackInstancesGroup=[
                    cloudformation.StackInstances(
                        DeploymentTargets=cloudformation.DeploymentTargets(
                            OrganizationalUnitIds=self.organizational_units),
                        Regions=self.regions,
                    )
                ],
                TemplateURL=(
                    f"https://{stack.s3_bucket}.s3.amazonaws.com/{stack.s3_key}"
                    f"{self.template_filename}"),
            )
        ]

    def create_data_dir(self, root_dir: str) -> None:
        """Create data to be pushed to bucket used by cloudformation for resources."""
        template_path = os.path.join(root_dir, self.template_filename)
        with open(template_path, "w") as template_f:
            template_f.write(self.stack.body)