fix: publish new Lambda version when AutoPublishAliasAllProperties references a changed parameter - #3965
Open
roger-zhangg wants to merge 2 commits into
Conversation
…operty references a changed parameter Fixes aws#3820. With `AutoPublishAliasAllProperties: true`, a property whose value comes from a template parameter — e.g. an Environment variable set to `!Ref SomeParam` — did not trigger a new Lambda version when the parameter value changed, so `sam deploy` published nothing. The version's logical id is a hash of the properties that should trigger a new version. The non-AllProperties (CodeUri) path resolves parameter references before hashing, with a comment explaining exactly why: an unresolved `{"Ref": "SomeParam"}` hashes identically regardless of the value supplied. The AllProperties path skipped that step and hashed the raw resource dict, so `{"Ref": "TestParameter"}` produced the same id whether the override was `2` or `3`. Resolve template parameter references on the AllProperties dict too. Pseudo parameters (AWS::Region, AWS::Partition, ...) are excluded: they are present in the resolver's parameter map but do not represent a template change, and resolving them would rewrite `Fn::Sub` strings that reference them and shift the version id of existing, unchanged templates. Excluding them keeps this a no-op for any template that does not reference a real parameter in a version-tracked property, so existing version ids are preserved — verified by the full translator suite passing unchanged (2174 tests). Testing: - New regression tests in test_function_resources.py assert that changing a referenced parameter value yields a different version logical id, and that an unchanged value is stable. Confirmed both fail against the pre-fix code. - Full tests/translator suite passes (2229 tests), so no golden-file version id changed — confirming backward compatibility, including the pseudo-parameter fixture (function_with_alias_and_all_properties_property) that a naive fix regressed.
… output Addresses review feedback on aws#3965. `resolve_parameter_refs` mutates the dict it is given -- `_traverse_dict` assigns back via `input_dict[key] = ...` -- and the values reachable from `_generate_resource_dict()` are the live objects from the user's template (`properties["Environment"] is function.Environment`). Resolving in place therefore inlined parameter values into the resources the translator actually emits, replacing `{"Ref": "TestParameter"}` with the literal on the AWS::Lambda::Function. Confirmed a NoEcho parameter value appeared as plaintext in the transformed template. The layer branch is affected the same way and is worse: `layer_properties` comes from ResourceResolver over the output template, so resolving could rewrite a different resource's emitted properties. Resolve against `copy.deepcopy(properties)` instead. This is what the resolver's own docstring warns about: "Don't pass this dictionary directly into transform's output because it changes the template structure by inlining parameter values." Adds two tests that a version-logical-id assertion cannot cover: the emitted function still carries `{"Ref": "TestParameter"}`, and a NoEcho value never appears anywhere in the output. Both fail without the deepcopy. Testing: 4 tests in the new class pass, and both new ones confirmed to fail against the pre-deepcopy code. Full tests/translator suite passes (2425). ruff and black clean.
Contributor
|
An Alternative to avoid the breaking change is to add a new parameter to track this behavior. Which could be something like |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Issue #, if available
Fixes #3820
Description of changes
With
AutoPublishAliasAllProperties: true, a property whose value comes from a template parameter — e.g. anEnvironmentvariable set to!Ref SomeParam— did not trigger a new Lambda version when the parameter value changed, sosam deploypublished nothing.The version's logical id is a hash of the properties that should trigger a new version. The non-
AllProperties(CodeUri) path resolves parameter references before hashing, with a comment explaining exactly why — an unresolved{"Ref": "SomeParam"}hashes identically regardless of the value supplied:The
AllPropertiespath skipped that step and hashed the raw resource dict, so{"Ref": "TestParameter"}produced the same id whether the override was2or3, and no newAWS::Lambda::Versionwas created.This change resolves template parameter references on the
AllPropertiesdict too. Pseudo parameters (AWS::Region,AWS::Partition, ...) are deliberately excluded: they live in the resolver's parameter map but do not represent a template change, and resolving them would rewriteFn::Substrings that reference them — shifting the version id of existing, unchanged templates. Excluding them keeps this a no-op for any template that does not reference a real parameter in a version-tracked property, so existing version ids are preserved.Flagging this explicitly: for affected templates, a customer who changes nothing in their template will get one new Lambda version published on their next deploy.
The version logical id is a hash of the tracked properties. This change alters the hash input for exactly one case, so the id shifts once even with an unchanged template:
AutoPublishAliasAllProperties: true)!Ref MyParam)FVersion63442c9615FVersioncf1b5d3b18FVersiona6597ab2daFVersiona6597ab2daFn::Subwith pseudo params (${AWS::Partition})FVersionb48d397249FVersionb48d397249!Refto another resource (not a parameter)FVersion7e8c27dae7FVersion7e8c27dae7(Measured by transforming the same template with the same parameter values on
developvs this branch.)Scope: only templates that both set
AutoPublishAliasAllProperties: trueand reference a template parameter in a version-tracked property. Everything else is byte-identical, which is why all 2174 existing transform tests pass with no golden-file changes.Impact of that one-time shift: CloudFormation sees a new
AWS::Lambda::Versionlogical id, creates the new version, and points the alias at it. The old version is retained (DeletionPolicy: Retain), so nothing is deleted. Practical effects to be aware of:DeploymentPreference(canary/linear), that deploy will run a real traffic shift rather than a no-op.This is unavoidable if the bug is to be fixed at all: publishing a version when the parameter changes requires the parameter value to be part of the hash. I kept the blast radius as small as I could by excluding pseudo parameters — resolving those too would have shifted ids for any template using
${AWS::Partition}-styleFn::Sub, which is a far larger population and is what an earlier iteration of this patch did (caught byfunction_with_alias_and_all_properties_property).Worth a note in the release notes.
Description of how you validated changes
tests/translator/test_function_resources.py): assert that changing a referenced parameter value yields a different version logical id, and that an unchanged value is stable. Confirmed both fail against the pre-fix code and pass with the fix.tests/translatorsuite passes (2229 tests) with no golden-file version id changes — confirming backward compatibility. This specifically includesfunction_with_alias_and_all_properties_property, a fixture that usesFn::Subwith${AWS::Partition}etc., which a naive fix (resolving all parameters including pseudo) regressed. The pseudo-parameter exclusion is what keeps that fixture stable.ruffandblackclean on both files.Checklist
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.