diff --git a/.buildkite/steps/launch.sh b/.buildkite/steps/launch.sh index 0ffb22880..bd5659562 100755 --- a/.buildkite/steps/launch.sh +++ b/.buildkite/steps/launch.sh @@ -103,10 +103,10 @@ echo "--- Building templates" make "mappings-for-${os}-${arch}-image" build/aws-stack.yml "IMAGE_ID=$image_id" echo "--- Validating templates" -make validate +make validate "BUCKET_PREFIX=$stack_name" echo "--- Creating stack ${stack_name}" -make create-stack "STACK_NAME=$stack_name" "SERVICE_ROLE=$service_role" +make create-stack "BUCKET_PREFIX=$stack_name" "STACK_NAME=$stack_name" "SERVICE_ROLE=$service_role" echo "+++ ⌛️ Waiting for update to complete" ./parfait watch-stack "${stack_name}" diff --git a/Makefile b/Makefile index a2249c48c..a4fe06989 100644 --- a/Makefile +++ b/Makefile @@ -143,12 +143,13 @@ ifdef SERVICE_ROLE role_arn="--role-arn=$(SERVICE_ROLE)" endif -create-stack: build/aws-stack.yml env-STACK_NAME +create-stack: build/aws-stack.yml env-STACK_NAME env-BUILDKITE_AWS_STACK_BUCKET env-BUCKET_PREFIX + aws s3 cp --content-type 'text/yaml' --acl public-read build/aws-stack.yml "s3://$(BUILDKITE_AWS_STACK_BUCKET)/$(BUCKET_PREFIX)/aws-stack.yml" aws cloudformation create-stack \ --output text \ --stack-name $(STACK_NAME) \ --disable-rollback \ - --template-body "file://$(PWD)/build/aws-stack.yml" \ + --template-url "https://$(BUILDKITE_AWS_STACK_BUCKET).s3.amazonaws.com/$(BUCKET_PREFIX)/aws-stack.yml" \ --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM CAPABILITY_AUTO_EXPAND \ --parameters "$$(cat config.json)" \ "$(role_arn)" @@ -175,10 +176,11 @@ bump-agent-version: git add README.md packer/linux/scripts/install-buildkite-agent.sh packer/windows/scripts/install-buildkite-agent.ps1 git commit -m "Bump buildkite-agent to v$(AGENT_VERSION)" -validate: build/aws-stack.yml +validate: build/aws-stack.yml env-BUILDKITE_AWS_STACK_BUCKET env-BUCKET_PREFIX + aws s3 cp --content-type 'text/yaml' --acl public-read build/aws-stack.yml "s3://$(BUILDKITE_AWS_STACK_BUCKET)/$(BUCKET_PREFIX)/aws-stack.yml" aws cloudformation validate-template \ --output text \ - --template-body "file://$(PWD)/build/aws-stack.yml" + --template-url "https://$(BUILDKITE_AWS_STACK_BUCKET).s3.amazonaws.com/$(BUCKET_PREFIX)/aws-stack.yml" generate-toc: docker run -it --rm -v "$(PWD):/app" node:slim bash \ diff --git a/packer/linux/conf/bin/bk-install-elastic-stack.sh b/packer/linux/conf/bin/bk-install-elastic-stack.sh index a95a1c391..9a4aa5fc9 100755 --- a/packer/linux/conf/bin/bk-install-elastic-stack.sh +++ b/packer/linux/conf/bin/bk-install-elastic-stack.sh @@ -215,6 +215,12 @@ fi systemctl enable "buildkite-agent" +# If warm pool is enabled, don't start the agent until the ASG BootHook +if [ "${BUILDKITE_WARM_POOL:-false}" == "false" ] +then + systemctl start "buildkite-agent" +fi + # let the stack know that this host has been initialized successfully /opt/aws/bin/cfn-signal \ --region "$AWS_REGION" \ diff --git a/packer/windows/conf/bin/bk-install-elastic-stack.ps1 b/packer/windows/conf/bin/bk-install-elastic-stack.ps1 index 140a0523f..f3b4531dc 100755 --- a/packer/windows/conf/bin/bk-install-elastic-stack.ps1 +++ b/packer/windows/conf/bin/bk-install-elastic-stack.ps1 @@ -210,6 +210,11 @@ If ($lastexitcode -ne 0) { Exit $lastexitcode } nssm set buildkite-agent AppRestartDelay 5000 If ($lastexitcode -ne 0) { Exit $lastexitcode } +# If warm pool is enabled, don't start the agent until the ASG BootHook +If ($null -eq $Env:BUILDKITE_WARM_POOL -or $Env:BUILDKITE_WARM_POOL -eq "false") { + Restart-Service buildkite-agent +} + # renable debug tracing Set-PSDebug -Trace 2 diff --git a/templates/aws-stack.yml b/templates/aws-stack.yml index 5b1943439..194c9e981 100644 --- a/templates/aws-stack.yml +++ b/templates/aws-stack.yml @@ -231,12 +231,23 @@ Parameters: InstanceType: Description: Instance type. Comma-separated list with 1-4 instance types. The order is a prioritized preference for launching OnDemand instances, and a non-prioritized list of types to consider for Spot Instances (where used). - Type: String + Type: CommaDelimitedList Default: t3.large - MinLength: 1 - AllowedPattern: "^[\\w\\.]+(,[\\w\\.]*){0,3}$" + # AllowedPattern and MinLength not allowed on a CommaDelimitedList :( + # MinLength: 1 + # AllowedPattern: "^[\\w\\.]+(,[\\w\\.]*){0,3}$" ConstraintDescription: "must contain 1-4 instance types separated by commas. No space before/after the comma." + InstanceTypes: + Description: How many instance types are specified in InstanceType + Type: String + Default: "1" + AllowedValues: + - "1" + - "2" + - "3" + - "4" + SpotPrice: Description: Maximum spot price to use for the instances, in instance cost per hour. Values >0 will result in 100% of instances being spot. 0 means only use normal (non-spot) instances. This parameter is deprecated - we recommend setting to 0 and using OnDemandPercentage to opt into spot instances. Type: String @@ -443,6 +454,11 @@ Parameters: Default: 0 MinValue: 0 + WarmPoolMinSize: + Type: Number + Description: Minimum number of instances in warm pool + Default: 0 + Rules: HasToken: Assertions: @@ -457,6 +473,19 @@ Rules: - !Ref BuildkiteAgentTokenParameterStorePath - "" AssertDescription: "You must provide BuildkiteAgentToken or BuildkiteAgentTokenParameterStorePath" + # WarmPoolInstanceTypeCompatible: + # RuleCondition: + # # If warm pool is enabled + # !Not [ !Equals [ !Ref WarmPoolMinSize, 0 ] ] + # Assertions: + # # There must only be one instance type + # - Assert: + # !Equals [ !Ref InstanceTypes, "1" ] + # # Spot must not be used + # - Assert: + # !And + # - !Equals [ !Ref SpotPrice, 0 ] + # - !Equals [ !Ref OnDemandPercentage, 100 ] Outputs: VpcId: @@ -531,14 +560,23 @@ Conditions: UseDefaultRootVolumeName: !Equals [ !Ref RootVolumeName, "" ] - UseInstanceType2: - !Not [ !Equals [ !Select [ "1", !Split [ ",", !Join [ ",", [ !Ref InstanceType, "", "", "" ] ] ] ], ""] ] + # If using spot, or multiple instances we need to use a MixedInstancesPolicy + UseMixedInstancesPolicy: + !Or + - !Not [ !Equals [ !Ref SpotPrice, 0 ] ] + - !Not [ !Equals [ !Ref OnDemandPercentage, 100 ] ] + - !Condition UseInstanceType2 + - !Condition UseInstanceType3 + - !Condition UseInstanceType4 + UseSingleInstanceLaunchTemplate: + !Not [ !Condition UseMixedInstancesPolicy ] + UseInstanceType2: + !Equals [ !Ref InstanceTypes, "2" ] UseInstanceType3: - !Not [ !Equals [ !Select [ "2", !Split [ ",", !Join [ ",", [ !Ref InstanceType, "", "", "" ] ] ] ], ""] ] - + !Equals [ !Ref InstanceTypes, "3" ] UseInstanceType4: - !Not [ !Equals [ !Select [ "3", !Split [ ",", !Join [ ",", [ !Ref InstanceType, "", "", "" ] ] ] ], ""] ] + !Equals [ !Ref InstanceTypes, "4" ] UseManagedPolicyARN: !Not [ !Equals [ !Join [ "", !Ref ManagedPolicyARN ], "" ] ] @@ -582,19 +620,22 @@ Conditions: UsingArmInstances: !Or - - !Equals [ !Select [ 0, !Split [ ".", !Ref InstanceType ] ], "m6g" ] - - !Equals [ !Select [ 0, !Split [ ".", !Ref InstanceType ] ], "m6gd" ] - - !Equals [ !Select [ 0, !Split [ ".", !Ref InstanceType ] ], "t4g" ] - - !Equals [ !Select [ 0, !Split [ ".", !Ref InstanceType ] ], "a1" ] - - !Equals [ !Select [ 0, !Split [ ".", !Ref InstanceType ] ], "c6g" ] - - !Equals [ !Select [ 0, !Split [ ".", !Ref InstanceType ] ], "c6gd" ] - - !Equals [ !Select [ 0, !Split [ ".", !Ref InstanceType ] ], "c6gn" ] - - !Equals [ !Select [ 0, !Split [ ".", !Ref InstanceType ] ], "r6g" ] - - !Equals [ !Select [ 0, !Split [ ".", !Ref InstanceType ] ], "r6gd" ] + - !Equals [ !Select [ 0, !Split [ ".", !Select [ 0, !Ref InstanceType ] ] ], "m6g" ] + - !Equals [ !Select [ 0, !Split [ ".", !Select [ 0, !Ref InstanceType ] ] ], "m6gd" ] + - !Equals [ !Select [ 0, !Split [ ".", !Select [ 0, !Ref InstanceType ] ] ], "t4g" ] + - !Equals [ !Select [ 0, !Split [ ".", !Select [ 0, !Ref InstanceType ] ] ], "a1" ] + - !Equals [ !Select [ 0, !Split [ ".", !Select [ 0, !Ref InstanceType ] ] ], "c6g" ] + - !Equals [ !Select [ 0, !Split [ ".", !Select [ 0, !Ref InstanceType ] ] ], "c6gd" ] + - !Equals [ !Select [ 0, !Split [ ".", !Select [ 0, !Ref InstanceType ] ] ], "c6gn" ] + - !Equals [ !Select [ 0, !Split [ ".", !Select [ 0, !Ref InstanceType ] ] ], "r6g" ] + - !Equals [ !Select [ 0, !Split [ ".", !Select [ 0, !Ref InstanceType ] ] ], "r6gd" ] UseMaxInstanceLifetime: !Not [ !Equals [ !Ref MaxInstanceLifetime, 0 ] ] + UseWarmPool: + !Not [ !Equals [ !Ref WarmPoolMinSize, 0 ] ] + Mappings: ECRManagedPolicy: none : { Policy: '' } @@ -904,7 +945,7 @@ Resources: KeyName: !If [ "HasKeyName", !Ref KeyName, !Ref 'AWS::NoValue' ] IamInstanceProfile: Arn: !GetAtt "IAMInstanceProfile.Arn" - InstanceType: !Select [ "0", !Split [ ",", !Join [ ",", [ !Ref InstanceType, "", "", "" ] ] ] ] + InstanceType: !Select [ 0, !Ref InstanceType ] MetadataOptions: HttpTokens: !Ref IMDSv2Tokens # Allow containers using a Docker network on the host to receive IDMSv2 responses @@ -983,11 +1024,13 @@ Resources: $Env:ECR_PLUGIN_ENABLED="${EnableECRPlugin}" $Env:DOCKER_LOGIN_PLUGIN_ENABLED="${EnableDockerLoginPlugin}" $Env:AWS_REGION="${AWS::Region}" + $Env:BUILDKITE_WARM_POOL="${WarmPool}" powershell -file C:\buildkite-agent\bin\bk-install-elastic-stack.ps1 >> C:\buildkite-agent\elastic-stack.log - { LocalSecretsBucket: !If [ CreateSecretsBucket, !Ref ManagedSecretsBucket, !Ref SecretsBucket ], AgentTokenPath: !If [ UseCustomerManagedParameterPath, !Ref BuildkiteAgentTokenParameterStorePath, !Ref BuildkiteAgentTokenParameter ], + WarmPool: !If [ UseWarmPool, "true", "false" ], } - !Sub - | @@ -1030,11 +1073,13 @@ Resources: DOCKER_LOGIN_PLUGIN_ENABLED="${EnableDockerLoginPlugin}" \ DOCKER_EXPERIMENTAL="${EnableDockerExperimental}" \ AWS_REGION="${AWS::Region}" \ + BUILDKITE_WARM_POOL="${WarmPool}" \ /usr/local/bin/bk-install-elastic-stack.sh --==BOUNDARY==-- - { LocalSecretsBucket: !If [ CreateSecretsBucket, !Ref ManagedSecretsBucket, !Ref SecretsBucket ], AgentTokenPath: !If [ UseCustomerManagedParameterPath, !Ref BuildkiteAgentTokenParameterStorePath, !Ref BuildkiteAgentTokenParameter ], + WarmPool: !If [ UseWarmPool, "true", "false" ], } AgentAutoScaleGroup: @@ -1044,29 +1089,38 @@ Resources: - VpcComplete Properties: VPCZoneIdentifier: !If [ "CreateVpcResources", [ !Ref Subnet0, !Ref Subnet1 ], !Ref Subnets ] + LaunchTemplate: + !If + - UseSingleInstanceLaunchTemplate + - LaunchTemplateId: !Ref AgentLaunchTemplate + Version: !GetAtt AgentLaunchTemplate.LatestVersionNumber + - !Ref AWS::NoValue MixedInstancesPolicy: - InstancesDistribution: - OnDemandPercentageAboveBaseCapacity: !If [ SpotPriceSet, 0, !Ref OnDemandPercentage ] - SpotAllocationStrategy: capacity-optimized - SpotMaxPrice: !If [SpotPriceSet, !Ref SpotPrice, !Ref "AWS::NoValue"] - LaunchTemplate: - LaunchTemplateSpecification: - LaunchTemplateId: !Ref AgentLaunchTemplate - Version: !GetAtt "AgentLaunchTemplate.LatestVersionNumber" - Overrides: - - InstanceType: !Select [ "0", !Split [ ",", !Join [ ",", [ !Ref InstanceType, "", "", "" ] ] ] ] - - !If - - UseInstanceType2 - - InstanceType: !Select [ "1", !Split [ ",", !Join [ ",", [ !Ref InstanceType, "", "", "" ] ] ] ] - - !Ref "AWS::NoValue" - - !If - - UseInstanceType3 - - InstanceType: !Select [ "2", !Split [ ",", !Join [ ",", [ !Ref InstanceType, "", "", "" ] ] ] ] - - !Ref "AWS::NoValue" - - !If - - UseInstanceType4 - - InstanceType: !Select [ "3", !Split [ ",", !Join [ ",", [ !Ref InstanceType, "", "", "" ] ] ] ] - - !Ref "AWS::NoValue" + !If + - UseMixedInstancesPolicy + - InstancesDistribution: + OnDemandPercentageAboveBaseCapacity: !If [ SpotPriceSet, 0, !Ref OnDemandPercentage ] + SpotAllocationStrategy: capacity-optimized + SpotMaxPrice: !If [SpotPriceSet, !Ref SpotPrice, !Ref "AWS::NoValue"] + LaunchTemplate: + LaunchTemplateSpecification: + LaunchTemplateId: !Ref AgentLaunchTemplate + Version: !GetAtt "AgentLaunchTemplate.LatestVersionNumber" + Overrides: + - InstanceType: !Select [ 0, !Ref InstanceType ] + - !If + - UseInstanceType2 + - InstanceType: !Select [ 1, !Ref InstanceType ] + - !Ref "AWS::NoValue" + - !If + - UseInstanceType3 + - InstanceType: !Select [ 2, !Ref InstanceType ] + - !Ref "AWS::NoValue" + - !If + - UseInstanceType4 + - InstanceType: !Select [ 3, !Ref InstanceType ] + - !Ref "AWS::NoValue" + - !Ref AWS::NoValue MinSize: !Ref MinSize MaxSize: !Ref MaxSize Cooldown: 60 @@ -1147,7 +1201,14 @@ Resources: - Effect: Allow Action: ssm:StartAutomationExecution Resource: - - !Sub arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:automation-definition/${BootHookAutomation}:$DEFAULT + - !If + - UseWarmPool + - !Sub arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:automation-definition/${BootHookWarmedAutomation}:$DEFAULT + - !Ref "AWS::NoValue" + - !If + - UseWarmPool + - !Sub arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:automation-definition/${BootHookInServiceAutomation}:$DEFAULT + - !Ref "AWS::NoValue" - !Sub arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:automation-definition/${ShutdownHookAutomation}:$DEFAULT - !Sub arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:automation-definition/${SpotInteruptionAutomation}:$DEFAULT - Effect: Allow @@ -1189,11 +1250,14 @@ Resources: Version: '2012-10-17' Statement: - Effect: Allow - Action: autoscaling:CompleteLifecycleAction + Action: + - autoscaling:CompleteLifecycleAction + - autoscaling:TerminateInstanceInAutoScalingGroup Resource: !Sub arn:${AWS::Partition}:autoscaling:${AWS::Region}:${AWS::AccountId}:autoScalingGroup:*:autoScalingGroupName/${AWS::StackName}-AgentAutoScaleGroup-* BootHook: Type: AWS::AutoScaling::LifecycleHook + Condition: UseWarmPool Properties: AutoScalingGroupName: !Ref AgentAutoScaleGroup LifecycleHookName: BootHook @@ -1202,10 +1266,11 @@ Resources: # them HeartbeatTimeout: !If [ UseLinuxAgents, 300, 600 ] - BootHookRule: + BootHookWarmedRule: Type: AWS::Events::Rule + Condition: UseWarmPool Properties: - Description: !Sub Run the boot time AWS SSM Automation for ${BootHook} + Description: !Sub Run the ${AgentAutoScaleGroup} ${BootHook} warmed AWS SSM Automation ${BootHookWarmedAutomation} EventPattern: source: - aws.autoscaling @@ -1214,17 +1279,92 @@ Resources: detail: AutoScalingGroupName: [ !Ref AgentAutoScaleGroup ] LifecycleHookName: [ !Ref BootHook ] + # Launching directly into the WarmPool + Destination: [ "WarmPool" ] Targets: - - Arn: !Sub arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:automation-definition/${BootHookAutomation}:$DEFAULT + - Arn: !Sub arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:automation-definition/${BootHookWarmedAutomation}:$DEFAULT RoleArn: !GetAtt EventBridgeRuleRole.Arn Id: TargetSsmAutomation InputTransformer: InputPathsMap: + token: "$.detail.LifecycleActionToken" instanceid: "$.detail.EC2InstanceId" - InputTemplate: "{\"InstanceId\":[]}" + InputTemplate: "{\"InstanceId\":[],\"LifecycleActionToken\":[]}" + + BootHookWarmedAutomation: + Type: AWS::SSM::Document + Condition: UseWarmPool + Properties: + DocumentType: Automation + Content: + schemaVersion: "0.3" + assumeRole: !GetAtt AutomationRole.Arn + description: Complete the Warmed:Pending:Wait lifecycle action + parameters: + InstanceId: + type: String + LifecycleActionToken: + type: String + AutoScalingGroupName: + type: String + default: !Ref AgentAutoScaleGroup + LifecycleHook: + type: String + default: !Ref BootHook + mainSteps: + - !If + - UseLinuxAgents + - name: RunCommand + action: aws:runCommand + inputs: + DocumentName: AWS-RunShellScript + InstanceIds: + - "{{ InstanceId }}" + Parameters: + executionTimeout: "300" + commands: + # Wait for cloud-init to complete + - cloud-init status --wait + # TODO add a wait barrier for Windows instances to complete cloud-init + - !Ref AWS::NoValue + - name: CompleteLifecycleAction + action: aws:executeAwsApi + inputs: + Service: autoscaling + Api: CompleteLifecycleAction + AutoScalingGroupName: "{{ AutoScalingGroupName }}" + LifecycleActionToken: "{{ LifecycleActionToken }}" + LifecycleActionResult: "CONTINUE" + LifecycleHookName: "{{ LifecycleHook }}" + + BootHookInServiceRule: + Type: AWS::Events::Rule + Condition: UseWarmPool + Properties: + Description: !Sub Run the ${AgentAutoScaleGroup} ${BootHook} in service AWS SSM Automation ${BootHookInServiceAutomation} + EventPattern: + source: + - aws.autoscaling + detail-type: + - "EC2 Instance-launch Lifecycle Action" + detail: + AutoScalingGroupName: [ !Ref AgentAutoScaleGroup ] + LifecycleHookName: [ !Ref BootHook ] + # Launching directly into the ASG, or moving from the WarmPool into the ASG + Destination: [ "AutoScalingGroup" ] + Targets: + - Arn: !Sub arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:automation-definition/${BootHookInServiceAutomation}:$DEFAULT + RoleArn: !GetAtt EventBridgeRuleRole.Arn + Id: TargetSsmAutomation + InputTransformer: + InputPathsMap: + token: "$.detail.LifecycleActionToken" + instanceid: "$.detail.EC2InstanceId" + InputTemplate: "{\"InstanceId\":[],\"LifecycleActionToken\":[]}" - BootHookAutomation: + BootHookInServiceAutomation: Type: AWS::SSM::Document + Condition: UseWarmPool Properties: DocumentType: Automation Content: @@ -1232,6 +1372,8 @@ Resources: assumeRole: !GetAtt AutomationRole.Arn description: Start the buildkite-agent and complete the launch lifecycle action parameters: + LifecycleActionToken: + type: String InstanceId: type: String AutoScalingGroupName: @@ -1252,6 +1394,9 @@ Resources: Parameters: executionTimeout: "300" commands: + # Wait for cloud-init to complete + - cloud-init status --wait + # Start the buildkite-agent - systemctl start buildkite-agent - name: RunCommand action: aws:runCommand @@ -1269,7 +1414,7 @@ Resources: Service: autoscaling Api: CompleteLifecycleAction AutoScalingGroupName: "{{ AutoScalingGroupName }}" - InstanceId: "{{ InstanceId }}" + LifecycleActionToken: "{{ LifecycleActionToken }}" LifecycleActionResult: "CONTINUE" LifecycleHookName: "{{ LifecycleHook }}" @@ -1286,7 +1431,7 @@ Resources: ShutdownHookRule: Type: AWS::Events::Rule Properties: - Description: !Sub Run the shutdown time AWS SSM Automation for ${ShutdownHook} + Description: !Sub Run the ${AgentAutoScaleGroup} ${ShutdownHook} AWS SSM Automation ${ShutdownHookAutomation} EventPattern: source: - aws.autoscaling @@ -1301,8 +1446,9 @@ Resources: Id: TargetSsmAutomation InputTransformer: InputPathsMap: + token: "$.detail.LifecycleActionToken" instanceid: "$.detail.EC2InstanceId" - InputTemplate: "{\"InstanceId\":[]}" + InputTemplate: "{\"InstanceId\":[],\"LifecycleActionToken\":[]}" ShutdownHookAutomation: Type: AWS::SSM::Document @@ -1313,6 +1459,8 @@ Resources: assumeRole: !GetAtt AutomationRole.Arn description: Stop the buildkite-agent, wait for it to exit, complete the launch lifecycle action parameters: + LifecycleActionToken: + type: String InstanceId: type: String AutoScalingGroupName: @@ -1350,7 +1498,7 @@ Resources: Service: autoscaling Api: CompleteLifecycleAction AutoScalingGroupName: "{{ AutoScalingGroupName }}" - InstanceId: "{{ InstanceId }}" + LifecycleActionToken: "{{ LifecycleActionToken }}" LifecycleActionResult: "CONTINUE" LifecycleHookName: "{{ LifecycleHook }}" @@ -1395,3 +1543,14 @@ Resources: Api: TerminateInstanceInAutoScalingGroup InstanceId: "{{ InstanceId }}" ShouldDecrementDesiredCapacity: false + + WarmPool: + Type: AWS::AutoScaling::WarmPool + Condition: UseWarmPool + DependsOn: + - BootHookWarmedRule + - BootHookInServiceRule + Properties: + AutoScalingGroupName: !Ref AgentAutoScaleGroup + MinSize: !Ref WarmPoolMinSize + PoolState: Stopped