Repository: cloud-custodian/cloud-custodian

Issue:
Mode config-rule cannot handle S3 Buckets in eu-north-1
### Describe the bug

When running a Custodian policy in the mode `config-rule`, and a configuration change occurs on an AWS S3 bucket, then the Custodian policy errors before the filters/actions are applied.

This error occurs in the `eu-north-1` region where [S3 Transfer Acceleration](https://docs.aws.amazon.com/AmazonS3/latest/userguide/transfer-acceleration.html) is not enabled (therefore BucketAccelerateConfiguration is not included in the `supplementaryConfiguration`)

### What did you expect to happen?

Ignore the BucketAcceleration config (consider it equal to the null value)

### Cloud Provider

Amazon Web Services (AWS)

### Cloud Custodian version and dependency information

```shell
Please copy/paste the following info along with any bug reports:

Custodian:   0.9.18
Python:      3.10.5 (main, Jul  3 2022, 05:52:33) [Clang 11.1.0 ]
Platform:    posix.uname_result(sysname='Darwin', nodename='nyx.localdomain', release='21.6.0', version='Darwin Kernel Version 21.6.0: Mon Aug 22 20:17:10 PDT 2022; root:xnu-8020.140.49~2/RELEASE_X86_64', machine='x86_64')
Using venv:  True
Docker: False
Installed:

argcomplete==2.0.0
attrs==22.1.0
boto3==1.24.44
botocore==1.27.44
docutils==0.17.1
importlib-metadata==4.12.0
jmespath==1.0.1
jsonschema==4.9.0
pyrsistent==0.18.1
python-dateutil==2.8.2
pyyaml==6.0
s3transfer==0.6.0
six==1.16.0
tabulate==0.8.10
urllib3==1.26.11
zipp==3.8.1
```


### Policy

```shell
# cat policy.yaml
vars:
  template: &template
    filters:
      - or:
          - "tag:XYZ": absent
    actions:
      - type: tag
        tags:
          XYZ: REDACTED_VALUE
    mode:
      type: config-rule
      role: arn:aws:iam::{account_id}:role/XYZ_ROLE
policies:
- <<: *template
  name: policyname
  resource: s3
```


### Relevant log/traceback output

```shell
[ERROR] KeyError: 'BucketAccelerateConfiguration'
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 165, in dispatch_event
    p.push(event, context)
  File "/var/task/c7n/policy.py", line 1245, in push
    return mode.run(event, lambda_ctx)
  File "/var/task/c7n/policy.py", line 942, in run
    resources = super(ConfigRuleMode, self).run(event, lambda_context)
  File "/var/task/c7n/policy.py", line 449, in run
    resources = self.resolve_resources(event)
  File "/var/task/c7n/policy.py", line 924, in resolve_resources
    return [source.load_resource(self.cfg_event['configurationItem'])]
  File "/var/task/c7n/resources/s3.py", line 120, in load_resource
    v = cfg[k]
```


### Extra information or context


<details><summary>The resource configuration sent to the Lambda script (click to open)</summary>

```shell
$ aws configservice get-resource-config-history --resource-type AWS::S3::Bucket --resource-id REDACTED_BUCKET_NAME > s3resource.txt
```

```json
{
  "configurationItems": [
    {
      "version": "1.3",
      "accountId": "REDACTED_ACCOUNT_ID",
      "configurationItemCaptureTime": "2022-09-28T10:39:09.705000+03:00",
      "configurationItemStatus": "ResourceDiscovered",
      "configurationStateId": "1664350749705",
      "configurationItemMD5Hash": "",
      "arn": "arn:aws:s3:::REDACTED_BUCKET_NAME",
      "resourceType": "AWS::S3::Bucket",
      "resourceId": "REDACTED_BUCKET_NAME",
      "resourceName": "REDACTED_BUCKET_NAME",
      "awsRegion": "eu-north-1",
      "availabilityZone": "Regional",
      "resourceCreationTime": "2022-09-28T10:38:21+03:00",
      "tags": {
        "aws:cloudformation:logical-id": "Bucket",
        "aws:cloudformation:stack-id": "arn:aws:cloudformation:eu-north-1:REDACTED_ACCOUNT_ID:stack/SOME_STACK/1617fbd0-3e5b-11ed-ba3d-0a1f1653d268",
        "aws:cloudformation:stack-name": "SOME_STACK"
      },
      "relatedEvents": [],
      "relationships": [],
      "configuration": "{\"name\":\"REDACTED_BUCKET_NAME\",\"owner\":{\"displayName\":null,\"id\":\"REDACTED\"},\"creationDate\":\"2022-09-28T07:38:21.000Z\"}",
      "supplementaryConfiguration": {
        "AccessControlList": "\"{\\\"grantSet\\\":null,\\\"grantList\\\":[{\\\"grantee\\\":{\\\"id\\\":\\\"REDACTED\\\",\\\"displayName\\\":null},\\\"permission\\\":\\\"FullControl\\\"}],\\\"owner\\\":{\\\"displayName\\\":null,\\\"id\\\":\\\"REDACTED2\\\"},\\\"isRequesterCharged\\\":false}\"",
        "BucketLoggingConfiguration": "{\"destinationBucketName\":null,\"logFilePrefix\":null}",
        "BucketNotificationConfiguration": "{\"configurations\":{}}",
        "BucketPolicy": "{\"policyText\":\"{\\\"Version\\\":\\\"2012-10-17\\\",\\\"Statement\\\":[{\\\"Sid\\\":\\\"AWSConfigBucketPermissionsCheck\\\",\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":{\\\"Service\\\":\\\"config.amazonaws.com\\\"},\\\"Action\\\":\\\"s3:GetBucketAcl\\\",\\\"Resource\\\":\\\"arn:aws:s3:::REDACTED_BUCKET_NAME\\\",\\\"Condition\\\":{\\\"StringEquals\\\":{\\\"AWS:SourceAccount\\\":\\\"REDACTED_ACCOUNT_ID\\\"}}},{\\\"Sid\\\":\\\"AWSConfigBucketExistenceCheck\\\",\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":{\\\"Service\\\":\\\"config.amazonaws.com\\\"},\\\"Action\\\":\\\"s3:ListBucket\\\",\\\"Resource\\\":\\\"arn:aws:s3:::REDACTED_BUCKET_NAME\\\",\\\"Condition\\\":{\\\"StringEquals\\\":{\\\"AWS:SourceAccount\\\":\\\"REDACTED_ACCOUNT_ID\\\"}}},{\\\"Sid\\\":\\\"AWSConfigBucketDelivery\\\",\\\"Effect\\\":\\\"Allow\\\",\\\"Principal\\\":{\\\"Service\\\":\\\"config.amazonaws.com\\\"},\\\"Action\\\":\\\"s3:PutObject\\\",\\\"Resource\\\":\\\"arn:aws:s3:::REDACTED_BUCKET_NAME/AWSLogs/REDACTED_ACCOUNT_ID/Config/*\\\",\\\"Condition\\\":{\\\"StringEquals\\\":{\\\"AWS:SourceAccount\\\":\\\"REDACTED_ACCOUNT_ID\\\",\\\"s3:x-amz-acl\\\":\\\"bucket-owner-full-control\\\"}}}]}\"}",
        "BucketTaggingConfiguration": "{\"tagSets\":[{\"tags\":{\"aws:cloudformation:stack-name\":\"SOME_STACK\",\"aws:cloudformation:logical-id\":\"Bucket\",\"aws:cloudformation:stack-id\":\"arn:aws:cloudformation:eu-north-1:REDACTED_ACCOUNT_ID:stack/SOME_STACK/1617fbd0-3e5b-11ed-ba3d-0a1f1653d268\",}}]}",
        "BucketVersioningConfiguration": "{\"status\":\"Off\",\"isMfaDeleteEnabled\":null}",
        "IsRequesterPaysEnabled": "false",
        "ServerSideEncryptionConfiguration": "{\"rules\":[{\"applyServerSideEncryptionByDefault\":{\"sseAlgorithm\":\"AES256\",\"kmsMasterKeyID\":null},\"bucketKeyEnabled\":false}]}"
      }
    }
  ]
}
```

</details>

https://github.com/cloud-custodian/cloud-custodian/blob/24810f7ed42cfd128d96de7e9970e2caff40386c/c7n/resources/s3.py#L113-L123

Likely the above code should handle missing keys at line 114, the same as the "null valued" keys.


Discussion:

Resolving patch:
diff --git a/c7n/resources/s3.py b/c7n/resources/s3.py
--- a/c7n/resources/s3.py
+++ b/c7n/resources/s3.py
@@ -111,6 +111,8 @@ def load_resource(self, item):
         resource.pop('Owner', None)
 
         for k, null_value in S3_CONFIG_SUPPLEMENT_NULL_MAP.items():
+            if k not in cfg:
+                continue
             if cfg.get(k) == null_value:
                 continue
             method = getattr(self, "handle_%s" % k, None)


Test patch:
diff --git a/tests/data/config/s3-from-rule-sans-accelerator.json b/tests/data/config/s3-from-rule-sans-accelerator.json
new file mode 100644
--- /dev/null
+++ b/tests/data/config/s3-from-rule-sans-accelerator.json
@@ -0,0 +1 @@
+{"invokingEvent": "{\"configurationItemDiff\":null,\"configurationItem\":{\"relatedEvents\":[\"99f04786-16e3-4f91-98a7-b7d710fe1e3b\"],\"relationships\":[],\"configuration\":{\"name\":\"c7n-fire-logs\",\"owner\":{\"displayName\":\"mandeep.bal\",\"id\":\"e7c8bb65a5fc49cf906715eae09de9e4bb7861a96361ba79b833aa45f6833b15\"},\"creationDate\":\"2017-09-15T02:05:40.000Z\"},\"supplementaryConfiguration\":{\"AccessControlList\":\"{\\\"grantSet\\\":null,\\\"grantList\\\":[{\\\"grantee\\\":{\\\"id\\\":\\\"e7c8bb65a5fc49cf906715eae09de9e4bb7861a96361ba79b833aa45f6833b15\\\",\\\"displayName\\\":\\\"mandeep.bal\\\"},\\\"permission\\\":\\\"FullControl\\\"}],\\\"owner\\\":{\\\"displayName\\\":\\\"mandeep.bal\\\",\\\"id\\\":\\\"e7c8bb65a5fc49cf906715eae09de9e4bb7861a96361ba79b833aa45f6833b15\\\"},\\\"isRequesterCharged\\\":false}\",\"BucketLoggingConfiguration\":{\"destinationBucketName\":null,\"logFilePrefix\":null},\"BucketNotificationConfiguration\":{\"configurations\":{}},\"BucketPolicy\":{\"policyText\":null},\"BucketVersioningConfiguration\":{\"status\":\"Off\",\"isMfaDeleteEnabled\":null},\"IsRequesterPaysEnabled\":false},\"tags\":{},\"configurationItemVersion\":\"1.2\",\"configurationItemCaptureTime\":\"2017-09-15T02:08:26.719Z\",\"configurationStateId\":1505441306719,\"awsAccountId\":\"644160558196\",\"configurationItemStatus\":\"ResourceDiscovered\",\"resourceType\":\"AWS::S3::Bucket\",\"resourceId\":\"c7n-fire-logs\",\"resourceName\":\"c7n-fire-logs\",\"ARN\":\"arn:aws:s3:::c7n-fire-logs\",\"awsRegion\":\"us-east-1\",\"availabilityZone\":\"Regional\",\"configurationStateMd5Hash\":\"6d3815aa37dbbd79e55abc72a11fb298\",\"resourceCreationTime\":\"2017-09-15T02:05:40.000Z\"},\"notificationCreationTime\":\"2017-10-30T08:54:48.029Z\",\"messageType\":\"ConfigurationItemChangeNotification\",\"recordVersion\":\"1.2\"}"}
diff --git a/tests/test_s3.py b/tests/test_s3.py
--- a/tests/test_s3.py
+++ b/tests/test_s3.py
@@ -1040,6 +1040,16 @@ def test_config_normalize_notification(self):
             },
         )
 
+    def test_config_handle_missing_attr(self):
+        # test for bug of
+        # https://github.com/cloud-custodian/cloud-custodian/issues/7808
+        event = event_data("s3-from-rule-sans-accelerator.json", "config")
+        p = self.load_policy({"name": "s3cfg", "resource": "s3"})
+        source = p.resource_manager.get_source("config")
+        resource_config = json.loads(event["invokingEvent"])["configurationItem"]
+        resource = source.load_resource(resource_config)
+        assert resource['Name'] == 'c7n-fire-logs'
+
     def test_config_normalize_lifecycle_null_predicate(self):
         event = event_data("s3-lifecycle-null-predicate.json", "config")
         p = self.load_policy({"name": "s3cfg", "resource": "s3"})