Issue
I am associating an elastic IP to an EC2 instance that i am creating via cloudformation. I am attaching it to my EC2 instance via
AWSTemplateFormatVersion: 2010-09-09
Description:
Testing stuff
Resources:
myENI:
Type: AWS::EC2::NetworkInterface
Properties:
SubnetId: <subnet-id>
Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: <ami-id>
InstanceType: t2.micro
NetworkInterfaces:
- NetworkInterfaceId: !Ref myENI
DeviceIndex: '0'
Tags:
- Key: Name
Value: test-instance-1
myEIP:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
EIPAssociation:
Type: AWS::EC2::EIPAssociation
Properties:
AllocationId: !GetAtt myEIP.AllocationId
NetworkInterfaceId: !Ref myENI
DependsOn: Instance
and i am creating the stack using aws cloudformation create-stack --stack-name ec2-example --template-body file://test-instance.yaml
Which creates my resources including the elastic IP. 35.82.18.137
, an instance, EIPAssociation and it's own ENI.
Now i spin up another EC2 instance by updating my existing stack by changing the value of tags to test-instance-2 using the following command aws cloudformation create-stack --stack-name ec2-example-1 --template-body file://test-instance.yaml
AWSTemplateFormatVersion: 2010-09-09
Description:
Testing stuff
Resources:
myENI:
Type: AWS::EC2::NetworkInterface
Properties:
SubnetId: <subnet-id>
Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: <ami-id>
InstanceType: t2.micro
NetworkInterfaces:
- NetworkInterfaceId: !Ref myENI
DeviceIndex: '0'
Tags:
- Key: Name
Value: test-instance-2
myEIP:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
EIPAssociation:
Type: AWS::EC2::EIPAssociation
Properties:
AllocationId: !GetAtt myEIP.AllocationId
NetworkInterfaceId: !Ref myENI
DependsOn: Instance
Which creates a new instance and issues a new EIP : 44.229.115.61
, an instance, EIPAssociation and it's own ENI.
How can i switch over the old elastic IP 35.82.18.137
from the old instance to the new instance without deleting the old instance? Is that even possible using cloudformation ( No clickops if possible )?
Solution
Yes, it's possible as outlined in the documentation: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/refactor-stacks.html
Here's a demo (all of the code snippets below are run in bash
).
Let's say we have these two templates (note that the first template must have the deletion policy set to Retain
for the EIP):
template1.yml :
AWSTemplateFormatVersion: 2010-09-09
Description:
Testing stuff
Resources:
myENI:
Type: AWS::EC2::NetworkInterface
Properties:
SubnetId: <subnet-id>
Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-02d7ab1717c7787ce
InstanceType: t2.micro
NetworkInterfaces:
- NetworkInterfaceId: !Ref myENI
DeviceIndex: '0'
Tags:
- Key: Name
Value: test-instance-1
myEIP:
Type: AWS::EC2::EIP
DeletionPolicy: Retain
Properties:
Domain: vpc
EIPAssociation:
Type: AWS::EC2::EIPAssociation
Properties:
AllocationId: !GetAtt myEIP.AllocationId
NetworkInterfaceId: !Ref myENI
DependsOn: Instance
template2.yml:
AWSTemplateFormatVersion: 2010-09-09
Description:
Testing stuff
Resources:
myENI:
Type: AWS::EC2::NetworkInterface
Properties:
SubnetId: <subnet-id>
Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-02d7ab1717c7787ce
InstanceType: t2.micro
NetworkInterfaces:
- NetworkInterfaceId: !Ref myENI
DeviceIndex: '0'
Tags:
- Key: Name
Value: test-instance-2
then let's deploy them both:
$ aws cloudformation deploy --template-file template1.yml --stack-name stack1
$ aws cloudformation deploy --template-file template2.yml --stack-name stack2
once the stacks have been deployed, we need to fetch the allocation id and the public ip of the elastic ip deployed in the first stack:
$ EIP_PHYSICAL_RESOURCE_ID=$(aws cloudformation describe-stack-resources --stack-name stack1 --query 'StackResources[?LogicalResourceId == `myEIP`].PhysicalResourceId' --output text)
$ EIP_ALLOCATION_ID=$(aws ec2 describe-addresses --query "Addresses[?PublicIp == '${EIP_PHYSICAL_RESOURCE_ID}'].AllocationId" --output text)
we can now proceed to remove the EIP and the EIP association from the first stack:
template1.yml
AWSTemplateFormatVersion: 2010-09-09
Description:
Testing stuff
Resources:
myENI:
Type: AWS::EC2::NetworkInterface
Properties:
SubnetId: <subnet-id>
Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-02d7ab1717c7787ce
InstanceType: t2.micro
NetworkInterfaces:
- NetworkInterfaceId: !Ref myENI
DeviceIndex: '0'
Tags:
- Key: Name
Value: test-instance-1
and let's update the first stack:
$ aws cloudformation update-stack --template-body file://template1.yml --stack-name stack1
now we must modify the second template to include the EIP.
template2.yml:
AWSTemplateFormatVersion: 2010-09-09
Description:
Testing stuff
Resources:
myENI:
Type: AWS::EC2::NetworkInterface
Properties:
SubnetId: <subnet-id>
Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-02d7ab1717c7787ce
InstanceType: t2.micro
NetworkInterfaces:
- NetworkInterfaceId: !Ref myENI
DeviceIndex: '0'
Tags:
- Key: Name
Value: test-instance-2
myEIP:
Type: AWS::EC2::EIP
DeletionPolicy: Retain
Properties:
Domain: vpc
now let's create the following file (file.txt
) which will be used in the import operation later:
[
{
"ResourceType":"AWS::EC2::EIP",
"LogicalResourceId":"myEIP",
"ResourceIdentifier": {
"AllocationId": "$EIP_ALLOCATION_ID",
"PublicIp": "$EIP_PHYSICAL_RESOURCE_ID"
}
}
]
and now let's populate it (envsubst
is a handy way to replace the two variables into the text file, however you can obviously do this in other ways too):
$ export EIP_PHYSICAL_RESOURCE_ID
$ export EIP_ALLOCATION_ID
$ envsubst < file.txt > resourcesToImport.txt
resourcesToImport.txt
now contains the allocation id and public ip of the EIP.
Let's run the import operation:
$ aws cloudformation create-change-set \
--stack-name stack2 --change-set-name ImportChangeSet \
--change-set-type IMPORT \
--resources-to-import file://resourcesToImport.txt \
--template-body file://template2.yml
$ aws cloudformation execute-change-set --change-set-name ImportChangeSet --stack-name stack2
the second stack now has imported the EIP.
Finally, let's create an association with the existing instance
template2.yml:
AWSTemplateFormatVersion: 2010-09-09
Description:
Testing stuff
Resources:
myENI:
Type: AWS::EC2::NetworkInterface
Properties:
SubnetId: <subnet-id>
Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-02d7ab1717c7787ce
InstanceType: t2.micro
NetworkInterfaces:
- NetworkInterfaceId: !Ref myENI
DeviceIndex: '0'
Tags:
- Key: Name
Value: test-instance-2
myEIP:
Type: AWS::EC2::EIP
DeletionPolicy: Retain
Properties:
Domain: vpc
EIPAssociation:
Type: AWS::EC2::EIPAssociation
Properties:
AllocationId: !GetAtt myEIP.AllocationId
NetworkInterfaceId: !Ref myENI
DependsOn: Instance
and let's update the stack:
$ aws cloudformation update-stack --template-body file://template2.yml --stack-name stack2
The EIP which was originally associated with the instance in the first stack is now associated with the instance in the second stack
Answered By - Paolo Answer Checked By - David Goodson (WPSolving Volunteer)