Repository: aws-cloudformation/cfn-lint

Issue:
Feature Request: !Sub equivalent of W1001
*cfn-lint version: 0.58.2*

I believe that `!Sub` parameters should be checked to see if they depend on conditional resources the same way W1001 checks this for `!Ref` (e.g. `SubCondParam.Value` should produce a warning). I suspect this is possible since E1019 checks for non-existent parameters within a `!Sub`.

```
---
AWSTemplateFormatVersion: 2010-09-09

Conditions:
  CreateContainerRepository: !Equals ["yes", "no"]

Resources:
  Repository:
    Type: AWS::ECR::Repository
    Condition: CreateContainerRepository

  SubCondParam:
    Type: AWS::SSM::Parameter
    Properties:
      Type: String
      Value: !Sub ${Repository}

  RefCondParam:
    Type: AWS::SSM::Parameter
    Properties:
      Type: String
      Value: !Ref Repository

  SubFakeParam:
    Type: AWS::SSM::Parameter
    Properties:
      Type: String
      Value: !Sub ${Fake}

  RefFakeParam:
    Type: AWS::SSM::Parameter
    Properties:
      Type: String
      Value: !Ref Fake
```

* SubCondParam.Value shows no error or warning, I believe it should show a warning
* RefCondParam.Value shows W1001
* SubFakeParam.Value shows E1019
* RefFakeParam.Value shows E1012


Discussion:

Resolving patch:
diff --git a/src/cfnlint/rules/functions/RelationshipConditions.py b/src/cfnlint/rules/functions/RelationshipConditions.py
--- a/src/cfnlint/rules/functions/RelationshipConditions.py
+++ b/src/cfnlint/rules/functions/RelationshipConditions.py
@@ -17,7 +17,7 @@ class RelationshipConditions(CloudFormationLintRule):
         "condition."
     )
     source_url = "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-ref.html"
-    tags = ["conditions", "resources", "relationships", "ref", "getatt"]
+    tags = ["conditions", "resources", "relationships", "ref", "getatt", "sub"]
 
     def match(self, cfn):
         """Check CloudFormation Ref/GetAtt for Conditions"""
@@ -83,4 +83,46 @@ def match(self, cfn):
                             )
                         )
 
+        # The do Sub
+        sub_objs = cfn.search_deep_keys(searchText="Fn::Sub", includeGlobals=False)
+        for sub_obj in sub_objs:
+            sub_string = sub_obj[-1]
+            # Filter out bad types of sub_strings.
+            # Lists have two be two items and it can be just a string
+            if not isinstance(sub_string, (list, str)):
+                continue
+            if isinstance(sub_string, str):
+                sub_string = [sub_string, {}]
+            if len(sub_string) != 2:
+                continue
+            sub_params = sub_string[1]
+            string_params = cfn.get_sub_parameters(sub_string[0])
+
+            for string_param in string_params:
+                if string_param not in sub_params:
+                    # deal with GetAtts by dropping everything after the .
+                    string_param = string_param.split(".")[0]
+                    if string_param in cfn.template.get("Resources", {}):
+                        scenarios = cfn.is_resource_available(
+                            sub_obj[:-1], string_param
+                        )
+                        for scenario in scenarios:
+                            scenario_text = " and ".join(
+                                [
+                                    f'when condition "{k}" is {v}'
+                                    for (k, v) in scenario.items()
+                                ]
+                            )
+                            message = 'Fn::Sub to resource "{0}" that may not be available {1} at {2}'
+                            matches.append(
+                                RuleMatch(
+                                    sub_obj[:-1],
+                                    message.format(
+                                        string_param,
+                                        scenario_text,
+                                        "/".join(map(str, sub_obj[:-1])),
+                                    ),
+                                )
+                            )
+
         return matches


Test patch:
diff --git a/test/fixtures/templates/bad/functions/relationship_conditions.yaml b/test/fixtures/templates/bad/functions/relationship_conditions.yaml
--- a/test/fixtures/templates/bad/functions/relationship_conditions.yaml
+++ b/test/fixtures/templates/bad/functions/relationship_conditions.yaml
@@ -45,6 +45,19 @@ Resources:
       Timeout: 25
       TracingConfig:
         Mode: Active
+
+  SubCondRefParam:
+    Type: AWS::SSM::Parameter
+    Properties:
+      Type: String
+      Value: !Sub ${LambdaExecutionRole}
+
+  SubCondGetAttParam:
+    Type: AWS::SSM::Parameter
+    Properties:
+      Type: String
+      Value: !Sub ${LambdaExecutionRole.Arn}
+
 Outputs:
   lambdaArn:
     Value: !GetAtt LambdaExecutionRole.Arn
diff --git a/test/unit/rules/functions/test_relationship_conditions.py b/test/unit/rules/functions/test_relationship_conditions.py
--- a/test/unit/rules/functions/test_relationship_conditions.py
+++ b/test/unit/rules/functions/test_relationship_conditions.py
@@ -28,5 +28,5 @@ def test_file_positive(self):
     def test_file_negative(self):
         """Test failure"""
         self.helper_file_negative(
-            "test/fixtures/templates/bad/functions/relationship_conditions.yaml", 3
+            "test/fixtures/templates/bad/functions/relationship_conditions.yaml", 5
         )