55import pulumi_aws as aws
66import pulumi_awsx as awsx
77
8- from pprint import pprint
8+
9+ def _find_endpoint_for_az (az : str , statuses ) -> str :
10+ for sync_state in statuses [0 ]["sync_states" ]:
11+ if sync_state ["availability_zone" ] == az :
12+ return sync_state ["attachments" ][0 ]["endpoint_id" ]
13+ raise Exception (f"No firewall endpoint found for AZ '{ az } '" )
914
1015
1116@dataclass
@@ -25,7 +30,6 @@ def __init__(
2530 ) -> None :
2631 super ().__init__ ("awsAdvancedNetworking:index:InspectionVpc" , name , None , opts )
2732
28- # So we can reference later in our apply handler:
2933 self .name = name
3034 self .args = args
3135
@@ -51,6 +55,7 @@ def __init__(
5155 nat_gateways = awsx .ec2 .NatGatewayConfigurationArgs (
5256 strategy = awsx .ec2 .NatGatewayStrategy .NONE
5357 ),
58+ subnet_strategy = awsx .ec2 .SubnetAllocationStrategy .AUTO ,
5459 ),
5560 opts = pulumi .ResourceOptions (
5661 * (opts or {}),
@@ -124,17 +129,35 @@ def __init__(
124129 pulumi .ResourceOptions (parent = self ),
125130 )
126131
132+ # We know that there are 3 AZs in our VPC because that is the default
133+ # for the AWSX VPC component and we don't specify an argument and we
134+ # also do not allow the input to be configurable in this component.
135+ #
136+ # Because we know there are 3 AZs, we can extract the 3 subnet IDs into
137+ # a list of known length, which in turn allows us to avoid creating
138+ # resources in an apply(), which is bad practice because the pulumi
139+ # preview output would not necessarily match the pulumi up behavior.
140+ #
141+ # If we did not know the length of the list in advance, we would have to
142+ # call apply on the list of unknown length and then loop through its
143+ # values to create the necessary routes.
144+ public_subnet_ids = [
145+ self .vpc .public_subnet_ids .apply (lambda x : x [0 ]),
146+ self .vpc .public_subnet_ids .apply (lambda x : x [1 ]),
147+ self .vpc .public_subnet_ids .apply (lambda x : x [2 ]),
148+ ]
149+
150+ isolated_subnet_ids = [
151+ self .vpc .isolated_subnet_ids .apply (lambda x : x [0 ]),
152+ self .vpc .isolated_subnet_ids .apply (lambda x : x [1 ]),
153+ self .vpc .isolated_subnet_ids .apply (lambda x : x [2 ]),
154+ ]
155+
127156 if args .firewall_policy_arn :
128157 self .create_firewall ()
129- pulumi .Output .all (
130- self .firewall .firewall_statuses ,
131- self .vpc .public_subnet_ids ,
132- self .vpc .isolated_subnet_ids ,
133- ).apply (lambda args : self .create_firewall_routes (args [0 ], args [1 ], args [2 ]))
158+ self .create_firewall_routes (public_subnet_ids , isolated_subnet_ids )
134159 else :
135- pulumi .Output .all (self .vpc .public_subnet_ids , self .vpc .isolated_subnet_ids ).apply (
136- lambda args : self .create_direct_nat_routes (args [0 ], args [1 ])
137- )
160+ self .create_direct_nat_routes (public_subnet_ids , isolated_subnet_ids )
138161
139162 self .register_outputs (
140163 {
@@ -145,16 +168,18 @@ def __init__(
145168 )
146169
147170 def create_direct_nat_routes (
148- self , public_subnet_ids : Sequence [str ], isolated_subnet_ids : Sequence [str ]
171+ self ,
172+ public_subnet_ids : Sequence [pulumi .Input [str ]],
173+ isolated_subnet_ids : Sequence [pulumi .Input [str ]],
149174 ):
150175 # Create routes for the supernet (a CIDR block that encompasses all
151176 # spoke VPCs) from the public subnets in the inspection VPC (where the
152177 # NAT Gateways for centralized egress live) to the TGW.
153- for subnet_id in public_subnet_ids :
154- route_table = aws .ec2 .get_route_table (subnet_id = subnet_id )
178+ for i , subnet_id in enumerate ( public_subnet_ids ) :
179+ route_table = aws .ec2 .get_route_table_output (subnet_id = subnet_id )
155180
156181 aws .ec2 .Route (
157- f"{ self .name } -route-{ subnet_id } -to-tgw" ,
182+ f"{ self .name } -public- route-{ i } -to-tgw" ,
158183 aws .ec2 .RouteArgs (
159184 route_table_id = route_table .id ,
160185 destination_cidr_block = self .args .supernet_cidr_block ,
@@ -168,11 +193,11 @@ def create_direct_nat_routes(
168193 )
169194
170195 # Create routes from the TGW subnet to the NAT Gateway.
171- for subnet_id in isolated_subnet_ids :
172- route_table = aws .ec2 .get_route_table (subnet_id = subnet_id )
196+ for i , subnet_id in enumerate ( isolated_subnet_ids ) :
197+ route_table = aws .ec2 .get_route_table_output (subnet_id = subnet_id )
173198
174199 aws .ec2 .Route (
175- f"{ self .name } -route-{ subnet_id } -to-tgw " ,
200+ f"{ self .name } -isolated- route-{ i } -to-nat " ,
176201 aws .ec2 .RouteArgs (
177202 route_table_id = route_table .id ,
178203 destination_cidr_block = "0.0.0.0/0" ,
@@ -273,40 +298,27 @@ def create_firewall(self):
273298 ),
274299 )
275300
276- def create_firewall_routes (self , statuses , public_subnet_ids , tgw_subnet_ids ):
277- # Map the output of the Firewall attachments to a structure more
278- # suitable structure:
279- attachments = []
280- for sync_state in statuses [0 ]["sync_states" ]:
281- attachment = {
282- "az" : sync_state ["availability_zone" ],
283- "subnet_id" : sync_state ["attachments" ][0 ]["subnet_id" ],
284- "endpoint_id" : sync_state ["attachments" ][0 ]["endpoint_id" ],
285- }
286- attachments .append (attachment )
287-
301+ def create_firewall_routes (
302+ self ,
303+ public_subnet_ids : Sequence [pulumi .Input [str ]],
304+ tgw_subnet_ids : Sequence [pulumi .Input [str ]],
305+ ):
288306 # Add routes from public subnets to the firewall for incoming packets.
289- for subnet_id in public_subnet_ids :
290- subnet = aws .ec2 .get_subnet (id = subnet_id )
291- route_table = aws .ec2 .get_route_table (subnet_id = subnet_id )
292-
293- # Find the attachment in our availability zone
294- subnet_attachments = [
295- attachment
296- for attachment in attachments
297- if attachment ["az" ] == subnet .availability_zone
298- ]
299- if len (subnet_attachments ) != 1 :
300- raise Exception (
301- f"Expected exactly 1 firewall subnet attachment for AZ '{ subnet .availability_zone } '. Found { len (subnet_attachments )} instead."
302- )
307+ for i , subnet_id in enumerate (public_subnet_ids ):
308+ subnet = aws .ec2 .get_subnet_output (id = subnet_id )
309+ route_table = aws .ec2 .get_route_table_output (subnet_id = subnet_id )
310+
311+ endpoint_id = pulumi .Output .all (
312+ subnet .availability_zone ,
313+ self .firewall .firewall_statuses ,
314+ ).apply (lambda args : _find_endpoint_for_az (args [0 ], args [1 ]))
303315
304316 aws .ec2 .Route (
305- f"{ self .name } -{ subnet_id } -to-firewall" ,
317+ f"{ self .name } -public- { i } -to-firewall" ,
306318 aws .ec2 .RouteArgs (
307319 route_table_id = route_table .id ,
308320 destination_cidr_block = self .args .supernet_cidr_block ,
309- vpc_endpoint_id = subnet_attachments [ 0 ][ " endpoint_id" ] ,
321+ vpc_endpoint_id = endpoint_id ,
310322 ),
311323 pulumi .ResourceOptions (
312324 parent = self ,
@@ -315,27 +327,21 @@ def create_firewall_routes(self, statuses, public_subnet_ids, tgw_subnet_ids):
315327 )
316328
317329 # Add routes from the TGW subnets to the firewall for outgoing packets.
318- for subnet_id in tgw_subnet_ids :
319- subnet = aws .ec2 .get_subnet (id = subnet_id )
320- route_table = aws .ec2 .get_route_table (subnet_id = subnet_id )
321-
322- # Find the attachment in our availability zone
323- subnet_attachments = [
324- attachment
325- for attachment in attachments
326- if attachment ["az" ] == subnet .availability_zone
327- ]
328- if len (subnet_attachments ) != 1 :
329- raise Exception (
330- f"Expected exactly 1 firewall subnet attachment for AZ '{ subnet .availability_zone } '. Found { len (subnet_attachments )} instead."
331- )
330+ for i , subnet_id in enumerate (tgw_subnet_ids ):
331+ subnet = aws .ec2 .get_subnet_output (id = subnet_id )
332+ route_table = aws .ec2 .get_route_table_output (subnet_id = subnet_id )
333+
334+ endpoint_id = pulumi .Output .all (
335+ subnet .availability_zone ,
336+ self .firewall .firewall_statuses ,
337+ ).apply (lambda args : _find_endpoint_for_az (args [0 ], args [1 ]))
332338
333339 aws .ec2 .Route (
334- f"{ self .name } -{ subnet_id } -to-firewall" ,
340+ f"{ self .name } -tgw- { i } -to-firewall" ,
335341 aws .ec2 .RouteArgs (
336342 route_table_id = route_table .id ,
337343 destination_cidr_block = "0.0.0.0/0" ,
338- vpc_endpoint_id = subnet_attachments [ 0 ][ " endpoint_id" ] ,
344+ vpc_endpoint_id = endpoint_id ,
339345 ),
340346 pulumi .ResourceOptions (
341347 parent = self ,
0 commit comments