'AWS Cloudformation Default keypair for ec2
I have a cloudformation template to create an ec2-instance. That template also starts an httpd along with some content that is served.
I'm using the Parameter section to allow a key to be specified or selected - see snippet below:
Parameters:
paramKeyPair:
Description: KeyPairName
Type: AWS::EC2::KeyPair::KeyName
I'm calling the ec2-instance through the AWS CLI like this :
aws cloudformation create-stack --stack-name stack-ec2instance --template-body file://demo-ec2instance --parameters ParameterKey=paramKeyPair,ParameterValue=peterKeyPair
So the instance can be created and the keypair can be passed through as an argument - BUT - frankly I don't actually care that much if the instance can be access. It's just a web server that can be spun up or down. SSH access is nice but no big deal.
In fact, if I removed the keypair Parameter from the cloudformation template - and removed the associated reference in the AWS CLI call - Cloudformation will happily spin up the instance without a keypair. Great !
What I would really like is for cloudformation to deal with the keypair being present or not. I thought the best way to do this would be to update the code so that the parameter has a default value of "None" (for example) and then the ec2-instance could be run from the AWS CLI and if the keypair parameter is not specified then AWS would know not to bother with the keypair at all.
The problem is that by specifying the Type as AWS::EC2::KeyPair::KeyName, the AWS CLI expects an actual value.
I'm out of ideas - if anyone else has figured this out - I would really appreciate it. Thankyou Peter.
Solution 1:[1]
If I understand you correctly you want to be able to keep the parameter in your Cloudformation template, but only "allocate" a key pair to an instance if you specify a value, otherwise don't allocate a key pair to the ec2 instance resource. You can do this with AWS::NoValue pseudo parameter.
Here is a sample template:
Description: My EC2 instance
Parameters:
SSHKeyName:
Type: String
Conditions:
Has-EC2-Key:
!Not [ !Equals [ !Ref SSHKeyName, '' ] ]
Resources:
Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: <InstanceImageID>
InstanceType: t2.micro
KeyName: !Ref SSHKeyName
KeyName:
Fn::If:
- Has-EC2-Key
- Ref: SSHKeyName
- Ref: AWS::NoValue
<other properties as required
So what this does is the condition checks if a SSHKeyName value is blank, if it's blank then the KeyName property will be ignored, if it isn't blank then it will use the value of SSHKeyName.
Solution 2:[2]
Thankyou WarrenG, your solution worked with one small exception which was to change the parameter type from AWS::EC2::KeyPair::KeyName to String. Without your help I am certain I would have burned many more hours on this.
So in conclusion, the fix was 1: Change the Parameter type to String.
Parameters:
SSHKeyName:
Type: String
2: Add a function that determines if the key is present.
Conditions:
Has-EC2-Key:
!Not [ !Equals [ !Ref SSHKeyName, '' ] ]
- Use the function within the resources section.
KeyName:
Fn::If:
- Has-EC2-Key
- Ref: SSHKeyName
- Ref: AWS::NoValue
Within my question I kept the code snippets to a minimum for readability but now I have marked this as solved I'm adding two blocks of code just for documentation and incase this helps anyone else.
- One example of calling the template through the AWS CLI.
aws cloudformation create-stack --stack-name stack-ec2instance --template-body file://demo-ec2instance --parameters ParameterKey=paramSubnetId,ParameterValue=$SubnetId ParameterKey=paramKeyPair,ParameterValue=peterKeyPair ParameterKey=paramSecurityGroupIds,ParameterValue=$SecurityGroupId
- The template to create a EC2 instance.
AWSTemplateFormatVersion: 2010-09-09
Parameters:
SSHKeyName:
Description: EC2 KeyPair for SSH access.
Type: String
Conditions:
Has-EC2-Key:
!Not [ !Equals [ !Ref SSHKeyName, '' ] ]
Mappings:
RegionMap:
eu-west-1:
AMI: ami-3bfab942
eu-west-2:
AMI: ami-098828924dc89ea4a
Resources:
EC2Instance:
Type: AWS::EC2::Instance
Metadata:
AWS::CloudFormation::Init:
config:
packages:
yum:
httpd: []
php: []
files:
/var/www/html/index.php:
content: !Sub |
<?php print "Hello Peter !"; ?>
services:
sysvinit:
httpd:
enabled: true
ensureRunning: true
Properties:
InstanceType: t2.micro
ImageId:
Fn::FindInMap:
- RegionMap
- !Ref AWS::Region
- AMI
SecurityGroupIds:
- !Ref MySecurityGroup
KeyName:
Fn::If:
- Has-EC2-Key
- Ref: SSHKeyName
- Ref: AWS::NoValue
UserData:
'Fn::Base64':
!Sub |
#!/bin/bash -xe
# Ensure AWS CFN Bootstrap is the latest
yum install -y aws-cfn-bootstrap
# Install the files and packages from the metadata
/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource EC2Instance --region ${AWS::Region}
MySecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Open Ports 22 and 80
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: '22'
ToPort: '22'
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: '80'
ToPort: '80'
CidrIp: 0.0.0.0/0
Outputs:
Website:
Description: The Public DNS for the EC2 Instance
Value: !Sub 'http://${EC2Instance.PublicDnsName}'
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | Peter Rhodes |
