Repository: aws-cloudformation/cfn-lint

Issue:
aws::ec2::networkinterface E1041 bug
### CloudFormation Lint Version

1.25.1

### What operating system are you using?

windows

### Describe the bug

[cfn-lint] E1041: {'Ref': 'EniSg'} does not match destination format of 'AWS::EC2::SecurityGroup.Id'


the custom function finds the SG Id and returns it for a !Ref in other resources. E1041 is picking it up as a format error

### Expected behavior

E1041 ignores the format error when a !Ref is to a Custom resource. I've confirmed that using a !Sub '${EniSg}' doesn't produce the error

### Reproduction template

```
Resources:
  EniSg:
    Type: Custom::SgFinder
    Properties:
      ServiceToken: test123
      SgIdentifier: Sg1

  Ec2NodeEni:
    Type: AWS::EC2::NetworkInterface
    Properties:
      Description: networkinterface
      GroupSet:
        - !Ref EniSg
      SubnetId: subnet-12345678
```

Discussion:
@kddejong: I'm trying to replicate this.  What is the payload you are sending back from the custom resource?  Are you using a custom resource helper like `cfnresponse` or the `custom-resource-helper`?
@whoDoneItAgain: Yes. Im using cfnresponse to get data out; either via ref or getatt. @kddejong: Fixed and going to have a release shortly.  Thanks for the feedback.

Solution patch:
diff --git a/src/cfnlint/rules/functions/RefFormat.py b/src/cfnlint/rules/functions/RefFormat.py
index c734e6403..64a9b651b 100644
--- a/src/cfnlint/rules/functions/RefFormat.py
+++ b/src/cfnlint/rules/functions/RefFormat.py
@@ -8,6 +8,7 @@ from __future__ import annotations
 from copy import deepcopy
 from typing import Any
 
+from cfnlint.helpers import is_custom_resource
 from cfnlint.jsonschema import ValidationResult, Validator
 from cfnlint.jsonschema._utils import Unset
 from cfnlint.rules.formats._schema_comparer import compare_schemas
@@ -62,6 +63,12 @@ class RefFormat(CfnLintKeyword):
         if instance not in validator.context.resources:
             return
         t = validator.context.resources[instance].type
+
+        # When using a custom resource you can set the physical ID
+        # which results in a usable Ref
+        if is_custom_resource(t):
+            return
+
         for (
             regions,
             resource_schema,
@@ -69,9 +76,7 @@ class RefFormat(CfnLintKeyword):
             t, validator.context.regions
         ):
             region = regions[0]
-
             ref_schema = validator.context.resources[instance].ref(region)
-
             ref_schema = self._filter_schema(validator, t, instance, ref_schema)
 
             err = compare_schemas(schema, ref_schema)


Test patch:
diff --git a/test/unit/rules/functions/test_ref_format.py b/test/unit/rules/functions/test_ref_format.py
index 9e13087c0..5c7b26013 100644
--- a/test/unit/rules/functions/test_ref_format.py
+++ b/test/unit/rules/functions/test_ref_format.py
@@ -21,6 +21,7 @@ def template():
         "Resources": {
             "MyBucket": {"Type": "AWS::S3::Bucket"},
             "MyVpc": {"Type": "AWS::EC2::VPC"},
+            "MyCustomResource": {"Type": "Custom::MyResource"},
         },
     }
 
@@ -34,6 +35,12 @@ def template():
             {"format": "AWS::EC2::VPC.Id"},
             [],
         ),
+        (
+            "Valid Ref with a good format",
+            "MyCustomResource",
+            {"format": "AWS::EC2::VPC.Id"},
+            [],
+        ),
         (
             "Invalid Ref with a bad format",
             "MyVpc",