Repository: aws-cloudformation/cfn-lint

Issue:
Fn::ForEach could not be resolved when values array is empty
### CloudFormation Lint Version

cfn-lint 1.17.1

### What operating system are you using?

Mac

### Describe the bug

Error when `Fn::ForEach` is used with an empty array (or more realistically with a map that [sometimes] resolves to an empty array)

```
$ cfn-lint foreach.yaml
E0001 Error transforming template: Fn::ForEach could not be resolved
foreach.yaml:6:7
```

Originally thought this was the same as #3747 but I don't think it is really related to `AccountId` resolution

### Expected behavior

cfn-lint passes

### Reproduction template

```yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::LanguageExtensions
Resources:
  'Fn::ForEach::Subscriptions':
    - Email
    - []
    - 'SubscriptionFor&{Email}':
        Type: AWS::SNS::Subscription
        Properties:
          TopicArn: "arn:aws:sns:us-east-1:12345678901:my-sns-topic"
          Protocol: email
          Endpoint: !Ref Email
```

Or slightly more realistically:
 
```yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::LanguageExtensions
Mappings:
  AccountMap:
    '012345678901':
      Emails: []
    '123456789012':
      Emails:
        - test@test.com
Resources:
  'Fn::ForEach::Subscriptions':
    - Email
    - !FindInMap [AccountMap, !Ref AWS::AccountId, Emails]
    - 'SubscriptionFor&{Email}':
        Type: AWS::SNS::Subscription
        Properties:
          TopicArn: "arn:aws:sns:us-east-1:012345678901:my-sns-topic"
          Protocol: email
          Endpoint: !Ref Email
```


Discussion:

Patch:
diff --git a/src/cfnlint/template/transforms/_language_extensions.py b/src/cfnlint/template/transforms/_language_extensions.py
index 6760be1b01..e1e6850228 100644
--- a/src/cfnlint/template/transforms/_language_extensions.py
+++ b/src/cfnlint/template/transforms/_language_extensions.py
@@ -370,15 +370,23 @@ def value(
             except _ResolveError:
                 try:
                     t_map[2].value(cfn, params)
+                    max_length = -1
                     for k, v in mapping.items():
                         if isinstance(v, dict):
                             if t_map[2].value(cfn, params, only_params) in v:
+                                if (
+                                    len(v[t_map[2].value(cfn, params, only_params)])
+                                    <= max_length
+                                ):
+                                    continue
                                 if isinstance(t_map[1], _ForEachValueRef):
                                     if t_map[1]._ref._value == "AWS::AccountId":
                                         global _ACCOUNT_ID
                                         _ACCOUNT_ID = k
                                 t_map[1] = _ForEachValue.create(k)
-                                break
+                                max_length = len(
+                                    v[t_map[2].value(cfn, params, only_params)]
+                                )
                 except _ResolveError:
                     pass
 
@@ -529,7 +537,7 @@ def values(
         if self._fn:
             try:
                 values = self._fn.value(cfn, {}, False)
-                if values:
+                if values is not None:
                     if isinstance(values, list):
                         for value in values:
                             if isinstance(value, (str, dict)):


Test patch:
diff --git a/test/unit/module/template/transforms/test_language_extensions.py b/test/unit/module/template/transforms/test_language_extensions.py
index 7741501121..a1829d72f8 100644
--- a/test/unit/module/template/transforms/test_language_extensions.py
+++ b/test/unit/module/template/transforms/test_language_extensions.py
@@ -971,3 +971,191 @@ def test_transform(self):
             self.result,
             template,
         )
+
+
+class TestTransformValueEmptyList(TestCase):
+    def setUp(self) -> None:
+
+        cfnlint.template.transforms._language_extensions._ACCOUNT_ID = None
+
+        self.template_obj = convert_dict(
+            {
+                "Transform": ["AWS::LanguageExtensions"],
+                "Mappings": {
+                    "Accounts": {
+                        "111111111111": {"AppName": []},
+                    },
+                },
+                "Resources": {
+                    "Fn::ForEach::Regions": [
+                        "AppName",
+                        {
+                            "Fn::FindInMap": [
+                                "Accounts",
+                                {"Ref": "AWS::AccountId"},
+                                "AppName",
+                            ]
+                        },
+                        {
+                            "${AppName}Role": {
+                                "Type": "AWS::IAM::Role",
+                                "Properties": {
+                                    "RoleName": {"Ref": "AppName"},
+                                    "AssumeRolePolicyDocument": {
+                                        "Version": "2012-10-17",
+                                        "Statement": [
+                                            {
+                                                "Effect": "Allow",
+                                                "Principal": {
+                                                    "Service": ["ec2.amazonaws.com"]
+                                                },
+                                                "Action": ["sts:AssumeRole"],
+                                            }
+                                        ],
+                                    },
+                                    "Path": "/",
+                                },
+                            }
+                        },
+                    ],
+                },
+            }
+        )
+
+        self.result = {
+            "Mappings": {
+                "Accounts": {
+                    "111111111111": {"AppName": []},
+                },
+            },
+            "Resources": {},
+            "Transform": ["AWS::LanguageExtensions"],
+        }
+
+    def test_transform(self):
+        self.maxDiff = None
+        with mock.patch(
+            "cfnlint.template.transforms._language_extensions._ACCOUNT_ID", None
+        ):
+            cfn = Template(
+                filename="", template=self.template_obj, regions=["us-east-1"]
+            )
+            matches, template = language_extension(cfn)
+            self.assertListEqual(matches, [])
+            self.assertDictEqual(
+                template,
+                self.result,
+                template,
+            )
+
+
+class TestTransformValueOneEmpty(TestCase):
+    def setUp(self) -> None:
+        self.template_obj = convert_dict(
+            {
+                "Transform": ["AWS::LanguageExtensions"],
+                "Mappings": {
+                    "Accounts": {
+                        "111111111111": {"AppName": []},
+                        "222222222222": {"AppName": ["C", "D"]},
+                        "333333333333": {"AppName": []},
+                    },
+                },
+                "Resources": {
+                    "Fn::ForEach::Regions": [
+                        "AppName",
+                        {
+                            "Fn::FindInMap": [
+                                "Accounts",
+                                {"Ref": "AWS::AccountId"},
+                                "AppName",
+                            ]
+                        },
+                        {
+                            "${AppName}Role": {
+                                "Type": "AWS::IAM::Role",
+                                "Properties": {
+                                    "RoleName": {"Ref": "AppName"},
+                                    "AssumeRolePolicyDocument": {
+                                        "Version": "2012-10-17",
+                                        "Statement": [
+                                            {
+                                                "Effect": "Allow",
+                                                "Principal": {
+                                                    "Service": ["ec2.amazonaws.com"]
+                                                },
+                                                "Action": ["sts:AssumeRole"],
+                                            }
+                                        ],
+                                    },
+                                    "Path": "/",
+                                },
+                            }
+                        },
+                    ],
+                },
+            }
+        )
+
+        self.result = {
+            "Mappings": {
+                "Accounts": {
+                    "111111111111": {"AppName": []},
+                    "222222222222": {"AppName": ["C", "D"]},
+                    "333333333333": {"AppName": []},
+                },
+            },
+            "Resources": {
+                "CRole": {
+                    "Properties": {
+                        "AssumeRolePolicyDocument": {
+                            "Statement": [
+                                {
+                                    "Action": ["sts:AssumeRole"],
+                                    "Effect": "Allow",
+                                    "Principal": {"Service": ["ec2.amazonaws.com"]},
+                                }
+                            ],
+                            "Version": "2012-10-17",
+                        },
+                        "Path": "/",
+                        "RoleName": "C",
+                    },
+                    "Type": "AWS::IAM::Role",
+                },
+                "DRole": {
+                    "Properties": {
+                        "AssumeRolePolicyDocument": {
+                            "Statement": [
+                                {
+                                    "Action": ["sts:AssumeRole"],
+                                    "Effect": "Allow",
+                                    "Principal": {"Service": ["ec2.amazonaws.com"]},
+                                }
+                            ],
+                            "Version": "2012-10-17",
+                        },
+                        "Path": "/",
+                        "RoleName": "D",
+                    },
+                    "Type": "AWS::IAM::Role",
+                },
+            },
+            "Transform": ["AWS::LanguageExtensions"],
+        }
+
+    def test_transform(self):
+        self.maxDiff = None
+        with mock.patch(
+            "cfnlint.template.transforms._language_extensions._ACCOUNT_ID", None
+        ):
+            cfn = Template(
+                filename="", template=self.template_obj, regions=["us-east-1"]
+            )
+            matches, template = language_extension(cfn)
+            self.assertListEqual(matches, [])
+            self.assertDictEqual(
+                template,
+                self.result,
+                template,
+            )