[AWS::CodeBuild::Fleet] - [Coverage] - add reserved capacity fleets with VPC-support
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
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"
}
}
}