Repository: aws-cloudformation/cfn-lint

Issue:
resource metadata in foreach
### CloudFormation Lint Version

0.83.7

### What operating system are you using?

windows

### Describe the bug

i am unable to suppress findings via metadata in a resource when inside a foreach resource. if i put the metadata in the looped resource, it errors with str object has no attribute....

if i put it as a property of the for loop itself i get a did not find expected key

### Expected behavior

metadata is recognized and correct action taken.

### Reproduction template

```
  ## Replicas
  'Fn::ForEach::ReplicaInstance':
    - ReplicaIdentifier
    - [
        '1',
        '2',
        '3',
        '4',
        '5',
        '6',
        '7',
        '8',
        '9',
        '10',
        '11',
        '12',
        '13',
        '14',
        '15'
      ]
    - 'ReplicaInstance${ReplicaIdentifier}':
        Metadata:
          cfn-lint:
            config:
              ignore_checks:
                - I3013
        Type: AWS::RDS::DBInstance
        DependsOn:
          - !Sub 'ResourceWaiterCloudwatchLogsAgent${ReplicaIdentifier}'
          - !Sub 'ResourceWaiterCloudwatchLogsError${ReplicaIdentifier}'
        DeletionPolicy: !If
          - ConditionProd
          - Retain
          - Delete
        UpdateReplacePolicy: !If
          - ConditionProd
          - Retain
          - Delete
        Condition: !Sub 'ConditionCreateReplicaInstance${ReplicaIdentifier}'
        Properties:
          AllocatedStorage: !Ref AllocatedStorage
          AllowMajorVersionUpgrade: !Ref AllowMajorVersionUpgrade
          AutoMinorVersionUpgrade: !Ref AllowMinorVersionUpgrade
          CACertificateIdentifier: !Ref CaCertificateIdentifier
          DBInstanceClass: !Ref InstanceClass
          DBInstanceIdentifier: !Sub '${Region}-${BusinessUnit}-${Environment}-${AppName}-${Service}-${RdsPurpose}${ReplicaIdentifier}'
          DBParameterGroupName: !Ref InstanceParameterGroup
          DBSubnetGroupName: !Ref SubnetGroup
          DeletionProtection: !If
            - ConditionOVERRIDEDeletionProtectionReplicas
            - !Ref OVERRIDEDeletionProtectionReplicas
            - !If
              - ConditionProd
              - true
              - false
          EnableCloudwatchLogsExports: !If
            - ConditionPublishLogsToCloudwatchLogs
            - - !If
                - !Sub 'ConditionPublishLogsToCloudwatchLogsAgentReplica${ReplicaIdentifier}'
                - agent
                - !Ref AWS::NoValue
              - !If
                - !Sub 'ConditionPublishLogsToCloudwatchLogsErrorReplica${ReplicaIdentifier}'
                - error
                - !Ref AWS::NoValue
            - !Ref AWS::NoValue
          EnablePerformanceInsights: true
          Engine: !Ref SqlEngine
          EngineVersion: !If
            - ConditionReplicaSqlVersionSame
            - !Ref SqlEngineVersion
            - !Ref ReplicasSqlEngineVersion
          Iops: !If
            - ConditionDbIops
            - !Ref Iops
            - !Ref AWS::NoValue
          LicenseModel: license-included
          MaxAllocatedStorage: !Ref MaxAllocatedStorage
          MonitoringInterval: !Ref MonitoringInterval
          MonitoringRoleArn: !GetAtt RdsMonitoringRole.Arn
          OptionGroupName: !Ref OptionGroup
          PerformanceInsightsKMSKeyId:
            Fn::ImportValue: !Sub '${Region}-${BusinessUnit}-${Environment}-${AppName}-${Service}-${RdsPurpose}-kms-key-alias-arn'
          PerformanceInsightsRetentionPeriod: !If
            - ConditionOVERRIDEPerformanceInsightsRetentionPeriod
            - !Ref OVERRIDEPerformanceInsightsRetentionPeriod
            - !If
              - ConditionProd
              - 731
              - 7
          PreferredMaintenanceWindow: !Ref PreferredMaintenanceWindow
          PubliclyAccessible: false
          SourceDBInstanceIdentifier: !Ref PrimaryInstance
          StorageType: !Ref StorageType
          VPCSecurityGroups:
            - Fn::ImportValue: !Sub '${Region}-${BusinessUnit}-${Environment}-${AppName}-sg-${Service}-${RdsPurpose}'
          Tags:
            - Key: Name
              Value: !Sub '${Region}-${BusinessUnit}-${Environment}-${AppName}-${Service}-${RdsPurpose}${ReplicaIdentifier}'
            - Key: region-abbreviation
              Value: !Ref Region
            - Key: business-unit
              Value: !Ref BusinessUnit
            - Key: environment-type
              Value: !Ref Environment
            - Key: app-name
              Value: !Ref AppName
            - Key: service-type
              Value: !Ref Service
            - Key: cost-center
              Value: !Ref CostCenter
            - Key: compliance-type
              Value: !Ref Compliance
            - Key: owner-name
              Value: !Ref OwnerName
            - Key: owner-email-id
              Value: !Ref OwnerEmail
            - Key: patch-group
              Value: !Ref PatchGroup
            - Key: criticality-level
              Value: !Ref CriticalityLevel
            - Key: map-migrated
              Value: !Ref MapMigrated
```

and 
```
  'Fn::ForEach::ReplicaInstance':
    Metadata:
          cfn-lint:
            config:
              ignore_checks:
                - I3013
    - ReplicaIdentifier
    - [
        '1',
        '2',
        '3',
        '4',
        '5',
        '6',
        '7',
        '8',
        '9',
        '10',
        '11',
        '12',
        '13',
        '14',
        '15'
      ]
    - 'ReplicaInstance${ReplicaIdentifier}':
        Type: AWS::RDS::DBInstance
        DependsOn:
          - !Sub 'ResourceWaiterCloudwatchLogsAgent${ReplicaIdentifier}'
          - !Sub 'ResourceWaiterCloudwatchLogsError${ReplicaIdentifier}'
        DeletionPolicy: !If
          - ConditionProd
          - Retain
          - Delete
        UpdateReplacePolicy: !If
          - ConditionProd
          - Retain
          - Delete
        Condition: !Sub 'ConditionCreateReplicaInstance${ReplicaIdentifier}'
        Properties:
          AllocatedStorage: !Ref AllocatedStorage
          AllowMajorVersionUpgrade: !Ref AllowMajorVersionUpgrade
          AutoMinorVersionUpgrade: !Ref AllowMinorVersionUpgrade
          CACertificateIdentifier: !Ref CaCertificateIdentifier
          DBInstanceClass: !Ref InstanceClass
          DBInstanceIdentifier: !Sub '${Region}-${BusinessUnit}-${Environment}-${AppName}-${Service}-${RdsPurpose}${ReplicaIdentifier}'
          DBParameterGroupName: !Ref InstanceParameterGroup
          DBSubnetGroupName: !Ref SubnetGroup
          DeletionProtection: !If
            - ConditionOVERRIDEDeletionProtectionReplicas
            - !Ref OVERRIDEDeletionProtectionReplicas
            - !If
              - ConditionProd
              - true
              - false
          EnableCloudwatchLogsExports: !If
            - ConditionPublishLogsToCloudwatchLogs
            - - !If
                - !Sub 'ConditionPublishLogsToCloudwatchLogsAgentReplica${ReplicaIdentifier}'
                - agent
                - !Ref AWS::NoValue
              - !If
                - !Sub 'ConditionPublishLogsToCloudwatchLogsErrorReplica${ReplicaIdentifier}'
                - error
                - !Ref AWS::NoValue
            - !Ref AWS::NoValue
          EnablePerformanceInsights: true
          Engine: !Ref SqlEngine
          EngineVersion: !If
            - ConditionReplicaSqlVersionSame
            - !Ref SqlEngineVersion
            - !Ref ReplicasSqlEngineVersion
          Iops: !If
            - ConditionDbIops
            - !Ref Iops
            - !Ref AWS::NoValue
          LicenseModel: license-included
          MaxAllocatedStorage: !Ref MaxAllocatedStorage
          MonitoringInterval: !Ref MonitoringInterval
          MonitoringRoleArn: !GetAtt RdsMonitoringRole.Arn
          OptionGroupName: !Ref OptionGroup
          PerformanceInsightsKMSKeyId:
            Fn::ImportValue: !Sub '${Region}-${BusinessUnit}-${Environment}-${AppName}-${Service}-${RdsPurpose}-kms-key-alias-arn'
          PerformanceInsightsRetentionPeriod: !If
            - ConditionOVERRIDEPerformanceInsightsRetentionPeriod
            - !Ref OVERRIDEPerformanceInsightsRetentionPeriod
            - !If
              - ConditionProd
              - 731
              - 7
          PreferredMaintenanceWindow: !Ref PreferredMaintenanceWindow
          PubliclyAccessible: false
          SourceDBInstanceIdentifier: !Ref PrimaryInstance
          StorageType: !Ref StorageType
          VPCSecurityGroups:
            - Fn::ImportValue: !Sub '${Region}-${BusinessUnit}-${Environment}-${AppName}-sg-${Service}-${RdsPurpose}'
          Tags:
            - Key: Name
              Value: !Sub '${Region}-${BusinessUnit}-${Environment}-${AppName}-${Service}-${RdsPurpose}${ReplicaIdentifier}'
            - Key: region-abbreviation
              Value: !Ref Region
            - Key: business-unit
              Value: !Ref BusinessUnit
            - Key: environment-type
              Value: !Ref Environment
            - Key: app-name
              Value: !Ref AppName
            - Key: service-type
              Value: !Ref Service
            - Key: cost-center
              Value: !Ref CostCenter
            - Key: compliance-type
              Value: !Ref Compliance
            - Key: owner-name
              Value: !Ref OwnerName
            - Key: owner-email-id
              Value: !Ref OwnerEmail
            - Key: patch-group
              Value: !Ref PatchGroup
            - Key: criticality-level
              Value: !Ref CriticalityLevel
            - Key: map-migrated
              Value: !Ref MapMigrated
```


Discussion:
Found the issue and working on a fix.  Thanks for raising this.

Resolving patch:
diff --git a/src/cfnlint/template/transforms/_language_extensions.py b/src/cfnlint/template/transforms/_language_extensions.py
--- a/src/cfnlint/template/transforms/_language_extensions.py
+++ b/src/cfnlint/template/transforms/_language_extensions.py
@@ -14,6 +14,7 @@
 import regex as re
 
 from cfnlint.conditions._utils import get_hash
+from cfnlint.decode.node import str_node
 from cfnlint.helpers import FUNCTION_FOR_EACH
 from cfnlint.template.transforms._types import TransformResult
 
@@ -197,10 +198,15 @@ def _replace_string_params(
         pattern = r"\${[a-zA-Z0-9\.:]+}"
         if not re.search(pattern, s):
             return (True, s)
+
+        new_s = deepcopy(s)
         for k, v in params.items():
-            s = re.sub(rf"\$\{{{k}\}}", v, s)
+            new_s = re.sub(rf"\$\{{{k}\}}", v, new_s)
+
+        if isinstance(s, str_node):
+            new_s = str_node(new_s, s.start_mark, s.end_mark)
 
-        return (not (bool(re.search(pattern, s))), s)
+        return (not (bool(re.search(pattern, new_s))), new_s)
 
 
 class _ForEachValue:


Test patch: