Skip to content

Conversation

@madsid
Copy link

@madsid madsid commented Nov 10, 2025

Issue #, if available

#3736

Description of changes

Support AWS::NoValue conditional for Role property in AWS::Serverless::Function

Role is an optional string value for AWS::Serverless::Function. An execution role is auto generated code when the value is None. This change adds support for AWS::NoValue.

Use cases :

  1. Role string passed though as is.
  2. Role If conditional with both AWS::NoValue creates an execution role for both true and false case and replaces AWS::NoValue
  3. Role if conditional with 1 AWS::NoValue creates an execution role and replaces AWS::NoValue
  4. Role value with Fn::GetAtt gets passed through as is.

Description of how you validated changes

  • Added unit tests
  • Hand tested sam-translate.py with templates and verified translated templates

Checklist

Examples?

Please reach out in the comments if you want to add an example. Examples will be
added to sam init through aws/aws-sam-cli-app-templates.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@madsid madsid requested a review from a team as a code owner November 10, 2025 22:50
lambda_function.Role = execution_role.get_runtime_attr("arn")

if is_intrinsic_if(lambda_role):
resources.append(execution_role)
Copy link
Contributor

Choose a reason for hiding this comment

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

This is not exactly correct here. In theory we need to create this role only if one of the values is AWS::NoValue.

If the Role field has an If with two different roles, then we shouldn't create the new one. (so, move this resources.append to inside a new condition "if 1 or 2 are intrinsic_no_value"

valerena
valerena previously approved these changes Dec 12, 2025

cfn_resources = self.function.to_cloudformation(**self.kwargs)
generated_roles = [x for x in cfn_resources if isinstance(x, IAMRole)]
lambda_function = next(r for r in cfn_resources if r.resource_type == "AWS::Lambda::Function")
Copy link
Contributor

Choose a reason for hiding this comment

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

You can do the same than for the roles with the functions: generated_functions = [x for x in cfnResources if isinstance(x, LambdaFunction)]
and then you can also confirm that there's only one created.

But it's not a big deal. We can probably change it in the future if needed.

Comment on lines 788 to 790
template = {"Conditions": {"Condition": True}}
kwargs = dict(self.kwargs)
kwargs["conditions"] = template.get("Conditions", {})
Copy link
Contributor

Choose a reason for hiding this comment

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

You don't use the variable template anywhere else, so you could just remove it and define the conditions below. (same in other tests)

Suggested change
template = {"Conditions": {"Condition": True}}
kwargs = dict(self.kwargs)
kwargs["conditions"] = template.get("Conditions", {})
kwargs = dict(self.kwargs)
kwargs["conditions"] = {"Condition": True}

self.function.CodeUri = "s3://foobar/foo.zip"
self.function.Runtime = "foo"
self.function.Handler = "bar"
self.template = {"Conditions": {}}
Copy link
Contributor

Choose a reason for hiding this comment

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

You don't need this self.template variable since it's not being used. Right below you use it to get Conditions from here, but it will be just {}, so you can just do that ("conditions": {})

@valerena valerena changed the title Adding AWS::NoValue support to AWS::Serverless::Function IAM Role feat: support conditional AWS::NoValue in SAM Function's IAM Role Dec 12, 2025
Comment on lines 425 to 429
if lambda_role is None:
resources.append(execution_role)
lambda_function.Role = execution_role.get_runtime_attr("arn")

elif is_intrinsic_if(lambda_role):
Copy link
Member

Choose a reason for hiding this comment

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

you can avoid a indentation by early return

Suggested change
if lambda_role is None:
resources.append(execution_role)
lambda_function.Role = execution_role.get_runtime_attr("arn")
elif is_intrinsic_if(lambda_role):
if lambda_role is None:
resources.append(execution_role)
lambda_function.Role = execution_role.get_runtime_attr("arn")
return
if not is_intrinsic_if(lambda_role):
return


def _make_lambda_role(
self,
lambda_function: LambdaFunction,
Copy link
Member

Choose a reason for hiding this comment

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

this implementation has some unnecessary change IMO, we can keep the if lambda_function.Role is None part as is, then _make_lambda_role can return the created role, so we don't need to pass in lambda_function in this function.

Copy link
Member

@roger-zhangg roger-zhangg Dec 12, 2025

Choose a reason for hiding this comment

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

One other concern is we are modifying values for lambda_function, execution_role, resources, conditions ( which are the function's input) inside this function which returns None, but all these values might have changed after running this function. This might make this part of the code harder to review in the future. If we need to change all these variable's value in place, I would perfer to implement this directly in to_cloudformation instead of this subfunction. If you really prefer to have this subfunction. Maybe we can return the new value instead of modifying them inplace. So future reviewers could easily understand these variables are modified in this subfunction.

Comment on lines +404 to +411
if role_changes["should_append_role"]:
resources.append(execution_role)

if role_changes["lambda_role_value"] is not None:
lambda_function.Role = role_changes["lambda_role_value"]

if role_changes["execution_role_condition"] is not None:
execution_role.set_resource_attribute("Condition", role_changes["execution_role_condition"])
Copy link
Contributor

Choose a reason for hiding this comment

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

You can change this a little bit so you return a role_changes["execution_role_resource"] that it's either execution_role or None. And you set the Condition attribute inside the function not here outside.

so then you have

if role_changes["execution_role_resource"] is not None:
    resources.append(role_changes["execution_role_resource"])

and that role comes ready to be added, you can change its resource_attribute("Condition, inside the function instead of here.

Comment on lines 451 to 456
result = {
"should_append_role": False,
"lambda_role_value": None,
"execution_role_condition": None,
"new_condition": None,
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Another recommendation. Work with the simple variables first. And them to the result only at the very end for returning. Using the result variable for all the following manipulations makes the code look so much worse.

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.

4 participants