Repository: cloud-custodian/cloud-custodian

Issue:
aws - [ERROR] KeyError: 'name' - CodePeipeline Custodian Policies with mode Config-rule fails
**Describe the bug**
-> After deploying the CodePipeline Tag Compliance policy and Artifact Store Encryption key check with config-rule mode in the AWS environment, I am encountering an error listed below:

Note: The error is same for both the policy logs.

[ERROR] KeyError: 'name'
Traceback (most recent call last):
  File "/var/task/custodian_policy.py", line 4, in run
    return handler.dispatch_event(event, context)
  File "/var/task/c7n/handler.py", line 166, in dispatch_event
    p.push(event, context)
  File "/var/task/c7n/policy.py", line 1138, in push
    return mode.run(event, lambda_ctx)
  File "/var/task/c7n/policy.py", line 854, in run
    resources = super(ConfigRuleMode, self).run(event, lambda_context)
  File "/var/task/c7n/policy.py", line 442, in run
    resources = self.policy.resource_manager.filter_resources(
  File "/var/task/c7n/manager.py", line 112, in filter_resources
    resources = f.process(resources, event)
  File "/var/task/c7n/filters/core.py", line 325, in process
    sweeper = AnnotationSweeper(self.get_resource_type_id(), resources)
  File "/var/task/c7n/filters/core.py", line 383, in __init__
    ra_map[r[id_key]] = {k: v for k, v in r.items() if k.startswith('c7n')}

The config rule created, for both the checks, through this policies shows "No resources in scope"

**To Reproduce**
1.Deploy this cloudformation template to create a a codepipeline.
[codepipeline-codecommit-events-yaml.yaml.zip](https://github.com/cloud-custodian/cloud-custodian/files/5495030/codepipeline-codecommit-events-yaml.yaml.zip)
[SampleApp_Linux.zip](https://github.com/cloud-custodian/cloud-custodian/files/5495031/SampleApp_Linux.zip)

2. Deploy the policy in your account and check your config rule and cloudformation logs.

Note: Change the account number and role name.

**Expected behavior**
On successful execution of the policy, the config rule should flag the Code pipelines with missing required tags as Non-compliant resource.

**Background (please complete the following information):**
 - Python Version: 3.8.5
 - Custodian Version: 0.9.7
 - Cloud Provider: AWS
 - Policy: [please exclude any account/sensitive information]

**Tag Compliance**

```
---
policies:
  - name: d-codepipeline-missing-tags-1
    resource: aws.codepipeline
    description: >
      Type: Config Rule|
      Compliance: Codepipeline missing tags
    mode:
      type: config-rule
      role: "arn:aws:iam::<acc_number>:role/<role_name>"
      timeout: 200
    filters:
      - or:
          - tag:abc: absent
          - tag:def: absent
          - tag:efg: absent
      - tag:__dwf: absent

```
**ArtifactStore Encryption Key Check**

```
---
policies:
  - name: d-codepipeline-encryption-2
    resource: aws.codepipeline
    description: >
      Type: Config Rule|
      Compliance: Codepipeline not encrypted
    mode:
      type: config-rule
      role: "arn:aws:iam::<account_numer>:role/<role_name>"
      timeout: 200
    filters:
      - type: value
        key: artifactStore.encryptionKey
        value: absent
      - tag:__CPEncryption: absent

```
Here are the cloudwatch log files for both policies lambda execution.
[CodePipelineEncryptionKeyCheckCWLogs.docx](https://github.com/cloud-custodian/cloud-custodian/files/5366220/CodePipelineEncryptionKeyCheckCWLogs.docx)
[CodePipelineTagComplianeCWLogs.docx](https://github.com/cloud-custodian/cloud-custodian/files/5366221/CodePipelineTagComplianeCWLogs.docx)

**Additional context**
Custodian:   0.9.7
Python:      3.8.5 (default, Jul 21 2020, 10:42:08) 
             [Clang 11.0.0 (clang-1100.0.33.17)]
Platform:    posix.uname_result(sysname='Darwin', nodename='f8ffc201b550.ant.amazon.com', release='18.7.0', version='Darwin Kernel Version 18.7.0: Mon Aug 31 20:53:32 PDT 2020; root:xnu-4903.278.44~1/RELEASE_X86_64', machine='x86_64')
Using venv:  True
Docker: False
Installed: 

argcomplete==1.12.1
attrs==20.2.0
boto3==1.16.10
botocore==1.19.10
importlib-metadata==1.7.0
jmespath==0.10.0
jsonpickle==1.3
jsonschema==3.2.0
pyrsistent==0.17.3
python-dateutil==2.8.1
pyyaml==5.3.1
s3transfer==0.3.3
setuptools==47.1.0
six==1.15.0
tabulate==0.8.7
urllib3==1.25.11
zipp==3.4.0


Discussion:

Resolving patch:
diff --git a/c7n/query.py b/c7n/query.py
--- a/c7n/query.py
+++ b/c7n/query.py
@@ -338,27 +338,33 @@ def get_query_params(self, query):
         return {'expr': s}
 
     def load_resource(self, item):
+        item_config = self._load_item_config(item)
+        resource = camelResource(item_config, implicitDate=True)
+        self._load_resource_tags(resource, item)
+        return resource
+
+    def _load_item_config(self, item):
         if isinstance(item['configuration'], str):
             item_config = json.loads(item['configuration'])
         else:
             item_config = item['configuration']
-        resource = camelResource(item_config, implicitDate=True)
+        return item_config
+
+    def _load_resource_tags(self, resource, item):
         # normalized tag loading across the many variants of config's inconsistencies.
-        if ((item.get('tags') or item['supplementaryConfiguration'].get('Tags'))
-                and 'Tags' not in resource):
-            if item.get('tags'):
-                resource['Tags'] = [
-                    {u'Key': k, u'Value': v} for k, v in item['tags'].items()]
-            else:
-                # config has a bit more variation on tags (serialized json, list, dict, etc)
-                stags = item['supplementaryConfiguration']['Tags']
-                if isinstance(stags, str):
-                    stags = json.loads(stags)
-                if isinstance(stags, list):
-                    resource['Tags'] = [{u'Key': t['key'], u'Value': t['value']} for t in stags]
-                elif isinstance(stags, dict):
-                    resource['Tags'] = [{u'Key': k, u'Value': v} for k, v in stags.items()]
-        return resource
+        if 'Tags' in resource:
+            return
+        elif item.get('tags'):
+            resource['Tags'] = [
+                {u'Key': k, u'Value': v} for k, v in item['tags'].items()]
+        elif item['supplementaryConfiguration'].get('Tags'):
+            stags = item['supplementaryConfiguration']['Tags']
+            if isinstance(stags, str):
+                stags = json.loads(stags)
+            if isinstance(stags, list):
+                resource['Tags'] = [{u'Key': t['key'], u'Value': t['value']} for t in stags]
+            elif isinstance(stags, dict):
+                resource['Tags'] = [{u'Key': k, u'Value': v} for k, v in stags.items()]
 
     def get_listed_resources(self, client):
         # fallback for when config decides to arbitrarily break select
diff --git a/c7n/resources/code.py b/c7n/resources/code.py
--- a/c7n/resources/code.py
+++ b/c7n/resources/code.py
@@ -216,6 +216,16 @@ def process_project(self, client, project):
                 "Exception deleting project:\n %s" % e)
 
 
+class ConfigPipeline(ConfigSource):
+
+    def load_resource(self, item):
+        item_config = self._load_item_config(item)
+        resource = item_config.pop('pipeline')
+        resource.update(item_config['metadata'])
+        self._load_resource_tags(resource, item)
+        return resource
+
+
 class DescribePipeline(DescribeSource):
 
     def augment(self, resources):
@@ -239,7 +249,7 @@ class resource_type(TypeInfo):
 
     source_mapping = {
         'describe': DescribePipeline,
-        'config': ConfigSource
+        'config': ConfigPipeline
     }
 
 


Test patch:
diff --git a/tests/data/config/pipeline.json b/tests/data/config/pipeline.json
new file mode 100644
--- /dev/null
+++ b/tests/data/config/pipeline.json
@@ -0,0 +1,44 @@
+{
+    "version": "1.3",
+    "accountId": "644160558196",
+    "configurationItemCaptureTime": "2020-11-15T17:24:05.822000-05:00",
+    "configurationItemStatus": "OK",
+    "configurationStateId": "1605479045822",
+    "configurationItemMD5Hash": "",
+    "arn": "arn:aws:codepipeline:us-east-2:644160558196:burnifyPipeline",
+    "resourceType": "AWS::CodePipeline::Pipeline",
+    "resourceId": "burnifyPipeline",
+    "resourceName": "burnifyPipeline",
+    "awsRegion": "us-east-2",
+    "availabilityZone": "Not Applicable",
+    "resourceCreationTime": "2020-11-15T16:21:54.605000-05:00",
+    "tags": {},
+    "relatedEvents": [],
+    "relationships": [
+        {
+            "resourceType": "AWS::S3::Bucket",
+            "resourceId": "mypipe-artifactbucketstore-4ebot00zlvbv",
+            "resourceName": "mypipe-artifactbucketstore-4ebot00zlvbv",
+            "relationshipName": "Is attached to "
+        },
+        {
+            "resourceType": "AWS::CodeBuild::Project",
+            "resourceName": "burnifyBuild",
+            "relationshipName": "Is associated with "
+        },
+        {
+            "resourceType": "AWS::IAM::Role",
+            "resourceName": "mypipe-CodePipelineRole-1MVD6071LRP5R",
+            "relationshipName": "Is associated with "
+        },
+        {
+            "resourceType": "AWS::CloudFormation::Stack",
+            "resourceName": "burnifyBetaStack",
+            "relationshipName": "Is associated with "
+        }
+    ],
+    "configuration": "{\"pipeline\":{\"name\":\"burnifyPipeline\",\"roleArn\":\"arn:aws:iam::644160558196:role/mypipe-CodePipelineRole-1MVD6071LRP5R\",\"artifactStore\":{\"type\":\"S3\",\"location\":\"mypipe-artifactbucketstore-4ebot00zlvbv\"},\"stages\":[{\"name\":\"Source\",\"actions\":[{\"name\":\"Source\",\"actionTypeId\":{\"category\":\"Source\",\"owner\":\"AWS\",\"provider\":\"CodeCommit\",\"version\":\"1\"},\"runOrder\":1,\"configuration\":{\"RepositoryName\":\"burnify\",\"BranchName\":\"master\"},\"outputArtifacts\":[{\"name\":\"SourceRepo\"}],\"inputArtifacts\":[]}]},{\"name\":\"Build\",\"actions\":[{\"name\":\"CodeBuild\",\"actionTypeId\":{\"category\":\"Build\",\"owner\":\"AWS\",\"provider\":\"CodeBuild\",\"version\":\"1\"},\"runOrder\":1,\"configuration\":{\"ProjectName\":\"burnifyBuild\"},\"outputArtifacts\":[{\"name\":\"CompiledCFNTemplate\"}],\"inputArtifacts\":[{\"name\":\"SourceRepo\"}]}]},{\"name\":\"Beta\",\"actions\":[{\"name\":\"CreateBetaChangeSet\",\"actionTypeId\":{\"category\":\"Deploy\",\"owner\":\"AWS\",\"provider\":\"CloudFormation\",\"version\":\"1\"},\"runOrder\":1,\"configuration\":{\"TemplatePath\":\"CompiledCFNTemplate::transformed.yaml\",\"ActionMode\":\"CHANGE_SET_REPLACE\",\"Capabilities\":\"CAPABILITY_IAM\",\"ChangeSetName\":\"burnifyChangeSet\",\"RoleArn\":\"arn:aws:iam::644160558196:role/mypipe-CFNDeployRole-I9O4V8I7J671\",\"StackName\":\"burnifyBetaStack\"},\"outputArtifacts\":[],\"inputArtifacts\":[{\"name\":\"CompiledCFNTemplate\"}]},{\"name\":\"ExecuteChangeSet\",\"actionTypeId\":{\"category\":\"Deploy\",\"owner\":\"AWS\",\"provider\":\"CloudFormation\",\"version\":\"1\"},\"runOrder\":2,\"configuration\":{\"ActionMode\":\"CHANGE_SET_EXECUTE\",\"OutputFileName\":\"StackOutputs.json\",\"ChangeSetName\":\"burnifyChangeSet\",\"StackName\":\"burnifyBetaStack\"},\"outputArtifacts\":[{\"name\":\"AppDeploymentValues\"}],\"inputArtifacts\":[]}]},{\"name\":\"Prod\",\"actions\":[{\"name\":\"OneMoreTime\",\"actionTypeId\":{\"category\":\"Build\",\"owner\":\"AWS\",\"provider\":\"CodeBuild\",\"version\":\"1\"},\"runOrder\":1,\"configuration\":{\"ProjectName\":\"burnifyBuild\"},\"outputArtifacts\":[],\"inputArtifacts\":[{\"name\":\"SourceRepo\"}],\"region\":\"us-east-2\"}]}],\"version\":2},\"metadata\":{\"pipelineArn\":\"arn:aws:codepipeline:us-east-2:644160558196:burnifyPipeline\",\"created\":1605475314605}}",
+    "supplementaryConfiguration": {
+        "unsupportedResources": "[{\"resourceType\":\"AWS::CodeCommit::Repository\",\"resourceName\":\"burnify\"}]"
+    }
+}
diff --git a/tests/test_code.py b/tests/test_code.py
--- a/tests/test_code.py
+++ b/tests/test_code.py
@@ -2,7 +2,7 @@
 # Copyright The Cloud Custodian Authors.
 # SPDX-License-Identifier: Apache-2.0
 import time
-from .common import BaseTest
+from .common import BaseTest, event_data
 
 from c7n.resources.aws import shape_validate
 
@@ -177,6 +177,20 @@ def test_post_finding_build(self):
 
 class CodePipeline(BaseTest):
 
+    def test_config_pipeline(self):
+        p = self.load_policy({
+            'name': 'config-pipe',
+            'resource': 'aws.codepipeline',
+            'source': 'config',
+        })
+        source = p.resource_manager.get_source('config')
+        item = event_data('pipeline.json', 'config')
+        resource = source.load_resource(item)
+        assert resource['name'] == 'burnifyPipeline'
+        assert resource['artifactStore'] == {
+            'type': 'S3', 'location': 'mypipe-artifactbucketstore-4ebot00zlvbv'}
+        assert len(resource['stages']) == 4
+
     def test_query_pipeline(self):
         factory = self.replay_flight_data("test_codepipeline")
         p = self.load_policy(