Adding EKS managed windows node group failed. How to debug? - amazon-web-services

At AWS Console,
I created an AWS EKS Node IAM role with following IAM policies:
AmazonEKSWorkerNodePolicy
AmazonEKS_CNI_Policy
AmazonEC2ContainerRegistryReadOnly
I created launch template with the AMI, ami-0e6430de0e2d50a33
(Windows_Server-English-Full-EKS-Optimized-1.16-2020.09.09)
I have an existing eks cluster created by terraform (0.11.13). It has one eks node group. I would like to add a new windows eks node group manually. At AWS console, I went to my eks cluster, clicked on "Add Node Group", use the template above, and clicked on the "Create button". But, I got "Create failed". I have no clue cause of the failure. Where can I find the logs at AWS console?

Not sure where to find those type of logs.
However, here is an AWS CloudFormation template we use to create a self-managed Windows Server 2019 node group that joins the given cluster. Note that it uses spot instances and the worker nodes also join an existing AD.
You will need to either export your EKS cluster name from another CF template or hard-code the value in the UserData property (or pass in your EKS cluster name).
Remove the line 'New-SSMAssociation' line if not joining the AD.
AWSTemplateFormatVersion: 2010-09-09
Description: Creates EC2 instances to support the EKS cluster worker nodes.
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
-
Label:
default: "EKS Worker Nodes Configuration"
Parameters:
- Environment
- NodeImageIdSSMParam
- SpotPrice
- Subnets
- ActiveDirectoryIdentifier
- ActiveDirectoryName
- DesiredCapacity
- MaxCapacity
- MinCapacity
Parameters:
Environment:
Type: String
Description: The associated environment of the EKS cluster.
AllowedValues:
- preprod
- prod
BootstrapArguments:
Type: String
Default: ""
Description: Arguments to pass to the bootstrap script.
NodeImageIdSSMParam:
Type: "AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>"
Default: /aws/service/ami-windows-latest/Windows_Server-2019-English-Core-EKS_Optimized-1.17/image_id
Description: AWS Systems Manager Parameter Store parameter of the AMI ID for the worker node instances.
SpotPrice:
Type: String
Description: The spot price to bid for the EKS Optimized instances.
Default: 0.4000
Subnets:
Description: Select the PRIVATE subnets where workers can be created.
Type: List<AWS::EC2::Subnet::Id>
ActiveDirectoryIdentifier:
Type: String
Description: The identifier of the shared Microsoft Managed AD
ActiveDirectoryName:
Type: String
Description: The name of the shared Microsoft Managed AD
DesiredCapacity:
Type: Number
Description: The desired number of EC2 instances for the Autoscaling group.
Default: 6
MaxCapacity:
Type: Number
Description: The maximum number of EC2 instances for the Autoscaling group.
Default: 6
MinCapacity:
Type: Number
Description: The minimum number of EC2 instances for the Autoscaling group.
Default: 6
Resources:
LaunchConfiguration:
Type: AWS::AutoScaling::LaunchConfiguration
Properties:
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
DeleteOnTermination: true
VolumeSize: 50
VolumeType: gp2
LaunchConfigurationName: !Sub eks-worker-nodes-windows-${Environment}-launch-config
SpotPrice: !Ref SpotPrice
AssociatePublicIpAddress: false
ImageId: !Ref NodeImageIdSSMParam
InstanceType: t3.large
IamInstanceProfile: !ImportValue eks-worker-instance-profile-arn
InstanceMonitoring: true
KeyName: samtec-ec2-key
SecurityGroups:
- Fn::ImportValue: !Sub eks-${Environment}-sg
UserData:
Fn::Base64: !Sub
- |
<powershell>
Set-DefaultAWSRegion -Region ${AWS::Region}
Set-Variable -name instance_id -value (Invoke-Restmethod -uri http://169.254.169.254/latest/meta-data/instance-id)
New-SSMAssociation -InstanceId $instance_id -Name "awsconfig_Domain_${ActiveDirectoryIdentifier}_${ActiveDirectoryName}"
[string]$EKSBinDir = "$env:ProgramFiles\Amazon\EKS"
[string]$EKSBootstrapScriptName = 'Start-EKSBootstrap.ps1'
[string]$EKSBootstrapScriptFile = "$EKSBinDir\$EKSBootstrapScriptName"
[string]$cfn_signal = "$env:ProgramFiles\Amazon\cfn-bootstrap\cfn-signal.exe"
& $EKSBootstrapScriptFile -EKSClusterName ${ClusterName} ${BootstrapArguments} 3>&1 4>&1 5>&1 6>&1
$LastError = if ($?) { 0 } else { $Error[0].Exception.HResult }
& $cfn_signal --exit-code=$LastError `
--stack="${AWS::StackName}" `
--resource="NodeGroup" `
--region=${AWS::Region}
</powershell>
- ClusterName:
'Fn::ImportValue': !Sub 'eks-${Environment}-name'
AutoScalingGroup:
Type: AWS::AutoScaling::AutoScalingGroup
Properties:
AutoScalingGroupName: !Sub eks-worker-nodes-windows-${Environment}-autoscaler
Cooldown: 30
DesiredCapacity: !Ref DesiredCapacity
HealthCheckGracePeriod: 300
HealthCheckType: EC2
LaunchConfigurationName: !Ref LaunchConfiguration
MaxSize: !Ref MaxCapacity
MinSize: !Ref MinCapacity
MetricsCollection:
- Granularity: 1Minute
Tags:
- Key: Name
Value: !Sub eks-windows-${Environment}-worker
PropagateAtLaunch: true
- Key: operating-system
Value: windows
PropagateAtLaunch: true
- Key: !Sub
- |
kubernetes.io/cluster/${ClusterName}
- ClusterName:
'Fn::ImportValue': !Sub 'eks-${Environment}-name'
Value: owned
PropagateAtLaunch: true
- Key: !Sub
- |
k8s.io/cluster-autoscaler/${ClusterName}
- ClusterName:
'Fn::ImportValue': !Sub 'eks-${Environment}-name'
Value: owned
PropagateAtLaunch: true
- Key: k8s.io/cluster-autoscaler/enabled
Value: true
PropagateAtLaunch: true
- Key: eks:cluster-name
Value:
'Fn::ImportValue': !Sub 'eks-${Environment}-name'
PropagateAtLaunch: true
- Key: eks:nodegroup-name
Value:
'Fn::ImportValue': !Sub 'eks-${Environment}-name'
PropagateAtLaunch: true
VPCZoneIdentifier: !Ref Subnets

Related

how to update dynamically kubeconfig during cluster creation on aws?

I use a template cloud formation to create my cluster on aws :
AWSTemplateFormatVersion: "2010-09-09"
Description: Deploys an EKS cluster in a new VPC (qs-1p7nknoht)
Metadata:
LintSpellExclude:
- Kubernetes
- ARNs
- Resource Names
- autoscaler
- IOPS
- EfsStorageClass
- Lambda
- maxIO
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: VPC network configuration
Parameters:
- NumberOfAZs
- AvailabilityZones
- VPCCIDR
- PrivateSubnet1CIDR
- PrivateSubnet2CIDR
- PrivateSubnet3CIDR
- PublicSubnet1CIDR
- PublicSubnet2CIDR
- PublicSubnet3CIDR
- RemoteAccessCIDR
- ProvisionBastionHost
- Label:
default: Amazon EC2 configuration
Parameters:
- KeyPairName
- Label:
default: Amazon EKS configuration
Parameters:
- NodeInstanceType
- NumberOfNodes
- MaxNumberOfNodes
- NodeGroupName
- NodeVolumeSize
- ManagedNodeGroup
- ManagedNodeGroupAMIType
- AdditionalEKSAdminArns
- KubernetesVersion
- Label:
default: Optional Kubernetes add-ins
Parameters:
- ClusterAutoScaler
- EfsStorageClass
- EfsPerformanceMode
- EfsThroughputMode
- EfsProvisionedThroughputInMibps
- MonitoringStack
- Label:
default: AWS Quick Start configuration
Parameters:
- QSS3BucketName
- QSS3KeyPrefix
- QSS3BucketRegion
- LambdaZipsBucketName
ParameterLabels:
AvailabilityZones:
default: Availability Zones
KeyPairName:
default: SSH key name
PrivateSubnet1CIDR:
default: Private subnet 1 CIDR
PrivateSubnet2CIDR:
default: Private subnet 2 CIDR
PrivateSubnet3CIDR:
default: Private subnet 3 CIDR
PublicSubnet1CIDR:
default: Public subnet 1 CIDR
PublicSubnet2CIDR:
default: Public subnet 2 CIDR
PublicSubnet3CIDR:
default: Public subnet 3 CIDR
QSS3BucketName:
default: Quick Start S3 bucket name
QSS3KeyPrefix:
default: Quick Start S3 key prefix
QSS3BucketRegion:
default: Quick Start S3 bucket region
RemoteAccessCIDR:
default: Allowed external access CIDR
VPCCIDR:
default: VPC CIDR
NodeInstanceType:
default: Nodes instance type
NumberOfNodes:
default: Number of nodes
MaxNumberOfNodes:
default: Maximum number of nodes
NodeGroupName:
default: Node group name
NodeVolumeSize:
default: Node volume size
ManagedNodeGroup:
default: Managed node group
ManagedNodeGroupAMIType:
default: Managed node group AMI type
AdditionalEKSAdminArns:
default: Additional EKS admin ARNs
KubernetesVersion:
default: Kubernetes version
LambdaZipsBucketName:
default: Lambda zips bucket name
ClusterAutoScaler:
default: Cluster autoscaler
EfsStorageClass:
default: EFS storage class
EfsPerformanceMode:
default: EFS performance mode
EfsThroughputMode:
default: EFS throughput mode
EfsProvisionedThroughputInMibps:
default: EFS provisioned throughput in Mibps
MonitoringStack:
default: Monitoring Stack
NumberOfAZs:
default: Number of Availability Zones
ProvisionBastionHost:
default: Provision Bastion Host
Parameters:
AvailabilityZones:
Description: The list of Availability Zones to use for the subnets in the VPC. Three
Availability Zones are used for this deployment, and the logical order of your
selections is preserved.
Type: List<AWS::EC2::AvailabilityZone::Name>
KeyPairName:
Description: The name of an existing public/private key pair, which allows you
to securely connect to your instance after it launches
Type: AWS::EC2::KeyPair::KeyName
PrivateSubnet1CIDR:
AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$
ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
Default: 10.0.0.0/19
Description: The CIDR block for private subnet 1 located in Availability Zone 1
Type: String
PrivateSubnet2CIDR:
AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$
ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
Default: 10.0.32.0/19
Description: The CIDR block for private subnet 2 located in Availability Zone 2
Type: String
PrivateSubnet3CIDR:
AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$
ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
Default: 10.0.64.0/19
Description: The CIDR block for private subnet 3 located in Availability Zone 3
Type: String
PublicSubnet1CIDR:
AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$
ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
Default: 10.0.128.0/20
Description: CIDR block for the public (DMZ) subnet 1 located in Availability
Zone 1
Type: String
PublicSubnet2CIDR:
AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$
ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
Default: 10.0.144.0/20
Description: The CIDR block for the public (DMZ) subnet 2 located in Availability
Zone 2
Type: String
PublicSubnet3CIDR:
AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$
ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
Default: 10.0.160.0/20
Description: The CIDR block for the public (DMZ) subnet 3 located in Availability
Zone 3
Type: String
QSS3BucketName:
AllowedPattern: ^[0-9a-zA-Z]+([0-9a-zA-Z-]*[0-9a-zA-Z])*$
ConstraintDescription: Quick Start bucket name can include numbers, lowercase
letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen
(-).
Default: aws-quickstart
Description: S3 bucket name for the Quick Start assets. This string can include
numbers, lowercase letters, uppercase letters, and hyphens (-). It cannot start
or end with a hyphen (-).
Type: String
QSS3KeyPrefix:
AllowedPattern: ^[0-9a-zA-Z-/.]*$
ConstraintDescription: Quick Start key prefix can include numbers, lowercase letters,
uppercase letters, hyphens (-), dots(.) and forward slash (/).
Default: quickstart-amazon-eks/
Description: S3 key prefix for the Quick Start assets. Quick Start key prefix
can include numbers, lowercase letters, uppercase letters, hyphens (-), dots(.) and
forward slash (/).
Type: String
QSS3BucketRegion:
Default: 'us-east-1'
Description: The AWS Region where the Quick Start S3 bucket (QSS3BucketName) is
hosted. When using your own bucket, you must specify this value.
Type: String
RemoteAccessCIDR:
AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$
ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/x
Description: The CIDR IP range that is permitted to access the instances. We recommend
that you set this value to a trusted IP range.
Type: String
VPCCIDR:
AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(1[6-9]|2[0-8]))$
ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
Default: 10.0.0.0/16
Description: The CIDR block for the VPC
Type: String
AdditionalEKSAdminArns:
Default: ""
Description: "[OPTIONAL] Comma separated list of IAM user/role Amazon Resource Names (ARNs) to be granted admin access to the EKS cluster"
Type: CommaDelimitedList
NodeInstanceType:
Default: t3.medium
AllowedValues:
- t2.small
- t2.medium
- t2.large
- t2.xlarge
- t2.2xlarge
- t3.nano
- t3.micro
- t3.small
- t3.medium
- t3.large
- t3.xlarge
- t3.2xlarge
- m3.medium
- m3.large
- m3.xlarge
- m3.2xlarge
- m4.large
- m4.xlarge
- m4.2xlarge
- m4.4xlarge
- m4.10xlarge
- m5.large
- m5.xlarge
- m5.2xlarge
- m5.4xlarge
- m5.12xlarge
- m5.24xlarge
- c4.large
- c4.xlarge
- c4.2xlarge
- c4.4xlarge
- c4.8xlarge
- c5.large
- c5.xlarge
- c5.2xlarge
- c5.4xlarge
- c5.9xlarge
- c5.18xlarge
- i3.large
- i3.xlarge
- i3.2xlarge
- i3.4xlarge
- i3.8xlarge
- i3.16xlarge
- r3.xlarge
- r3.2xlarge
- r3.4xlarge
- r3.8xlarge
- r4.large
- r4.xlarge
- r4.2xlarge
- r4.4xlarge
- r4.8xlarge
- r4.16xlarge
- x1.16xlarge
- x1.32xlarge
- p2.xlarge
- p2.8xlarge
- p2.16xlarge
- p3.2xlarge
- p3.8xlarge
- p3.16xlarge
- r5.large
- r5.xlarge
- r5.2xlarge
- r5.4xlarge
- r5.12xlarge
- r5.24xlarge
- r5d.large
- r5d.xlarge
- r5d.2xlarge
- r5d.4xlarge
- r5d.12xlarge
- r5d.24xlarge
- z1d.large
- z1d.xlarge
- z1d.2xlarge
- z1d.3xlarge
- z1d.6xlarge
- z1d.12xlarge
ConstraintDescription: Must be a valid EC2 instance type
Description: The type of EC2 instance for the node instances.
Type: String
NumberOfNodes:
Default: 3
Description: The number of Amazon EKS node instances. The default is one for each of the three Availability Zones.
Type: Number
MaxNumberOfNodes:
Default: 3
Description: The maximum number of Amazon EKS node instances. The default is three node.
Type: Number
NodeGroupName:
Default: Default
Description: The name for EKS node group.
Type: String
NodeVolumeSize:
Default: 20
Description: "The size for the node's root EBS volumes."
Type: String
ManagedNodeGroup:
AllowedValues: [ "yes", "no" ]
Default: "no"
Description: Choose if you want to use a managed node group. If you select "yes", you must select Kubernetes Version 1.14 or higher.
Type: String
ManagedNodeGroupAMIType:
Description: Select one of the two AMI types for your managed node group (only applies if you chose "yes" for ManagedNodeGroup). GPU instance types should use the AL2_x86_64_GPU AMI type, which uses the Amazon EKS-optimized Linux AMI with GPU support. Non-GPU instances should use the AL2_x86_64 AMI type, which uses the Amazon EKS-optimized Linux AMI.
AllowedValues: [ "AL2_x86_64", "AL2_x86_64_GPU", ""]
Default: "AL2_x86_64"
Type: String
KubernetesVersion:
Type: String
AllowedValues: [ "1.13", "1.14", "1.15" ]
Default: "1.15"
Description: The Kubernetes control plane version.
LambdaZipsBucketName:
Description: '[OPTIONAL] The name of the S3 bucket where the Lambda zip files should be placed. If you leave this parameter blank, an S3 bucket will be created.'
Type: String
Default: ''
ClusterAutoScaler:
Type: String
AllowedValues: [ Enabled, Disabled ]
Default: Disabled
Description: Choose Enabled to enable Kubernetes cluster autoscaler.
EfsStorageClass:
Type: String
AllowedValues: [ Enabled, Disabled ]
Default: Disabled
Description: Choose Enabled to enable EFS storage class, which will create the required EFS volume.
EfsPerformanceMode:
Type: String
AllowedValues: [ generalPurpose, maxIO ]
Default: generalPurpose
Description: Choose maxIO mode to provide greater IOPS with an increased latency. Only has an effect when EfsStorageClass is enabled.
EfsThroughputMode:
Type: String
AllowedValues: [ bursting, provisioned ]
Default: bursting
Description: Choose provisioned for throughput that is not dependent on the amount of data stored in the file system. Only has an effect when EfsStorageClass is enabled.
EfsProvisionedThroughputInMibps:
Type: Number
MinValue: 0
Default: 0
Description: Set to 0 if EfsThroughputMode is set to bursting. Only has an effect when EfsStorageClass is enabled.
MonitoringStack:
Type: String
AllowedValues: [ "Prometheus + Grafana", "None" ]
Default: "None"
Description: Enable Monitoring stack with "Prometheus+Grafana"
NumberOfAZs:
Type: String
AllowedValues: ["2", "3"]
Default: "3"
Description: Number of Availability Zones to use in the VPC. This must match your selections in the list of Availability Zones parameter.
ProvisionBastionHost:
Type: String
AllowedValues: [ "Enabled", "Disabled" ]
Default: "Enabled"
Description: "Skip creating a bastion host by setting this is set to Disabled."
Rules:
EKSSupport:
Assertions:
- AssertDescription: Your AWS Region does *NOT* yet support Amazon EKS
Assert: !Contains
- - us-west-2
- us-east-1
- us-east-2
- sa-east-1
- eu-west-1
- eu-west-2
- eu-west-3
- eu-north-1
- eu-central-1
- ap-southeast-1
- ap-southeast-2
- ap-northeast-1
- ap-northeast-2
- ap-south-1
- ca-central-1
- !Ref 'AWS::Region'
EKSVersion_ManagedNodeGroup:
RuleCondition: !Equals [ !Ref 'ManagedNodeGroup', 'yes' ]
Assertions:
- AssertDescription: To use Managed Node Groups you must use EKS version 1.14 or higher
Assert: !Contains
- [ '1.15', '1.14' ]
- !Ref 'KubernetesVersion'
ClusterAutoScalerVerification:
RuleCondition: !Equals [ !Ref 'ManagedNodeGroup', 'yes' ]
Assertions:
- AssertDescription: To use Cluster AutoScaler you should not use Managed Node Groups
Assert: !Contains
- - 'Disabled'
- !Ref 'ClusterAutoScaler'
Conditions:
3AZDeployment: !Equals [!Ref NumberOfAZs, "3"]
2AZDeployment: !Or
- !Equals [!Ref NumberOfAZs, "2"]
- !Equals [!Ref NumberOfAZs, "3"]
UsingDefaultBucket: !Equals [!Ref QSS3BucketName, 'aws-quickstart']
Resources:
VPCStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: !Sub
- 'https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QSS3KeyPrefix}submodules/quickstart-aws-vpc/templates/aws-vpc.template'
- S3Region: !If [UsingDefaultBucket, !Ref 'AWS::Region', !Ref QSS3BucketRegion]
S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName]
Parameters:
AvailabilityZones: !Join [ ',', !Ref 'AvailabilityZones' ]
KeyPairName: !Ref 'KeyPairName'
NumberOfAZs: !Ref 'NumberOfAZs'
PrivateSubnet1ACIDR: !Ref 'PrivateSubnet1CIDR'
PrivateSubnet2ACIDR: !Ref 'PrivateSubnet2CIDR'
PrivateSubnet3ACIDR: !Ref 'PrivateSubnet3CIDR'
PrivateSubnetATag2: "kubernetes.io/role/internal-elb="
PublicSubnet1CIDR: !Ref 'PublicSubnet1CIDR'
PublicSubnet2CIDR: !Ref 'PublicSubnet2CIDR'
PublicSubnet3CIDR: !Ref 'PublicSubnet3CIDR'
PublicSubnetTag2: "kubernetes.io/role/elb="
VPCCIDR: !Ref 'VPCCIDR'
EKSStack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: !Sub
- 'https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QSS3KeyPrefix}templates/amazon-eks.template.yaml'
- S3Region: !If [UsingDefaultBucket, !Ref 'AWS::Region', !Ref QSS3BucketRegion]
S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref QSS3BucketName]
Parameters:
PublicSubnet1ID: !GetAtt VPCStack.Outputs.PublicSubnet1ID
PublicSubnet2ID: !If
- 2AZDeployment
- !GetAtt VPCStack.Outputs.PublicSubnet2ID
- !Ref AWS::NoValue
PublicSubnet3ID: !If
- 3AZDeployment
- !GetAtt VPCStack.Outputs.PublicSubnet3ID
- !Ref AWS::NoValue
KeyPairName: !Ref KeyPairName
QSS3BucketName: !Ref QSS3BucketName
QSS3KeyPrefix: !Ref QSS3KeyPrefix
QSS3BucketRegion: !Ref QSS3BucketRegion
PrivateSubnet1ID: !GetAtt VPCStack.Outputs.PrivateSubnet1AID
PrivateSubnet2ID: !If
- 2AZDeployment
- !GetAtt VPCStack.Outputs.PrivateSubnet2AID
- !Ref AWS::NoValue
PrivateSubnet3ID: !If
- 3AZDeployment
- !GetAtt VPCStack.Outputs.PrivateSubnet3AID
- !Ref AWS::NoValue
NumberOfNodes: !Ref NumberOfNodes
MaxNumberOfNodes: !Ref MaxNumberOfNodes
NodeGroupName: !Ref NodeGroupName
NodeVolumeSize: !Ref NodeVolumeSize
ManagedNodeGroup: !Ref ManagedNodeGroup
ManagedNodeGroupAMIType: !Ref ManagedNodeGroupAMIType
LambdaZipsBucketName: !Ref LambdaZipsBucketName
NodeInstanceType: !Ref NodeInstanceType
RemoteAccessCIDR: !Ref RemoteAccessCIDR
AdditionalEKSAdminArns: !Join [ ",", !Ref AdditionalEKSAdminArns ]
VPCID: !GetAtt VPCStack.Outputs.VPCID
KubernetesVersion: !Ref KubernetesVersion
ProvisionClusterAutoScaler: !Ref ClusterAutoScaler
EfsStorageClass: !Ref EfsStorageClass
EfsPerformanceMode: !Ref EfsPerformanceMode
EfsThroughputMode: !Ref EfsThroughputMode
EfsProvisionedThroughputInMibps: !Ref EfsProvisionedThroughputInMibps
ProvisionMonitoringStack: !Ref MonitoringStack
ProvisionBastionHost: !Ref ProvisionBastionHost
Outputs:
KubeConfigPath:
Value: !GetAtt EKSStack.Outputs.KubeConfigPath
HelmLambdaArn:
Value: !GetAtt EKSStack.Outputs.HelmLambdaArn
KubeManifestLambdaArn:
Value: !GetAtt EKSStack.Outputs.KubeManifestLambdaArn
KubeGetLambdaArn:
Value: !GetAtt EKSStack.Outputs.KubeGetLambdaArn
EKSClusterName:
Value: !GetAtt EKSStack.Outputs.EKSClusterName
BastionIP:
Value: !GetAtt EKSStack.Outputs.BastionIP
BastionSecurityGroup:
Value: !GetAtt EKSStack.Outputs.BastionSecurityGroup
NodeGroupSecurityGroup:
Value: !GetAtt EKSStack.Outputs.NodeGroupSecurityGroup
I created also an IAM user to access my cluster whith this ARN : arn:aws:iam::XXXXXXXXXXXX:role/testrole
In order to access my cluster with this role aws suggested to update the config map aws-auth and to add something like this :
$ kubectl edit configmap aws-auth -n kube-system
mapRoles: |
- rolearn: arn:aws:iam::XXXXXXXXXXXX:role/testrole
username: testrole
groups:
- system:masters
link od recommandation here : https://aws.amazon.com/fr/premiumsupport/knowledge-center/eks-api-server-unauthorized-error/
Each day, when I leave work I have to delete my cluster and recreate it on monday (because of control plane cost). But I don't want each day, when I create my cluster with my cloud dormation template, to update manually my config map aws-auth to add the previous code.
How could I add this role in my cloudfourmation template in order to create my cluster with this default role added automatically ?
Thanks in advance
During the creation of the cluster with the template, there is a step asking you to add additionnal arn. You can specify here your arn then it will be added by default in your cluster automatically. You can set also this in your cloud formation file under :
AdditionalEKSAdminArns:
default: Additional EKS admin ARNs

Unable to create autoscalling group for application load balancer using aws cloud formation

AWS CFN build is failing with status "Value of property TargetGroupARNs must be of type List of String" for the following autoscalling group:
InfyASG:
Type: AWS::AutoScaling::AutoScalingGroup
UpdatePolicy:
AutoScalingRollingUpdate:
MaxBatchSize: "4"
MinInstancesInService:
Ref: InfyASGMin
Properties:
Cooldown: "300"
DesiredCapacity:
Ref: InfyASGDesiredSize
MaxSize:
Ref: InfyASGMaxSize
MinSize:
Ref: InfyASGMin
HealthCheckGracePeriod: "300"
HealthCheckType: ELB
VPCZoneIdentifier:
Ref: PrivateSubnet
LaunchConfigurationName:
Ref: InfyLaunchConfig
TargetGroupARNs:
- !GetAtt "InfyTG.LoadBalancerArns"
Tags:
- Key: Owner
Value:
Ref: BaseOwner
PropagateAtLaunch: true
- Key: Name
Value:
Fn::Sub: ${BaseName}-${Environment}-InfyASG
PropagateAtLaunch: true
- Key: Application
Value:
Ref: Application
PropagateAtLaunch: true
- Key: Environment
Value:
Ref: Environment
PropagateAtLaunch: true
- Key: Role
Value:
Fn::Sub: ${BaseName}-${Environment}-Role
PropagateAtLaunch: true
My target group name is "InfyTG". Only "TargetGroupARNs" was given under autoscaling group for mapping. I also have used the following, but that did not work.
TargetGroupARNs:
Ref: InfyTG
Should be problem mapping targetgroup to autoscaling group.
I try searching how the mapping will be between autoscaling group, target group, application load balancer but it didn't helped me fixing the above problem.
All I need is highly available application server under a target group so that I can route the traffic through application load balancer to the instance. If the instance is down, autoscalling group should generate new one and register it under the same target group.
Kindly help me with this.
Try this:
TargetGroupARNs: [!Ref InfyTG]
Alternatively (and you were close with your second form):
TargetGroupARNs:
- !Ref InfyTG

AWS ElasticSearch Service - Set Encryption options from CF template

I am creating a cloud formation template to provision elasticsearch service domain in AWS.
I would like to set this property under Encryption to true
"Require HTTPS for all traffic to the domain" but I am not able to find the way in AWS docs to do so.
Other options for setting encryption properties like
"Enable encryption of data at rest" & "Node-to-node encryption" are well documented.
Does anyone know how to set "Require HTTPS for all traffic to the domain" property from CF template ?
The way I did this was to make sure the security group only allows HTTPS (443) access to the cluster.
Not completely sure if this is what you are looking for, but if it's not, give me some more details and I will see if I can help you out.
mySecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
VpcId: !Ref VpcId
GroupName: !Ref SecurityGroup
GroupDescription: !Ref GroupDescription
SecurityGroupIngress:
- FromPort: '443'
IpProtocol: tcp
ToPort: '443'
CidrIp: 0.0.0.0/0
Here is what I have put together for Encryption at Rest as well as Node to node encryption:
Now, I had to add/edit plenty of moving parts, so I will post the whole template here so you can see what I did in the different sections so you can pull out what you need/want. I used Conditionals since you can chose to enable it or not. I also made sure to add a few comments here and there. Feel free to strip those if you want, but they do not impact the template at all.
edit: I know that there are several parts of the template that are not NEEDED, but I like to make mine look pretty and organized (when being used). I know I need to work on organization inside the template itself, but it's all there. :P
edit2: The below template assumes you will be using Existing VPC/Subnets/Security Groups, you can check out my repo (in comment below) for other versions of this template.
I did validate this template and was able to successfully build the Domain.
AWSTemplateFormatVersion: 2010-09-09
Description: >-
**NOTE** In order to create Elastisearch Domain in AWS using CloudFormation
verify you have the following Service Role created in IAM!!
-- AWSServiceRoleForAmazonElasticsearchService --
If you do not have this Role, create it using the following CLi Command --
aws iam create-service-linked-role --aws-service-name es.amazonaws.com
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
-
Label:
default: "Configure Cluster"
Parameters:
- DomainName
- ElasticsearchVersion
- ZoneAwareness
- SnapShotHour
-
Label:
default: "Data Instances"
Parameters:
- InstanceType
- DataInstanceCount
-
Label:
default: "Dedicated Master Instances"
Parameters:
- DedicatedMaster
- MasterInstanceType
- MasterInstanceCount
-
Label:
default: "Storage Config"
Parameters:
- StorageSize
-
Label:
default: "Network Config"
Parameters:
- VpcId
- SubNet1
- SubNet2
- SecurityGroup
-
Label:
default: "Encryption Settings"
Parameters:
- EncryptionAtRest
- KmsKey
- NodetoNode
-
Label:
default: "IAM User Restriction Policy"
Parameters:
- IamUserArn
ParameterLabels:
DomainName:
default: "Name of the ElasticSearch Domain (lowecase, no spaces) - If you don't specify a name, AWS CloudFormation generates a unique physical ID and uses that ID for the domain name"
ElasticsearchVersion:
default: "Select the ElasticSearch version desired"
InstanceType:
default: "Instance Size of Data Instances"
DataInstanceCount:
default: "Number of Data Instances Required"
DedicatedMaster:
default: "Select if a Dedicated Master Instance is required"
MasterInstanceType:
default: "Instance Size of Master Instances"
MasterInstanceCount:
default: "How many Dedicated Master Instances are needed? (0, 3 or 5)"
StorageSize:
default: "Storage Size in GB"
VpcId:
default: "Select the VPC to deploy into (must already exist)"
SubNet1:
default: "Select the First Subnet"
SubNet2:
default: "Select the Second Subnet"
SecurityGroup:
default: "Select the Security Group"
IamUserArn:
default: "Enter the ARN for the IAM User to give initial access to the stack"
ZoneAwareness:
default: "Enable Zone Awareness (Availability Zone Replication) (recommended)"
SnapShotHour:
default: "Set the hour to run the Automated Snapshot (0-23) (Default: UTC Timezone)"
EncryptionAtRest:
default: "Enable Encryption at Rest"
KmsKey:
default: "If Encryption at Rest is enabled, supply the KMS Key used for encryption"
NodetoNode:
default: "Enable Node to Node Encryption"
Parameters:
DomainName:
Type: String
Default: "elasticsearchstack-cf"
MaxLength: '128'
ConstraintDescription: "Must be lowercase, numbers/letters and/or a dash"
SnapShotHour:
Type: Number
Default: 0
MinValue: 0
MaxValue: 23
ElasticsearchVersion:
Type: String
Default: 7.1
AllowedValues: [7.1, 6.8, 6.7, 6.6, 6.5] # Remove this line for free-form number entry
InstanceType:
Type: String
Default: r5.large.elasticsearch
AllowedValues: [t2.small.elasticsearch, t2.medium.elasticsearch,
c4.large.elasticsearch, c4.xlarge.elasticsearch, c4.2xlarge.elasticsearch, c4.4xlarge.elasticsearch, c4.8xlarge.elasticsearch,
c5.large.elasticsearch, c5.xlarge.elasticsearch, c5.2xlarge.elasticsearch, c5.4xlarge.elasticsearch, c5.9xlarge.elasticsearch, c5.18xlarge.elasticsearch,
m3.medium.elasticsearch, m3.large.elasticsearch, m3.xlarge.elasticsearch, m3.2xlarge.elasticsearch,
m4.large.elasticsearch, m4.xlarge.elasticsearch, m4.2xlarge.elasticsearch, m4.4xlarge.elasticsearch, m4.10xlarge.elasticsearch,
m5.large.elasticsearch, m5.xlarge.elasticsearch, m5.2xlarge.elasticsearch, m5.4xlarge.elasticsearch, m5.12xlarge.elasticsearch,
r3.large.elasticsearch, r3.xlarge.elasticsearch, r3.2xlarge.elasticsearch, r3.4xlarge.elasticsearch, r3.8xlarge.elasticsearch,
r4.large.elasticsearch, r4.xlarge.elasticsearch, r4.2xlarge.elasticsearch, r4.4xlarge.elasticsearch, r4.16xlarge.elasticsearch,
r5.large.elasticsearch, r5.xlarge.elasticsearch, r5.2xlarge.elasticsearch, r5.4xlarge.elasticsearch, r5.12xlarge.elasticsearch,
i2.xlarge.elasticsearch, i2.2xlarge.elasticsearch,
i3.large.elasticsearch, i3.xlarge.elasticsearch, i3.2xlarge.elasticsearch, i3.4xlarge.elasticsearch, i3.8xlarge.elasticsearch, i3.16xlarge.elasticsearch]
ConstraintDescription: "Must be a valid EC2 Elasticsearch instance type."
DataInstanceCount:
Type: Number
Default: 2
AllowedValues: [2, 4, 6, 8, 10] # Remove this line for free-form number entry
MasterInstanceType:
Type: String
Default: r5.large.elasticsearch
AllowedValues: [t2.small.elasticsearch, t2.medium.elasticsearch,
c4.large.elasticsearch, c4.xlarge.elasticsearch, c4.2xlarge.elasticsearch, c4.4xlarge.elasticsearch, c4.8xlarge.elasticsearch,
c5.large.elasticsearch, c5.xlarge.elasticsearch, c5.2xlarge.elasticsearch, c5.4xlarge.elasticsearch, c5.9xlarge.elasticsearch, c5.18xlarge.elasticsearch,
m3.medium.elasticsearch, m3.large.elasticsearch, m3.xlarge.elasticsearch, m3.2xlarge.elasticsearch,
m4.large.elasticsearch, m4.xlarge.elasticsearch, m4.2xlarge.elasticsearch, m4.4xlarge.elasticsearch, m4.10xlarge.elasticsearch,
m5.large.elasticsearch, m5.xlarge.elasticsearch, m5.2xlarge.elasticsearch, m5.4xlarge.elasticsearch, m5.12xlarge.elasticsearch,
r3.large.elasticsearch, r3.xlarge.elasticsearch, r3.2xlarge.elasticsearch, r3.4xlarge.elasticsearch, r3.8xlarge.elasticsearch,
r4.large.elasticsearch, r4.xlarge.elasticsearch, r4.2xlarge.elasticsearch, r4.4xlarge.elasticsearch, r4.16xlarge.elasticsearch,
r5.large.elasticsearch, r5.xlarge.elasticsearch, r5.2xlarge.elasticsearch, r5.4xlarge.elasticsearch, r5.12xlarge.elasticsearch,
i2.xlarge.elasticsearch, i2.2xlarge.elasticsearch,
i3.large.elasticsearch, i3.xlarge.elasticsearch, i3.2xlarge.elasticsearch, i3.4xlarge.elasticsearch, i3.8xlarge.elasticsearch, i3.16xlarge.elasticsearch]
ConstraintDescription: "Must be a valid EC2 Elasticsearch instance type."
MasterInstanceCount:
Type: Number
Default: 0
AllowedValues: [0, 3, 5] # Remove this line for free-form number entry
VpcId:
Type: AWS::EC2::VPC::Id
ConstraintDescription: "Must be the VPC ID of an existing Virtual Private Cloud."
SubNet1:
Type: AWS::EC2::Subnet::Id
ConstraintDescription: "Must be the Subnet ID of an existing Subnet."
SubNet2:
Type: AWS::EC2::Subnet::Id
ConstraintDescription: "Must be the Subnet ID of an existing Subnet."
SecurityGroup:
Type: AWS::EC2::SecurityGroup::Id
ConstraintDescription: "Must be and existing Security Group."
DedicatedMaster:
Description: True or False
Type: String
Default: False
AllowedValues:
- True
- False
StorageSize:
Type: Number
Default: 20
MinValue: 10 # Remove this line for free-form number entry (suggested to keep this line)
MaxValue: 1000 # Remove this line for free-form number entry
IamUserArn:
Type: String
Default: "arn:aws:iam::<AccountNumber>:user/<username>"
ZoneAwareness:
Description: True or False
Type: String
Default: True
AllowedValues:
- True
- False
EncryptionAtRest:
Description: True or False
Type: String
Default: False
AllowedValues:
- True
- False
KmsKey:
Type: String
NodetoNode:
Description: True or False
Type: String
Default: False
AllowedValues:
- True
- False
Conditions: # Checks to see if Conditional Values are True
DedicatedMasterYes: !Equals [ !Ref DedicatedMaster, True]
EncryptionAtRestYes: !Equals [ !Ref EncryptionAtRest, True]
Resources:
ElasticsearchDomain:
Type: AWS::Elasticsearch::Domain
Properties:
DomainName: !Ref DomainName
ElasticsearchVersion: !Ref ElasticsearchVersion
ElasticsearchClusterConfig:
DedicatedMasterEnabled: !Ref DedicatedMaster
InstanceCount: !Ref DataInstanceCount
ZoneAwarenessEnabled: !Ref ZoneAwareness
InstanceType: !Ref InstanceType
DedicatedMasterType: # If Dedicated Master is True, then use !Ref, if not, use NoValue (NULL) for both settings below
!If [DedicatedMasterYes, !Ref MasterInstanceType, !Ref "AWS::NoValue"]
DedicatedMasterCount:
!If [DedicatedMasterYes, !Ref MasterInstanceCount, !Ref "AWS::NoValue"]
EBSOptions:
EBSEnabled: True
Iops: 0
VolumeSize: !Ref StorageSize
VolumeType: "gp2"
SnapshotOptions:
AutomatedSnapshotStartHour: !Ref SnapShotHour
AccessPolicies:
Version: 2012-10-17
Statement:
- Effect: Deny
Principal:
AWS: !Ref IamUserArn
Action: 'es:*'
Resource: "*"
AdvancedOptions: # BOTH of these settingsd are REQUIRED (regardless of what the documentation states) - Bug filed: https://forums.aws.amazon.com/thread.jspa?messageID=768527
rest.action.multi.allow_explicit_index: 'true'
indices.fielddata.cache.size: !Ref "AWS::NoValue"
VPCOptions:
SubnetIds:
- !Ref SubNet1
- !Ref SubNet2
SecurityGroupIds:
- !Ref SecurityGroup
EncryptionAtRestOptions: # If Encryption At Rest is True, then use !Ref, if not, use NoValue (NULL) for both settings below
!If [EncryptionAtRestYes, !Ref EncryptionAtRest, !Ref "AWS::NoValue"]
KmsKeyId:
!If [EncryptionAtRestYes, !Ref KmsKey, !Ref "AWS::NoValue"]
NodeToNodeEncryptionOptions:
Enabled: !Ref NodetoNode
Outputs:
DomainArn:
Value: !GetAtt ElasticsearchDomain.DomainArn
DomainEndpoint:
Value: !GetAtt ElasticsearchDomain.DomainEndpoint
SecurityGroupId:
Value: !Ref SecurityGroup
SubnetId1:
Value: !Ref SubNet1
SubnetId2:
Value: !Ref SubNet2
As feature was recently released, it is not yet available in CloudFormation or Terraform, But we can achieve same by using custom resource in a CF template. This custom resource is nothing but a lambda function which will be executed as part cloud formation stack creation.
complete automation can be divided into 3 section,
Lambda function development
Lambda function creation
Lambda function configuration as custom resource in Cloudformation stack
Lambda function development
As explained here follow below steps to create lambda fuction.
Create an empty folder that you’ll use to place your Lambda source. Then use pip to install crhelper into the folder, and create the lambda_function.py file to put your resource code into.
mkdir sum_function
cd sum_function
pip install -t . crhelper
# on some systems pip may fail with a distutils error, if you run into this, try
running pip with the –system argument
# pip install —system -t . crhelper
touch lambda_function.py
Now open the lambda_function.py file and place the following code into the lambda_function.py file.
from crhelper import CfnResource
import boto3
import logging
logger = logging.getLogger(__name__)
helper = CfnResource()
#helper.create
#helper.update
def update_es_domain_config(event, context):
logger.info("Updating elasticsearch domain config with HTTPS")
es = boto3.client('es')
response = es.update_elasticsearch_domain_config(
DomainName=event['ResourceProperties']['ESDomainName'],
DomainEndpointOptions={
'EnforceHTTPS': True,
'TLSSecurityPolicy': 'Policy-Min-TLS-1-0-2019-07'
}
)
logger.info("results:", response)
#helper.delete
def no_op(_, __):
pass
def handler(event, context):
helper(event, context)
After that zip the whole directory
zip -r ../sum.zip ./
Lambda function creation
Now create the lambda function, I am using CLI for the same. Replace the values according to your environment.
aws lambda create-function \
--function-name "update-es-domain" \
--handler "lambda_function.handler" \
--timeout 900 \
--zip-file fileb://../sum.zip \
--runtime python3.7 \
--role "arn:aws:iam::123412341234:role/lambda-cli-role"
You’ll need to take down the FunctionArn from the output to use in the next step.
Lambda function configuration as custom resource in Cloudformation stack
Now In Resources section of your CF template, after Elasticsearch resource, add custom resource as shown below. Replace the service token with the functionArn you get in last step as a output. Replace ESDomainName with the elasticsearch domain that you want to modify.
ESDomainUpdate:
Type: "Custom::elasticsearch"
Properties:
ServiceToken: "arn:aws:lambda:us-east-1:123412341234:function:update-es-domain"
ESDomainName: "advantagehelp"
PFB Complete Cloudformation template for reference.
AWSTemplateFormatVersion: 2010-09-09
Description: AWS CloudFormation templates to create AWS Elasticsearch Service domain.
Parameters:
NodeType:
Description: The node type to be provisioned for the Elasticsearch cluster
Type: String
Default: m4.large.elasticsearch
AllowedValues:
- m4.large.elasticsearch
- m4.xlarge.elasticsearch
- c4.large.elasticsearch
- c4.xlarge.elasticsearch
- r4.large.elasticsearch
- r4.xlarge.elasticsearch
ConstraintDescription: must be a valid Elasticsearch node type.
NodeCount:
Description: The number of nodes in the Elasticsearch cluster.
Type: Number
Default: '1'
AllowedESGetIpList:
Type: CommaDelimitedList
Description: List of ip and cidr for elasticsearch retrieval
AllowedESUpdateIpList:
Type: CommaDelimitedList
Description: List of ip and cidr for elasticsearch update
ESDomainName:
Type: String
Description: Name of Elastic Search Domain
Default: advantagehelp
Resources:
ElasticsearchDomain:
Type: 'AWS::Elasticsearch::Domain'
Metadata:
cfn_nag:
rules_to_suppress:
- id: W28
reason: "Needed to name elasticsearch domain"
Properties:
DomainName: !Ref ESDomainName
ElasticsearchClusterConfig:
DedicatedMasterEnabled: false
InstanceCount: !Ref NodeCount
ZoneAwarenessEnabled: false
InstanceType: !Ref NodeType
ElasticsearchVersion: '6.4'
AccessPolicies:
Version: 2012-10-17
Statement:
- Action:
- 'es:ESHttpGet'
- 'es:ESHttpHead'
Principal: '*'
Effect: Allow
Resource: !Sub arn:aws:es:${AWS::Region}:${AWS::AccountId}:domain/${ESDomainName}/*
Condition:
IpAddress:
'aws:SourceIp': !Ref AllowedESGetIpList
- Action:
- 'es:ESHttpGet'
- 'es:ESHttpHead'
- 'es:ESHttpPost'
- 'es:ESHttpPut'
Principal: '*'
Effect: Allow
Resource: !Sub arn:aws:es:${AWS::Region}:${AWS::AccountId}:domain/${ESDomainName}/*
Condition:
IpAddress:
'aws:SourceIp': !Ref AllowedESUpdateIpList
EBSOptions:
EBSEnabled: true
Iops: 0
VolumeSize: 10
VolumeType: gp2
SnapshotOptions:
AutomatedSnapshotStartHour: 1
AdvancedOptions:
rest.action.multi.allow_explicit_index: "true"
ESDomainUpdate:
Type: "Custom::elasticsearch"
Properties:
ServiceToken: "arn:aws:lambda:us-east-1:123412341234:function:update-es-domain"
ESDomainName: "advantagehelp"
DependsOn: ElasticsearchDomain
Outputs:
KibanaURL:
Description: Kibana URL
Value: !Join
- ''
- - !GetAtt
- ElasticsearchDomain
- DomainEndpoint
- /_plugin/kibana/
ElasticsearchEndpoint:
Description: Elasticsearch domain endpoint
Value: !GetAtt
- ElasticsearchDomain
- DomainEndpoint
ElasticsearchDomainARN:
Description: Elasticsearch domain ARN
Value: !GetAtt
- ElasticsearchDomain
- DomainArn

CodeDeploy does not deploy to new ASG group because of CloudFormation resources creation order

I am trying to develop an entire AWS architecture by usin CloudFormation only, however I am having some issues with the integration of CodeDeploy with CloudFormation and AutoScaling Group.
The problem is that, since I need to associate the CodeDeploy DeploymentGroup to an AutoScaling Group in order for the auto-deployment to work, CloudFormation recognizes the group as being required before creating the deployment group.
What happens is that the ASG gets created, instances start to spin up BEFORE the deployment group has been created, which means that these instances will never get deployed. I tried to think of a Lambda function to forcefully deploy these instances, however the problems persists because the CodeDeploy Deployment Group will still not be available yet most likely, or if it was, it's not reliable.
This problem only occurs when the stack is created for the first time.
This is my CloudFormation template:
[...]
UpdateApiAutoscalingGroup:
Type: AWS::AutoScaling::AutoScalingGroup
Properties:
AutoScalingGroupName:
Fn::Join:
- ''
- - !ImportValue UpdateApiCodeDeployApplication
- -autoscaling-group
- !Ref Environment
DesiredCapacity: !Ref MinimumApiAmount
HealthCheckGracePeriod: 30
HealthCheckType: ELB
LaunchConfigurationName: !Ref UpdateApiAutoscalingLaunchConfiguration
TargetGroupARNs:
- !Ref UpdateApiTargetGroup
MaxSize: !Ref MaximumApiAmount
MinSize: !Ref MinimumApiAmount
VPCZoneIdentifier:
- Fn::Select:
- 0
- !Split
- ","
- Fn::ImportValue:
!Sub "PrivateSubnets-${Environment}"
Tags:
- Key: Environment
Value: !Ref Environment
PropagateAtLaunch: true
- Key: CompanySshAccess
Value: 1
PropagateAtLaunch: true
- Key: Application
Value: update-api
PropagateAtLaunch: true
# Defines how the Update API servers should be provisioned in the scaling group.
UpdateApiAutoscalingLaunchConfiguration:
Type: AWS::AutoScaling::LaunchConfiguration
Properties:
AssociatePublicIpAddress: false
IamInstanceProfile: !Ref UpdateApiInstanceRole
ImageId: !FindInMap [Api, Image, !Ref Environment]
InstanceType: !FindInMap [Api, InstanceType, !Ref Environment]
LaunchConfigurationName: !Sub 'update-api-launchconfig-${Environment}'
SecurityGroups:
- Fn::ImportValue: !Sub 'InternalBastionSecurityGroupId-${Environment}'
- !GetAtt LoadBalancerProtectedSecurityGroup.GroupId
UpdateApiCodeDeploymentGroup:
Type: AWS::CodeDeploy::DeploymentGroup
Properties:
DeploymentGroupName: !Ref Environment
DeploymentConfigName: "atleast-one-instance-online"
ServiceRoleArn: !Ref CodeDeployServiceRoleArn # TODO: create CodeDeployServiceRole using CloudFormation
ApplicationName: !ImportValue UpdateApiCodeDeployApplication
LoadBalancerInfo:
ElbInfoList:
- Name: !GetAtt UpdateApiLoadBalancer.LoadBalancerName
DeploymentStyle:
DeploymentOption: !FindInMap [Api, DeploymentStyleOption, !Ref Environment]
DeploymentType: !FindInMap [Api, DeploymentStyleType, !Ref Environment]
AutoScalingGroups:
- !Ref UpdateApiAutoscalingGroup
[...]

How to launch an Amazon EC2 in a particular VPC in YAML CloudFormation template

How can I launch an Amazon EC2 instance in a particular subnet of a VPC using a YAML template in CloudFormation?
If anyone comes access this in the future, I was able to solve this by specifying the following: AvailabilityZone, SecurityGroupIds (not SecurityGroups), and SubnetId.
Resources:
EC2Instance:
Properties:
AvailabilityZone: us-east-1b
ImageId: ami-Id
InstanceType:
Ref: InstanceType
KeyName:
Ref: KeyName
Tags:
-
Key: "Name"
Value:
Ref: InstanceName
SecurityGroupIds:
- sg-idHere
SubnetId: subnet-idHere
Type: "AWS::EC2::Instance"
Make sure that the security group is available to the VPC you are trying to use. The SubnetId should represent the VPC.
Hierarchy:
VPC->SubnetID->SecurityGroupId
Here is the CF template for create a ec2 instance in region singapore. I have just used this template. If you are running in the other region please change ImageId name to met with you region
---
AWSTemplateFormatVersion: '2010-09-09'
Description: 'VPC with private subnets in two availability zones'
Parameters:
PrivateSubnet:
Description: Private Subnet to Attach NAT Gateway.
Type: AWS::EC2::Subnet::Id
InstanceType:
Description: EC2 instance type
Type: String
Default: t2.micro
AllowedValues: [t2.micro, t2.small, t2.medium, t2.large, m3.medium, m3.large,
m3.xlarge, m3.2xlarge, m4.large, m4.xlarge, m4.2xlarge, m4.4xlarge, m4.10xlarge,
c4.large, c4.xlarge, c4.2xlarge, c4.4xlarge, c4.8xlarge, c3.large, c3.xlarge,
c3.2xlarge, c3.4xlarge, c3.8xlarge, r3.large, r3.xlarge, r3.2xlarge, r3.4xlarge,
r3.8xlarge, i2.xlarge, i2.2xlarge, i2.4xlarge, i2.8xlarge]
ConstraintDescription: Please choose a valid instance type.
SSHKeyName:
Description: EC2 instance type
Type: String
ConstraintDescription: Please choose a valid KeyName
VolumeSize:
Description: size of volume
Type: Number
Default: 20
ConstraintDescription: Please choose a valid Number
AllowedValues: [20, 30, 40, 50]
IOPS:
Description: total ipos
Type: Number
Default: 100
ConstraintDescription: Please choose a valid Number
AllowedValues: [100, 200, 500, 1000]
ImageId:
Type: String
Description: 'value for region singapore. If you using other version please choose right'
Default: 'ami-33e4bc49'
Resources:
EC2Example:
Type: "AWS::EC2::Instance"
Properties:
SubnetId: !Ref PrivateSubnet
ImageId: !Ref ImageId
InstanceType: !Ref InstanceType
KeyName: !Ref SSHKeyName
BlockDeviceMappings:
-
DeviceName: /dev/sda1
Ebs:
VolumeType: io1
Iops: !Ref IOPS
DeleteOnTermination: false
VolumeSize: !Ref VolumeSize
Outputs:
EC2Example:
Description: 'Ec2 instance EC2Example'
Value: !Ref EC2Example
Export:
Name: !Sub '${AWS::StackName}-EC2Example'
The CloudFormation template includes a SubnetId parameter:
Type: "AWS::EC2::Instance"
Properties:
SubnetId: String
Simply insert the ID of the existing Subnet (eg subnet-1234abcd).