Repository: aws-cloudformation/cfn-lint

Issue:
cfn-lint cannot check condition when checking resource type is supported or not
### CloudFormation Lint Version

0.76.1

### What operating system are you using?

docker

### Describe the bug

I am using cfn-lint to check my cloudformation template. I have following logic:
Check if the region is not cn-north-1 or cn-northwest-1,  it will create AWS::GlobalAccelerator::Accelerator resource, otherwise, it will not create AWS::GlobalAccelerator::Accelerator resource.

But now I got following error when we are running cfn-lint to check my cloudformation template:

E3001 Invalid or unsupported Type AWS::GlobalAccelerator::Accelerator for resource Accelerator in cn-northwest-1
cdk.out/xxx.template.json:1416:4

I guess this is a bug, could you please help to fix this error?

Thanks,
Mingtong


### Expected behavior

I expect my template can be validated successfully.

### Reproduction template

Conditions:
  acceleratorEnableCondition:
    Fn::Not:
    - Fn::Or:
      - Fn::Equals:
        - Ref: AWS::Region
        - cn-north-1
      - Fn::Equals:
        - Ref: AWS::Region
        - cn-northwest-1
 MyAccelerator:
    Type: AWS::GlobalAccelerator::Accelerator
    Properties:
      Name: myAccelerator
      Enabled: true
    Condition: acceleratorEnableCondition


Discussion:
Your problem seem to be checking `AWS::GlobalAccelerator::Accelerator` against a `cn-` region that doesn't support it. You can override this by validating the template against a region with most features going GA, e.g. `us-east-1` or `ap-northeast-1`, instead of your own AWS region.

```
cfn-lint -r REGION ANOTHER_REGION .... -t template.yml
```

My opinion to cfn-lint is designed as a static type checker against specifications, which may not handle dynamic values as robustly as static values.
In your case requires evaluating the condition with `AWS::Region = cn-north-1` to know `AcceleratorEnableCondition = False` to skip the accelerator block, which cannot generally be done.

Consider this, there is no general solution to know if IsProduction is true / false without further context.
```
Condition:
  IsProduction: !Equals
    - NodeEnv
    - "production"
Parameter:
  NodeEnv:
    Type: String
Resource:
  Accel:
    Condition: IsProduction
    Type: AWS::GlobalAccelerator::Accelerator
```

We have methodologies to solve some of this as we use logic to compare condition scenarios to evaluate the results of the template.  (SAT solvers using sympy).  That logic needs to be expanded to this scenario. This is a pretty specific scenario in which the resources Condition is based on a condition that does and Equals against !Ref AWS::Region. We should actually be able to use the `--regions` parameter to determine if the resource is available in a certain region.

Resolving patch:
diff --git a/src/cfnlint/conditions/conditions.py b/src/cfnlint/conditions/conditions.py
--- a/src/cfnlint/conditions/conditions.py
+++ b/src/cfnlint/conditions/conditions.py
@@ -5,7 +5,7 @@
 import itertools
 import logging
 import traceback
-from typing import Any, Dict, Iterator, List, Tuple
+from typing import Any, Dict, Generator, Iterator, List, Tuple
 
 from sympy import And, Implies, Not, Symbol
 from sympy.assumptions.cnf import EncodedCNF
@@ -162,12 +162,6 @@ def build_scenarios(self, condition_names: List[str]) -> Iterator[Dict[str, bool
         if len(condition_names) == 0:
             return
 
-        # if only one condition we will assume its True/False
-        # if len(condition_names) == 1:
-        #    yield {condition_names[0]: True}
-        #    yield {condition_names[0]: False}
-        #    return
-
         try:
             # build a large matric of True/False options based on the provided conditions
             scenarios_returned = 0
@@ -255,3 +249,41 @@ def check_implies(self, scenarios: Dict[str, bool], implies: str) -> bool:
             # KeyError is because the listed condition doesn't exist because of bad
             #  formatting or just the wrong condition name
             return True
+
+    def build_scenerios_on_region(
+        self, condition_name: str, region: str
+    ) -> Generator[bool, None, None]:
+        """Based on a region validate if the condition_name coudle be true
+
+        Args:
+            condition_name (str): The name of the condition we are validating against
+            region (str): the name of the region
+
+        Returns:
+            Generator[bool]: Returns True, False, or True and False depending on if the
+               condition could be True, False or both based on the region parameter
+        """
+        if not isinstance(condition_name, str):
+            return
+        cnf_region = self._cnf.copy()
+        for eql in self._conditions[condition_name].equals:
+            is_region, equal_region = eql.is_region
+            if is_region:
+                if equal_region == region:
+                    cnf_region.add_prop(And(self._solver_params[eql.hash]))
+                else:
+                    cnf_region.add_prop(Not(self._solver_params[eql.hash]))
+
+        cnf_test = cnf_region.copy()
+        cnf_test.add_prop(
+            self._conditions[condition_name].build_true_cnf(self._solver_params)
+        )
+        if satisfiable(cnf_test):
+            yield True
+
+        cnf_test = cnf_region.copy()
+        cnf_test.add_prop(
+            self._conditions[condition_name].build_false_cnf(self._solver_params)
+        )
+        if satisfiable(cnf_test):
+            yield False
diff --git a/src/cfnlint/conditions/equals.py b/src/cfnlint/conditions/equals.py
--- a/src/cfnlint/conditions/equals.py
+++ b/src/cfnlint/conditions/equals.py
@@ -4,11 +4,12 @@
 """
 import json
 import logging
-from typing import Any, Dict, List, Union
+from typing import Any, Dict, List, Tuple, Union
 
 from cfnlint.conditions._utils import get_hash
 
 LOGGER = logging.getLogger(__name__)
+REF_REGION = get_hash({"Ref": "AWS::Region"})
 
 
 class EqualParameter:
@@ -27,6 +28,7 @@ class Equal:
     _left: Union[EqualParameter, str]
     _right: Union[EqualParameter, str]
     _is_static: Union[bool, None]
+    _is_region: Tuple[bool, str]
 
     def __init__(self, equal: List[Union[str, dict]]) -> None:
         self._is_static = None
@@ -45,6 +47,15 @@ def __init__(self, equal: List[Union[str, dict]]) -> None:
                 self._right, EqualParameter
             ):
                 self._is_static = self._left == self._right
+
+            self._is_region = (False, "")
+            if isinstance(self._left, EqualParameter):
+                if self._left.hash == REF_REGION and isinstance(self._right, str):
+                    self._is_region = (True, self._right)
+            if isinstance(self._right, EqualParameter):
+                if self._right.hash == REF_REGION and isinstance(self._left, str):
+                    self._is_region = (True, self._left)
+
             return
         raise ValueError("Equals has to be a list of two values")
 
@@ -85,6 +96,19 @@ def parameters(self) -> List[EqualParameter]:
             params.append(self._right)
         return params
 
+    @property
+    def is_region(self) -> Tuple[bool, str]:
+        """Returns a Tuple if the condition is comparing a region to a string
+
+        Args: None
+
+        Returns:
+            Tuple[bool, str]: Tuple where the boolean is True if the condition
+              is using Ref: AWS::Region and the second element is for the region
+              being compared
+        """
+        return self._is_region
+
     @property
     def left(self):
         return self._left
diff --git a/src/cfnlint/rules/resources/Configuration.py b/src/cfnlint/rules/resources/Configuration.py
--- a/src/cfnlint/rules/resources/Configuration.py
+++ b/src/cfnlint/rules/resources/Configuration.py
@@ -5,6 +5,7 @@
 import cfnlint.helpers
 from cfnlint.helpers import REGISTRY_SCHEMAS
 from cfnlint.rules import CloudFormationLintRule, RuleMatch
+from cfnlint.template import Template
 
 
 class Configuration(CloudFormationLintRule):
@@ -18,7 +19,7 @@ class Configuration(CloudFormationLintRule):
     source_url = "https://github.com/aws-cloudformation/cfn-python-lint"
     tags = ["resources"]
 
-    def _check_resource(self, cfn, resource_name, resource_values):
+    def _check_resource(self, cfn: Template, resource_name, resource_values):
         """Check Resource"""
 
         valid_attributes = [
@@ -110,6 +111,11 @@ def _check_resource(self, cfn, resource_name, resource_values):
             self.logger.debug("Check resource types by region...")
             for region, specs in cfnlint.helpers.RESOURCE_SPECS.items():
                 if region in cfn.regions:
+                    if condition:
+                        if False in cfn.conditions.build_scenerios_on_region(
+                            condition, region
+                        ):
+                            continue
                     if resource_type not in specs[
                         "ResourceTypes"
                     ] and resource_type not in [


Test patch: