@@ -488,10 +488,14 @@ def process_references(self):
488488 In the parent you can !GetAtt ModuleName.OutputName
489489 This will be converted so that it's correct in the packaged template.
490490
491+ The parent can also refer to module Properties as a convenience,
492+ so !GetAtt ModuleName.PropertyName will simply copy the
493+ configured property value.
494+
491495 Recurse over all sections in the parent template looking for
492496 GetAtts and Subs that reference a module References value.
493497 """
494- sections = [RESOURCES , REFERENCES , MODULES ]
498+ sections = [RESOURCES , REFERENCES , MODULES , OUTPUTS ]
495499 for section in sections :
496500 if section not in self .template :
497501 continue
@@ -502,8 +506,7 @@ def resolve_references(self, k, v, d, n):
502506 """
503507 Recursively resolve GetAtts and Subs that reference module references.
504508
505- :param name The name of the output
506- :param output The output dict
509+ :param name The name of the reference
507510 :param k The name of the node
508511 :param v The value of the node
509512 :param d The dict that holds the parent of k
@@ -512,9 +515,9 @@ def resolve_references(self, k, v, d, n):
512515 If a reference is found, this function sets the value of d[n]
513516 """
514517 if k == SUB :
515- self .resolve_output_sub (v , d , n )
518+ self .resolve_reference_sub (v , d , n )
516519 elif k == GETATT :
517- self .resolve_output_getatt (v , d , n )
520+ self .resolve_reference_getatt (v , d , n )
518521 else :
519522 if isdict (v ):
520523 for k2 , v2 in v .copy ().items ():
@@ -527,8 +530,72 @@ def resolve_references(self, k, v, d, n):
527530 for k3 , v3 in v2 .copy ().items ():
528531 self .resolve_references (k3 , v3 , v , idx )
529532
530- def resolve_output_sub (self , v , d , n ):
531- "Resolve a Sub that refers to a module output"
533+ def resolve_reference_sub_getatt (self , w ):
534+ """
535+ Resolve a reference to a module in a Sub GetAtt word.
536+
537+ For example, the parent has
538+
539+ Modules:
540+ A:
541+ Properties:
542+ Name: foo
543+ Outputs:
544+ B:
545+ Value: !Sub ${A.MyName}
546+ C:
547+ Value: !Sub ${A.Name}
548+
549+ The module has:
550+
551+ Inputs:
552+ Name:
553+ Type: String
554+ Resources:
555+ X:
556+ Properties:
557+ Y: !Ref Name
558+ References:
559+ MyName: !GetAtt Y.Name
560+
561+ The resulting output:
562+ B: !Sub ${AX.Name}
563+ C: foo
564+
565+ """
566+
567+ resolved = "${" + w + "}"
568+ tokens = w .split ("." , 1 )
569+ if len (tokens ) < 2 :
570+ msg = f"GetAtt { w } has unexpected number of tokens"
571+ raise exceptions .InvalidModuleError (msg = msg )
572+
573+ # !Sub ${Content.BucketArn} -> !Sub ${ContentBucket.Arn}
574+
575+ r = None
576+ if tokens [0 ] == self .name :
577+ if tokens [1 ] in self .references :
578+ # We're referring to the module's References
579+ r = self .references [tokens [1 ]]
580+ elif tokens [1 ] in self .props :
581+ # We're referring to parent Module Properties
582+ r = self .props [tokens [1 ]]
583+ if r is not None :
584+ if GETATT in r :
585+ getatt = r [GETATT ]
586+ resolved = "${" + self .name + "." .join (getatt ) + "}"
587+ elif SUB in r :
588+ resolved = "${" + self .name + r [SUB ] + "}"
589+ elif REF in r :
590+ resolved = "${" + self .name + r [REF ] + "}"
591+ else :
592+ # Handle scalar properties
593+ resolved = r
594+
595+ return resolved
596+
597+ def resolve_reference_sub (self , v , d , n ):
598+ "Resolve a Sub that refers to a module reference or property"
532599 words = parse_sub (v , True )
533600 sub = ""
534601 for word in words :
@@ -537,33 +604,21 @@ def resolve_output_sub(self, v, d, n):
537604 elif word .t == WordType .AWS :
538605 sub += "${AWS::" + word .w + "}"
539606 elif word .t == WordType .REF :
540- # A reference to an output has to be a getatt
607+ # A ref to a module reference has to be a getatt
541608 resolved = "${" + word .w + "}"
542609 sub += resolved
543610 elif word .t == WordType .GETATT :
544- resolved = "${" + word .w + "}"
545- tokens = word .w .split ("." , 1 )
546- if len (tokens ) < 2 :
547- msg = f"GetAtt { word .w } has unexpected number of tokens"
548- raise exceptions .InvalidModuleError (msg = msg )
549- # !Sub ${Content.BucketArn} -> !Sub ${ContentBucket.Arn}
550- if tokens [0 ] == self .name and tokens [1 ] in self .references :
551- output = self .references [tokens [1 ]]
552- if GETATT in output :
553- getatt = output [GETATT ]
554- resolved = "${" + self .name + "." .join (getatt ) + "}"
555- elif SUB in output :
556- resolved = "${" + self .name + output [SUB ] + "}"
557- elif REF in output :
558- resolved = "${" + self .name + output [REF ] + "}"
559- sub += resolved
611+ sub += self .resolve_reference_sub_getatt (word .w )
560612
561- d [n ] = {SUB : sub }
613+ if is_sub_needed (sub ):
614+ d [n ] = {SUB : sub }
615+ else :
616+ d [n ] = sub
562617
563618 # pylint:disable=too-many-branches
564- def resolve_output_getatt (self , v , d , n ):
619+ def resolve_reference_getatt (self , v , d , n ):
565620 """
566- Resolve a GetAtt that refers to a module output .
621+ Resolve a GetAtt that refers to a module Reference .
567622
568623 :param v The value
569624 :param d The dictionary
@@ -575,20 +630,25 @@ def resolve_output_getatt(self, v, d, n):
575630 msg = f"GetAtt { v } invalid"
576631 raise exceptions .InvalidModuleError (msg = msg )
577632
578- if v [0 ] == self .name and v [1 ] in self .references :
579- output = self .references [v [1 ]]
580- if REF in output :
581- ref = output [REF ]
633+ r = None
634+ if v [0 ] == self .name :
635+ if v [1 ] in self .references :
636+ r = self .references [v [1 ]]
637+ elif v [1 ] in self .props :
638+ r = self .props [v [1 ]]
639+ if r is not None :
640+ if REF in r :
641+ ref = r [REF ]
582642 d [n ] = {REF : self .name + ref }
583- elif GETATT in output :
584- getatt = output [GETATT ]
643+ elif GETATT in r :
644+ getatt = r [GETATT ]
585645 if len (getatt ) < 2 :
586646 msg = f"GetAtt { getatt } in Output { v [1 ]} is invalid"
587647 raise exceptions .InvalidModuleError (msg = msg )
588648 d [n ] = {GETATT : [self .name + getatt [0 ], getatt [1 ]]}
589- elif SUB in output :
590- # Parse the Sub in the module output
591- words = parse_sub (output [SUB ], True )
649+ elif SUB in r :
650+ # Parse the Sub in the module reference
651+ words = parse_sub (r [SUB ], True )
592652 sub = ""
593653 for word in words :
594654 if word .t == WordType .STR :
@@ -612,8 +672,10 @@ def resolve_output_getatt(self, v, d, n):
612672 if tokens [0 ] in self .resources :
613673 resolved = "${" + self .name + word .w + "}"
614674 sub += resolved
615-
616675 d [n ] = {SUB : sub }
676+ else :
677+ # Handle scalars in Properties
678+ d [n ] = r
617679
618680 def validate_overrides (self ):
619681 "Make sure resources referenced by overrides actually exist"
0 commit comments