Repository: aws-cloudformation/cfn-lint

Issue:
cfn-lint considers configuration broken when ignore_templates doesn't match any files
### CloudFormation Lint Version

1.22.0

### What operating system are you using?

Linux

### Describe the bug

cfn-lint considers configuration broken when ignore_templates doesn't match any files

We've added `node_modules/**` to `ignore_templates` but now cfn-lint fails since 1.22.0 when this directory doesn't exists / is empty.

The error message is non descriptive:

```
E0003 node_modules/** could not be processed by glob.glob
None:1:1
```

https://github.com/aws-cloudformation/cfn-lint/blob/f920591a75883f7b0b431bb414cdd627d9b1c627/src/cfnlint/config.py#L799-L800

Seems to be introduced by #3862 / @kddejong 

### Expected behavior

Allow excluding of templates that don't exists.

### Reproduction template

Use the following `.cfnlintrc`

```yaml
regions:
  - us-east-1
  - eu-west-1
templates:
  - "**/*.yaml"
  - "**/*.yml"
ignore_templates:
  - "node_modules/**"
```

Discussion:
@kddejong: The quick solution is `ignore_bad_template` but going to leave this open as I am researching a few more things.

```yaml
regions:
  - us-east-1
  - eu-west-1
templates:
  - "**/*.yaml"
  - "**/*.yml"
ignore_bad_template: true
ignore_templates:
  - "node_modules/**"
```


Solution patch:
diff --git a/src/cfnlint/config.py b/src/cfnlint/config.py
index a9f4aa5e4..244d8df77 100644
--- a/src/cfnlint/config.py
+++ b/src/cfnlint/config.py
@@ -785,9 +785,11 @@ class ConfigMixIn(TemplateArgs, CliArgs, ConfigFileArgs):
         if isinstance(filenames, str):
             filenames = [filenames]
 
-        return self._glob_filenames(filenames)
+        return self._glob_filenames(filenames, False)
 
-    def _glob_filenames(self, filenames: Sequence[str]) -> list[str]:
+    def _glob_filenames(
+        self, filenames: Sequence[str], raise_exception: bool = True
+    ) -> list[str]:
         # handle different shells and Config files
         # some shells don't expand * and configparser won't expand wildcards
         all_filenames = []
@@ -796,7 +798,8 @@ class ConfigMixIn(TemplateArgs, CliArgs, ConfigFileArgs):
             add_filenames = glob.glob(filename, recursive=True)
 
             if not add_filenames and not self.ignore_bad_template:
-                raise ValueError(f"{filename} could not be processed by glob.glob")
+                if raise_exception:
+                    raise ValueError(f"{filename} could not be processed by glob.glob")
 
             all_filenames.extend(add_filenames)
 


Test patch:
diff --git a/test/unit/module/config/test_config_mixin.py b/test/unit/module/config/test_config_mixin.py
index 9e584ead0..cbed4b458 100644
--- a/test/unit/module/config/test_config_mixin.py
+++ b/test/unit/module/config/test_config_mixin.py
@@ -247,6 +247,28 @@ class TestConfigMixIn(BaseTestCase):
         )
         self.assertEqual(len(config.templates), 3)
 
+    @patch("cfnlint.config.ConfigFileArgs._read_config", create=True)
+    def test_config_expand_and_ignore_templates_with_bad_path(self, yaml_mock):
+        """Test ignore templates"""
+
+        yaml_mock.side_effect = [
+            {
+                "templates": ["test/fixtures/templates/bad/resources/iam/*.yaml"],
+                "ignore_templates": [
+                    "dne/fixtures/templates/bad/resources/iam/resource_*.yaml"
+                ],
+            },
+            {},
+        ]
+        config = cfnlint.config.ConfigMixIn([])
+
+        # test defaults
+        self.assertIn(
+            str(Path("test/fixtures/templates/bad/resources/iam/resource_policy.yaml")),
+            config.templates,
+        )
+        self.assertEqual(len(config.templates), 4)
+
     @patch("cfnlint.config.ConfigFileArgs._read_config", create=True)
     def test_config_merge(self, yaml_mock):
         """Test merging lists"""