Reputation: 2896
I am trying to write a generic script in AWS Cloudformation CLI that will update the stacks' parameter AMI to a new value while leaving the rest of the parameters as is.
So far, I tried doing this like so:
aws cloudformation update-stack --stack-name asg-xxx-123 --use-previous-template --parameters ParameterKey=ApplicationName,UsePreviousValue=true ParameterKey=ArtefactVersion,UsePreviousValue=true ParameterKey=MachineImage,ParameterValue=ami-123
Notice that there are 2 parameters who are just using UsePreviousValue=true
and only the the value of ParameterKey=MachineImage
is the one that needs to change - this works fine.
However, since I need it to be a generic script how can I handle the case where some stacks have more parameters than above (or even some have different parameters but still have ParameterKey=MachineImage
)? Is there way to say only change value of ParameterKey=MachineImage
and all the rest should be using previous value without explicitly listing in the --parameters
?
Upvotes: 5
Views: 9933
Reputation: 6476
Per @MolecularMan's comment, provided you have the template that was previously used, you can use the deploy
command instead of update-stack
:
aws cloudformation deploy \
--stack-name asg-xxx-123 \
--template-file /path/to/template.json \
--region us-east-1 \
--capabilities CAPABILITY_NAMED_IAM \
--parameter-overrides MachineImage=ami-123
Upvotes: 0
Reputation: 1
I appreciated this forum, I was able to strategize accordingly and was able to come up with a solution using DescribeStacks. In this node lambda implementation following, I have it responding to a file upload event to s3 as part of a devops artifact pipeline. The ssm.putParameter command is specific to my use case but the rest shows the basic idea- first fetch the existing parameters, then update the stack with those existing parameters as well as the new one. Then create the change set, wait for it to be created, and finally execute it.
Thanks everyone!
const AWS = require('aws-sdk');
AWS.config.update({ region: process.env.APPLICATION_REGION });
const ssm = new AWS.SSM();
const cfn = new AWS.CloudFormation();
exports.handler = async (event) => {
try {
await cfn
.deleteChangeSet({
StackName: 'cwd-best-stack',
ChangeSetName: 'cwd-best-change-set',
})
.promise();
} catch (err) {
console.info('<<---<- no change set to delete! ->--->>');
}
const ssmPathInit = event.Records[0].s3.object.key.split('/')[0];
const ssmParamName = '/cwd/objVersions/' + ssmPathInit;
const ssmValue = event.Records[0].s3.object.versionId;
const ssmParamsEvent = {
Name: ssmParamName,
Value: ssmValue,
Overwrite: true,
Tier: 'Standard',
Type: 'String',
};
// Fetch Existing Parameters
const {
Stacks: [{ Parameters }],
} = await cfn.describeStacks({ StackName: 'cwd-best-stack' }).promise();
await ssm.putParameter(ssmParamsEvent).promise();
const changeSetParamsEvent = {
ChangeSetName: 'cwd-best-change-set',
StackName: 'cwd-best-stack',
ChangeSetType: 'UPDATE',
Parameters: [
...Parameters,
{
ParameterKey: 'cwd' + ssmPathInit.toLowerCase() + 'version',
ParameterValue: ssmValue,
},
],
UsePreviousTemplate: true,
Capabilities: ['CAPABILITY_IAM'],
};
await cfn.createChangeSet(changeSetParamsEvent).promise();
await new Promise((resolve) => {
setInterval(async () => {
const cfnDeets = await cfn
.describeChangeSet({
StackName: 'cwd-best-stack',
ChangeSetName: 'cwd-best-change-set',
})
.promise();
if (cfnDeets.ExecutionStatus === 'AVAILABLE') {
resolve('ok');
}
}, 5000);
});
await cfn
.executeChangeSet({
ChangeSetName: 'cwd-best-change-set',
StackName: 'cwd-best-stack',
})
.promise();
console.info('executed change set');
};
Upvotes: 0
Reputation: 2896
I was able to write the unix script using the aws cli as per below:
curdate=`date +"%Y-%m-%d"`
newami=${1}
for sname in $(aws cloudformation describe-stacks --query "Stacks[?contains(StackName,'prefix-') ].StackName" --output text) ;
do
paramslist="--parameters ";
for paramval in $(aws cloudformation describe-stacks --stack-name $sname --query "Stacks[].Parameters[].ParameterKey" --output text) ;
do
if [ $paramval == "MachineImg" ] || [ $paramval == "AMI" ]
then
paramslist+="ParameterKey=${paramval},ParameterValue=${newami} "; #use the ami from args
else
paramslist+="ParameterKey=${paramval},UsePreviousValue=true "; #else keep using UsePreviousValue=true
fi
done
printf "aws cloudformation update-stack --stack-name ${sname} --use-previous-template ${paramslist};\n" >> "/tmp/ami-update-${curdate}.sh"
done
Which generates a new .sh file which contains the update commands, I then review the contents of that generated .sh and do a source to execute these :
source ./ami-update-2020-08-17.sh
Upvotes: 7
Reputation: 238169
Based on the comments.
The aws cloudformation update-stack
does not provide desired functionality.
However, a possible solution is develop a custom wrapper around aws cloudformation update-stack
. The wrapper would allow a user to provide only new/changed parameters. It would also use describe-stacks command to get the current values of existing stack parameters.
Having the current parameters from the stack, as well as the new/changed ones, the wrapper could construct construct the valid aws cloudformation update-stack
command and execute the update.
Upvotes: 4