cloudformation-coverage-roadmap icon indicating copy to clipboard operation
cloudformation-coverage-roadmap copied to clipboard

[AWS::CodeBuild::Fleet] - [Coverage] - add reserved capacity fleets with VPC-support

Open rgoltz opened this issue 1 year ago • 1 comments

Name of the resource

AWS::CodeBuild::Fleet

Resource name

No response

Description

Normally CodeBuild provides only on-demand fleets, which are destroyed when the build finishes. Now, CodeBuild also offers reserved capacity fleets with VPC-support (EC2 that are maintained by CodeBuild) - These hosts remain available to receive subsequent build requests, which reduces build start-up latencies:

  • https://github.com/aws/aws-codebuild-docker-images/issues/526
  • https://aws.amazon.com/de/about-aws/whats-new/2024/05/aws-codebuild-connecting-vpc-reserved-capacity/

Following the "Working with reserved capacity"-docs, the API Reference should already Support Reserved Capacity for CreateFleet, UpdateFleet and BatchGetFleets. In order to use this capability, please add support within CloudFormation.

Other Details

No response

rgoltz avatar May 20 '24 20:05 rgoltz

FleetVpcConfig and OverflowBehavior has been implemented.

Please find test code below

class CodeBuildNest(NestedStack):

    def __init__(
        self,
        scope: Construct,
        construct_id: str,
        subnet_id: str,
        removal_policy: RemovalPolicy = RemovalPolicy.RETAIN,
        vpc: ec2.IVpc = None,
        **kwargs,
    ) -> None:
        super().__init__(scope, construct_id, **kwargs)

        # subnet_id = str(vpc.private_subnets[0].subnet_id)
        subnet_arn = f"arn:aws:ec2:{self.region}:{self.account}:subnet/{subnet_id}"
        # subnet = ec2.Subnet.from_subnet_id(self, "subnet", subnet_id)

        role = iam.Role(
            self,
            "CodeBuildRole",
            assumed_by=iam.ServicePrincipal("codebuild.amazonaws.com"),
        )

        CfnOutput(self, "CodeBuildRoleArn", value=role.role_arn)

        policy = iam.Policy(
            self,
            "codebuild-fleet-policy",
            statements=[
                iam.PolicyStatement(
                    actions=[
                        "ec2:CreateNetworkInterface",
                        "ec2:DescribeDhcpOptions",
                        "ec2:DescribeNetworkInterfaces",
                        "ec2:DeleteNetworkInterface",
                        "ec2:DescribeSubnets",
                        "ec2:DescribeSecurityGroups",
                        "ec2:DescribeVpcs",
                    ],
                    effect=iam.Effect.ALLOW,
                    resources=["*"],
                ),
                iam.PolicyStatement(
                    actions=[
                        "ec2:CreateNetworkInterfacePermission",
                        "ec2:ModifyNetworkInterfaceAttribute",
                    ],
                    effect=iam.Effect.ALLOW,
                    resources=[
                        f"arn:aws:ec2:{self.region}:{self.account}:network-interface/*"
                    ],
                    conditions={
                        # Doesn't work for some reason
                        # "StringEquals": {
                        #     "ec2:AuthorizedService": "codebuild.amazonaws.com"
                        # },
                        "ArnEquals": {"ec2:Subnet": [subnet_arn]},
                    },
                ),
            ],
        )
        policy.attach_to_role(role)

        fleet = codebuild.CfnFleet(
            self,
            "MyCfnFleet",
            base_capacity=3,
            compute_type="BUILD_GENERAL1_SMALL",
            environment_type="LINUX_CONTAINER",            
            tags=[CfnTag(key="Name", value="MyLinuxFleet")],
        )
        fleet.apply_removal_policy(removal_policy)

        # To avoid "Not authorized to perform DescribeSecurityGroups"
        # https://stackoverflow.com/a/60776576
        fleet.add_depends_on(policy.node.default_child)

        fleet.add_override("Properties.FleetVpcConfig.VpcId", vpc.vpc_id)
        fleet.add_override(
            "Properties.FleetVpcConfig.Subnets",
            [subnet_id],
        )

        sg = ec2.SecurityGroup(self, "BuildFleetSecurityGroup", vpc=vpc)
        fleet.add_override(
            "Properties.FleetVpcConfig.SecurityGroupIds", [sg.security_group_id]
        )

        fleet.add_override(
            "Properties.FleetServiceRole",
            role.role_arn,
        )

        fleet.add_override("Properties.OverflowBehavior", "QUEUE")

        CfnOutput(self, "FleetId", value=fleet.ref)
        CfnOutput(self, "FleetArn", value=fleet.attr_arn)
        CfnOutput(self, "FleetName", value=str(fleet.name))

{
 "Resources": {
  "CodeBuildRole728CBADE": {
   "Type": "AWS::IAM::Role",
   "Properties": {
    "AssumeRolePolicyDocument": {
     "Statement": [
      {
       "Action": "sts:AssumeRole",
       "Effect": "Allow",
       "Principal": {
        "Service": "codebuild.amazonaws.com"
       }
      }
     ],
     "Version": "2012-10-17"
    }
   }
  },
  "codebuildfleetpolicyC442E78B": {
   "Type": "AWS::IAM::Policy",
   "Properties": {
    "PolicyDocument": {
     "Statement": [
      {
       "Action": [
        "ec2:CreateNetworkInterface",
        "ec2:DeleteNetworkInterface",
        "ec2:DescribeDhcpOptions",
        "ec2:DescribeNetworkInterfaces",
        "ec2:DescribeSecurityGroups",
        "ec2:DescribeSubnets",
        "ec2:DescribeVpcs"
       ],
       "Effect": "Allow",
       "Resource": "*"
      },
      {
       "Action": [
        "ec2:CreateNetworkInterfacePermission",
        "ec2:ModifyNetworkInterfaceAttribute"
       ],
       "Condition": {
        "ArnEquals": {
         "ec2:Subnet": [
          "arn:aws:ec2:eu-west-1:000000000000:subnet/subnet-xxxxxxxxxxxxxxxxx"
         ]
        }
       },
       "Effect": "Allow",
       "Resource": "arn:aws:ec2:eu-west-1:000000000000:network-interface/*"
      }
     ],
     "Version": "2012-10-17"
    },
    "PolicyName": "codebuildfleetpolicyC442E78B",
    "Roles": [
     {
      "Ref": "CodeBuildRole728CBADE"
     }
    ]
   }
  },
  "MyCfnFleet": {
   "Type": "AWS::CodeBuild::Fleet",
   "Properties": {
    "BaseCapacity": 3,
    "ComputeType": "BUILD_GENERAL1_SMALL",
    "EnvironmentType": "LINUX_CONTAINER",
    "Tags": [
     {
      "Key": "Name",
      "Value": "MyLinuxFleet"
     }
    ],
    "FleetVpcConfig": {
     "VpcId": {
      "Fn::ImportValue": "CdkPythonNetworkingStack:VpcId"
     },
     "Subnets": [
      "subnet-xxxxxxxxxxxxxxxxx"
     ],
     "SecurityGroupIds": [
      {
       "Fn::GetAtt": [
        "BuildFleetSecurityGroup647CEC48",
        "GroupId"
       ]
      }
     ]
    },
    "FleetServiceRole": {
     "Fn::GetAtt": [
      "CodeBuildRole728CBADE",
      "Arn"
     ]
    },
    "OverflowBehavior": "QUEUE"
   },
   "DependsOn": [
    "codebuildfleetpolicyC442E78B"
   ],
   "UpdateReplacePolicy": "Delete",
   "DeletionPolicy": "Delete"
  },
  "BuildFleetSecurityGroup647CEC48": {
   "Type": "AWS::EC2::SecurityGroup",
   "Properties": {
    "GroupDescription": "CodeBuildStack/CodeBuildNest/BuildFleetSecurityGroup",
    "SecurityGroupEgress": [
     {
      "CidrIp": "0.0.0.0/0",
      "Description": "Allow all outbound traffic by default",
      "IpProtocol": "-1"
     }
    ],
    "VpcId": {
     "Fn::ImportValue": "CdkPythonNetworkingStack:VpcId"
    }
   }
  }
 },
 "Outputs": {
  "CodeBuildRoleArn": {
   "Value": {
    "Fn::GetAtt": [
     "CodeBuildRole728CBADE",
     "Arn"
    ]
   }
  },
  "FleetId": {
   "Value": {
    "Ref": "MyCfnFleet"
   }
  },
  "FleetArn": {
   "Value": {
    "Fn::GetAtt": [
     "MyCfnFleet",
     "Arn"
    ]
   }
  },
  "FleetName": {
   "Value": "None"
  }
 }
}

FarrOut avatar May 28 '24 07:05 FarrOut