Open plumdog opened 3 years ago
Hey @plumdog ,
thanks for opening the issue. You've ran into a very interesting edge case in CloudFormation. The crux of the issue is:
There is a difference in the unresolved template, but not in the resolved template
Where "resolve" means "the values of the parameters have been substituted into the template body, and all CloudFormation functions have been called".
Now, the most important thing: this substitution is actually safe. Meaning, if you run cdk deploy
while having that result of cdk diff
, CloudFormation will generate an empty change set, and your Bucket will not be replaced.
The bad part is that, because the change set was empty, no actual deployment happened - which means cdk diff
after that will still see the old template, and report that same difference.
You can get rid of it by changing any property of the affected resource - something like the metadata is enough. So, in your case, that would be:
const backupBucket = cfnInclude.getResource('BackupBucket');
backupBucket.addMetadata('A', 'B'); // this can be anything
After running cdk deploy
, the metadata will force the resource to be updated, and the template for it will be changed (again, this is safe; contrary to what cdk diff
says, your Bucket will not be replaced!).
After that deployment succeeds, you can safely get rid of the metadata, and perform another deployment to have your resource be "pristine".
Having cleared all that up, the question remains on "how do we make that cdk diff
message less scary". I think it's an interesting problem! We would need to check the current value of the parameters, and "resolve" CloudFormation functions like Fn::Sub
and Fn::Join
to actually determine whether the value in the template and the new value are in fact the same after parameter substitution (even though they differ in the template itself).
I'm leaving this as a feature request for the cloudformation-diff
module, where this logic would have to live.
Having cleared all that up, the question remains on "how do we make that
cdk diff
message less scary". I think it's an interesting problem! We would need to check the current value of the parameters, and "resolve" CloudFormation functions likeFn::Sub
andFn::Join
to actually determine whether the value in the template and the new value are in fact the same after parameter substitution (even though they differ in the template itself).I'm leaving this as a feature request for the
cloudformation-diff
module, where this logic would have to live.
This feature would be hugely helpful for those of us migrating lots of legacy stacks. We've been resorting to creating and manually verifying changesets and it's tedious at best.
My issue is cdk diff
keep fall back to no-change-set diff because parameters missing. In command help, cannot find the parameters option. How can I specify parameters to the include cloudformation template?
@singlewind you do it by passing them when instantiating the CfnInclude
construct. See the documentation of this feature for more information.
@skinny85, Yes, we tried this. It replaced the reference token with actual value. This is not the exact output as we expected. It will remove all the description of predefined parameters. It may cause a few issues for a few contructs to recreate. For example export CfnOutput, if another stack has import from it, the change-set will fail to deploy. Just try to figure out what will be a consistent way between diff
and deploy
commands.
How to do that is described in this comment: https://github.com/aws/aws-cdk/issues/13346#issuecomment-789110447. It won't remove or change any Outputs in the Stack (assuming you provided the correct values to the parameters), so everything should work.
If we have a look the synth result. Previously, the parameter was referenced as token now has been changed to value. Previously, the description and type of parameters was defined in cloudformation. Now it has been wiped out. type and default values possibly doesn't need any more. Description can keep I think. The same on all the resources. The token references has been replaced by value. What if we do want keep it as reference. For example a parameter store securestring value.
You can choose to keep any parameters you want, so if you want to keep the ParameterStore SecureString one, you're free to do that 🙂.
Are you saying that, when you keep some or all of Parameters when you do the include, cdk diff
still shows some difference for these parameters?
Thanks @skinny85 for sharing this. No, the function to replace a CloudFormation parameter works as charm. Just for best practices and consistent. cdk deploy
allow pass parameters
as argument but cdk diff
doesn't accept the parameters
as argument. The result of that, if we're not replacing all the parameters, cdk diff
unable create the diff results based on change-set.
Does that mean cdk diff
fails? Or does it report false results?
No. @skinny85 , it will fallback to no-change-set diff.
Right. And that’s the correct behavior if there are no changes, right? Or what should happen in that situation?
@skinny85 , in this situation. The change result is from no-change-set diff
. This name may confuse. It means the compare result is not from CloudFormation by create a change-set. It is based on CDK self to work out the change set. The change set will be based on the diff to the include CloudFormation stack.
I don't understand what that means, sorry 😀.
Can you please provide:
CfnInclude
construct, etc.). A small reproduction that I can try out locally on my machine would be ideal.cdk diff
(please provide the exact command that you execute, with all arguments) for that Stack.cdk diff
for that Stack.Otherwise, we'll just be spinning in circles like we did here for like the 10 last comments 😀.
Thanks, Adam
So if I'm understanding this correctly, cdk diff
is reporting incorrect information, so this is a bug, right? cdk diff
says a resource will be replaced when it actually will not be:
contrary to what cdk diff says, your Bucket will not be replaced!
And it has been that way for years?
Sorry, I missed the reply. There are two mechanisms for cdk diff
. One is just based on comparing the previous deployed template and current deploying template. Another one is analysing the difference by creating a cloudformation changeset. When use CfnInclude
a template with replace of parameters, the first approach won't be able to identify the changes. Because the resource name previously is dynamic now it is becoming static. People doesn't know the consequence until they try to deploy it. If cdk diff
allow pass the parameters options just like cdk deploy
, we should be able to trigger the 2nd approach to identify changes and people will more confident see that compare result instead. At the moment, if there is parameters in original cloudformation template, cdk diff
is unable to use the 2nd approach, it will try to use the 1st approach. If the parameter is not replace in the code, it simply throws errors. If we replaced in the CDK code, then we fall into the situation of scary message.
@singlewind in that case, I think it's better to split the migration into smaller steps:
CfnInclude
. Do not replace any parameters at this point, just import the template as-is.cdk diff
. This should show you a small diff related to CDK metadata (I talk about it in my video on migrating from CFN to CDK).cdk deploy
to do that CDK metadata-only update. After this, ckd diff
should show no changes.CfnInclude
, going through the procedure outlined in https://github.com/aws/aws-cdk/issues/13346#issuecomment-789110447.Thanks @skinny85 , that was exactly we were doing. I just wonder whether it is possible have cdk diff
can respect --parameters
options as cdk deploy
does.
I think it's not a bad idea for a feature request 🙂.
@singlewind you may want to use cdk migrate instead of CfnInclude. It will translate your CloudFormation stack into actual CDK code (L1s). The one limitation that it will create a new CDK app so if you're looking to just add a stack into an existing app, it doesn't quite do that.
Introduce a way of setting parameters, but in such a way that the diff doesn't make me scared when migrating existing Cloudformation stacks into CDK.
Use Case
I have an existing production system deployed using Cloudformation with parameters, and want to bring it into CDK so I can write new code as CDK and throw away the old deployment tooling. And, where possible, manage changes to the old code through CDK (eg using
cfnTemplate.getResource
). And I think this will all work fine.However, I pointed it at my yaml templates, set the parameters for each stack, ran cdk diff and was immediately terrified. There were lots and lots of changes because of clever CDK things setting parameters. I think all these changes are no-ops from what I have been able to manually review.
A small slice of the diff I see:
Where the existing Cloudformation parameter
EnvironmentName
is set tomyapp
, and I've passedparameters: {EnvironmentName: 'myapp'}
toCfnInclude
. Is it going to try to delete that bucket and replace it? I think... no? It will fail to do so even if it does try, because there's stuff in the bucket, but hopefully this illustrates the source of my fear.Proposed Solution
Some options I can think of:
cfn-include.CfnInclude
grow the ability to set parameters "the old fashioned way", which would match with what I am migrating into CDK using this module. I would expect this to then show nothing in the diff (with the exception of the CDK metadata stuff) which would no longer terrify me. I could then maybe switch the parameters over one-by-one to reduce my fear and to make the diff easier to understand, if I felt the need.I would be fairly happy with any of those options except 2. And there might well be another way of handling this to change the synth behaviour around parameters that I don't know.
This is a :rocket: Feature Request