Skip to content

fix: publish new Lambda version when AutoPublishAliasAllProperties references a changed parameter - #3965

Open
roger-zhangg wants to merge 2 commits into
aws:developfrom
roger-zhangg:fix/autopublishaliasallproperties-resolve-params
Open

fix: publish new Lambda version when AutoPublishAliasAllProperties references a changed parameter#3965
roger-zhangg wants to merge 2 commits into
aws:developfrom
roger-zhangg:fix/autopublishaliasallproperties-resolve-params

Conversation

@roger-zhangg

@roger-zhangg roger-zhangg commented Jul 31, 2026

Copy link
Copy Markdown
Member

Issue #, if available

Fixes #3820

Description of changes

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:

code_dict = intrinsics_resolver.resolve_parameter_refs(code_dict)

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, and no new AWS::Lambda::Version was created.

This change resolves template parameter references on the AllProperties dict 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 rewrite Fn::Sub strings 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.

⚠️ Potential breaking change — one new Lambda version on first deploy after upgrade

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:

Template shape (AutoPublishAliasAllProperties: true) Before After Changes?
Property references a template parameter (!Ref MyParam) FVersion63442c9615 FVersioncf1b5d3b18 Yes
Literal values only FVersiona6597ab2da FVersiona6597ab2da No
Fn::Sub with pseudo params (${AWS::Partition}) FVersionb48d397249 FVersionb48d397249 No
!Ref to another resource (not a parameter) FVersion7e8c27dae7 FVersion7e8c27dae7 No

(Measured by transforming the same template with the same parameter values on develop vs this branch.)

Scope: only templates that both set AutoPublishAliasAllProperties: true and 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::Version logical 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:

  • One extra version per affected function, once. Lambda's per-region code storage quota counts retained versions, so customers near that limit should know.
  • If the alias is under a DeploymentPreference (canary/linear), that deploy will run a real traffic shift rather than a no-op.
  • After that single deploy, ids are stable again for unchanged templates.

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}-style Fn::Sub, which is a far larger population and is what an earlier iteration of this patch did (caught by function_with_alias_and_all_properties_property).

Worth a note in the release notes.

Description of how you validated changes

  • New regression tests (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.
  • Full tests/translator suite passes (2229 tests) with no golden-file version id changes — confirming backward compatibility. This specifically includes function_with_alias_and_all_properties_property, a fixture that uses Fn::Sub with ${AWS::Partition} etc., which a naive fix (resolving all parameters including pseudo) regressed. The pseudo-parameter exclusion is what keeps that fixture stable.
  • ruff and black clean 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.

…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.
@roger-zhangg
roger-zhangg requested a review from a team as a code owner July 31, 2026 23:51

@aws-sam-tooling-bot aws-sam-tooling-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Results

Reviewed: f65ff54..f4f081b
Files: 2
Comments: 1

Comment thread samtranslator/model/sam_resources.py Outdated
… 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.
@valerena

valerena commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

An Alternative to avoid the breaking change is to add a new parameter to track this behavior. Which could be something like AutoPublishAliasIncludeParameters or similar. It might be more annoying to use, but it should avoid changing the behavior in case someone even relies on this existing behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Lambda with AutoPublishAlias and AutoPublishAliasAllProperties does not create a new version when the referenced Parameter value changes

2 participants