Thanks to visit codestin.com
Credit goes to github.com

Skip to content

CloudFormation v2 Engine: Base Support for Fn::GetAZs #12699

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 21 commits into from
Jun 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@ def __init__(self, scope: Scope, value: Any):
FnTransform: Final[str] = "Fn::Transform"
FnSelect: Final[str] = "Fn::Select"
FnSplit: Final[str] = "Fn::Split"
FnGetAZs: Final[str] = "Fn::GetAZs"
INTRINSIC_FUNCTIONS: Final[set[str]] = {
RefKey,
FnIfKey,
Expand All @@ -437,6 +438,7 @@ def __init__(self, scope: Scope, value: Any):
FnTransform,
FnSelect,
FnSplit,
FnGetAZs,
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
import re
from typing import Any, Final, Generic, Optional, TypeVar

from botocore.exceptions import ClientError

from localstack.aws.api.ec2 import AvailabilityZoneList, DescribeAvailabilityZonesResult
from localstack.aws.connect import connect_to
from localstack.services.cloudformation.engine.transformers import (
Transformer,
execute_macro,
Expand Down Expand Up @@ -677,7 +681,7 @@ def _compute_fn_select(args: list[Any]) -> Any:

def visit_node_intrinsic_function_fn_split(
self, node_intrinsic_function: NodeIntrinsicFunction
) -> PreprocEntityDelta:
):
# TODO: add further support for schema validation
arguments_delta = self.visit(node_intrinsic_function.arguments)
arguments_before = arguments_delta.before
Expand All @@ -703,6 +707,47 @@ def _compute_fn_split(args: list[Any]) -> Any:

return PreprocEntityDelta(before=before, after=after)

def visit_node_intrinsic_function_fn_get_a_zs(
self, node_intrinsic_function: NodeIntrinsicFunction
) -> PreprocEntityDelta:
# TODO: add further support for schema validation
arguments_delta = self.visit(node_intrinsic_function.arguments)
arguments_before = arguments_delta.before
arguments_after = arguments_delta.after

def _compute_fn_get_a_zs(region) -> Any:
if not isinstance(region, str):
raise RuntimeError(f"Invalid region value for Fn::GetAZs: '{region}'")

if not region:
region = self._change_set.region_name

account_id = self._change_set.account_id
ec2_client = connect_to(aws_access_key_id=account_id, region_name=region).ec2
try:
describe_availability_zones_result: DescribeAvailabilityZonesResult = (
ec2_client.describe_availability_zones()
)
except ClientError:
raise RuntimeError(
"Could not describe zones availability whilst evaluating Fn::GetAZs"
)
availability_zones: AvailabilityZoneList = describe_availability_zones_result[
"AvailabilityZones"
]
azs = [az["ZoneName"] for az in availability_zones]
return azs

before = Nothing
if not is_nothing(arguments_before):
before = _compute_fn_get_a_zs(arguments_before)

after = Nothing
if not is_nothing(arguments_after):
after = _compute_fn_get_a_zs(arguments_after)

return PreprocEntityDelta(before=before, after=after)

def visit_node_intrinsic_function_fn_find_in_map(
self, node_intrinsic_function: NodeIntrinsicFunction
) -> PreprocEntityDelta:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@ def visit_node_intrinsic_function_fn_split(
):
self.visit_children(node_intrinsic_function)

def visit_node_intrinsic_function_fn_get_a_zs(
self, node_intrinsic_function: NodeIntrinsicFunction
):
self.visit_children(node_intrinsic_function)

def visit_node_intrinsic_function_fn_sub(self, node_intrinsic_function: NodeIntrinsicFunction):
self.visit_children(node_intrinsic_function)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def test_simple_route_table_creation(deploy_cfn_template, aws_client, snapshot):
# ec2.describe_route_tables(RouteTableIds=[route_table_id])


@pytest.mark.skip(reason="CFNV2:Fn::Select, CFNV2:Fn::GatAZs")
@pytest.mark.skip(reason="CFNV2:Other")
@markers.aws.validated
def test_vpc_creates_default_sg(deploy_cfn_template, aws_client):
result = deploy_cfn_template(
Expand Down Expand Up @@ -109,7 +109,6 @@ def test_cfn_with_multiple_route_tables(deploy_cfn_template, aws_client):
assert len(resp["RouteTables"]) == 4


@pytest.mark.skip(reason="CFNV2:Fn::Select, CFNV2:Fn::GatAZs")
Copy link
Contributor

Choose a reason for hiding this comment

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

🎉

@markers.aws.validated
@markers.snapshot.skip_snapshot_verify(
paths=["$..PropagatingVgws", "$..Tags", "$..Tags..Key", "$..Tags..Value"]
Expand Down Expand Up @@ -165,7 +164,6 @@ def test_dhcp_options(aws_client, deploy_cfn_template, snapshot):
snapshot.match("description", response["DhcpOptions"][0])


@pytest.mark.skip(reason="CFNV2:Fn::Select, CFNV2:Fn::GatAZs")
Copy link
Contributor

Choose a reason for hiding this comment

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

🎉

@markers.aws.validated
@markers.snapshot.skip_snapshot_verify(
paths=[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,6 @@ def test_cidr_function(self, deploy_cfn_template):

assert deployed.outputs["Address"] == "10.0.0.0/24"

@pytest.mark.skip(reason="CFNV2:Fn::GetAZs")
Copy link
Contributor

Choose a reason for hiding this comment

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

🎉

@pytest.mark.parametrize(
"region",
[
Expand Down
Loading