Repository: cloud-custodian/cloud-custodian

Issue:
aws - [ERROR] KeyError: 'BillingModeSummary'- DynamoDB Custodian Policies with mode Config-rule fails 
**Describe the bug**
After deploying the DynamoDB Tag Compliance policy and Encryption key check with config-rule mode in the AWS environment, I am encountering an error listed below:

[ERROR] KeyError: 'BillingModeSummary'
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 438, in run
    resources = self.resolve_resources(event)
  File "/var/task/c7n/policy.py", line 836, in resolve_resources
    return [source.load_resource(self.cfg_event['configurationItem'])]
  File "/var/task/c7n/resources/dynamodb.py", line 25, in load_resource
    if 'LastUpdateToPayPerRequestDateTime' in resource['BillingModeSummary']:

**To Reproduce**
Steps to reproduce the behavior:

1. Deploy this cloudformation template to create a dynamodb table, set the billing mode to "PROVISIONED"
[dynamodb.yaml.zip](https://github.com/cloud-custodian/cloud-custodian/files/5489416/dynamodb.yaml.zip)
Note: **If you have billing mode to "PAY_PER_REQUEST", it works as per expectation because then the BillingModeSummary is passed in config event. However, for billing mode  "PROVISIONED" theBillingModeSummary is not passed hence the custodian code doesn't get any value for that key.**

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 DynamoDB with missing required tags as Non-compliant resource.
Also, it should flag  DynamoDB with missing KMSKeyId as Non-compliant resource.

**Background (please complete the following information):**
 - Python Version: 3.8.5
 - Custodian Version: 0.9.7
 - Cloud Provider: aws
 - Policy: 
```
Tag check: 
---
policies:
  - name: d-dynamodb-tags
    resource: aws.dynamodb-table
    description: "Type: Config Rule. Compliance: Tag Validation."
    mode:
      type: config-rule
      tags:
        RolePrefix: dynamodbDetective
    filters:
      - and:
          - or:
              - tag:abc: absent
              - tag:def: absent
              - tag:ApplicationName: absent
          - tag:DynamoDBTags: absent
```

```
Encryption Check
---
policies:
  - name: d-dynamodb-encryption-check
    resource: aws.dynamodb-table
    description: "Type: Config Rule. Compliance: KMS Validation."
    mode:
      type: config-rule
      tags:
        RolePrefix: dynamodbDetective
    filters:
      - and:
          - type: value
            key: SSEDescription.KMSMasterKeyArn
            value: absent
          - tag:__Exception-DynamoEncryption: absent
```
 - `custodian version --debug` output:
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

**Additional context**
Here are the cloudwatch log files for both policies lambda execution.
[DynamoDBEncryptionCWLogs.docx](https://github.com/cloud-custodian/cloud-custodian/files/5489734/DynamoDBEncryptionCWLogs.docx)
[DynamoDBTagsPolicyCWLogs.docx](https://github.com/cloud-custodian/cloud-custodian/files/5489735/DynamoDBTagsPolicyCWLogs.docx)


aws - [ERROR] KeyError: 'BillingModeSummary'- DynamoDB Custodian Policies with mode Config-rule fails 
**Describe the bug**
After deploying the DynamoDB Tag Compliance policy and Encryption key check with config-rule mode in the AWS environment, I am encountering an error listed below:

[ERROR] KeyError: 'BillingModeSummary'
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 438, in run
    resources = self.resolve_resources(event)
  File "/var/task/c7n/policy.py", line 836, in resolve_resources
    return [source.load_resource(self.cfg_event['configurationItem'])]
  File "/var/task/c7n/resources/dynamodb.py", line 25, in load_resource
    if 'LastUpdateToPayPerRequestDateTime' in resource['BillingModeSummary']:

**To Reproduce**
Steps to reproduce the behavior:

1. Deploy this cloudformation template to create a dynamodb table, set the billing mode to "PROVISIONED"
[dynamodb.yaml.zip](https://github.com/cloud-custodian/cloud-custodian/files/5489416/dynamodb.yaml.zip)
Note: **If you have billing mode to "PAY_PER_REQUEST", it works as per expectation because then the BillingModeSummary is passed in config event. However, for billing mode  "PROVISIONED" theBillingModeSummary is not passed hence the custodian code doesn't get any value for that key.**

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 DynamoDB with missing required tags as Non-compliant resource.
Also, it should flag  DynamoDB with missing KMSKeyId as Non-compliant resource.

**Background (please complete the following information):**
 - Python Version: 3.8.5
 - Custodian Version: 0.9.7
 - Cloud Provider: aws
 - Policy: 
```
Tag check: 
---
policies:
  - name: d-dynamodb-tags
    resource: aws.dynamodb-table
    description: "Type: Config Rule. Compliance: Tag Validation."
    mode:
      type: config-rule
      tags:
        RolePrefix: dynamodbDetective
    filters:
      - and:
          - or:
              - tag:abc: absent
              - tag:def: absent
              - tag:ApplicationName: absent
          - tag:DynamoDBTags: absent
```

```
Encryption Check
---
policies:
  - name: d-dynamodb-encryption-check
    resource: aws.dynamodb-table
    description: "Type: Config Rule. Compliance: KMS Validation."
    mode:
      type: config-rule
      tags:
        RolePrefix: dynamodbDetective
    filters:
      - and:
          - type: value
            key: SSEDescription.KMSMasterKeyArn
            value: absent
          - tag:__Exception-DynamoEncryption: absent
```
 - `custodian version --debug` output:
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

**Additional context**
Here are the cloudwatch log files for both policies lambda execution.
[DynamoDBEncryptionCWLogs.docx](https://github.com/cloud-custodian/cloud-custodian/files/5489734/DynamoDBEncryptionCWLogs.docx)
[DynamoDBTagsPolicyCWLogs.docx](https://github.com/cloud-custodian/cloud-custodian/files/5489735/DynamoDBTagsPolicyCWLogs.docx)




Discussion:
I'm going to take a stab at fixing this.  Initial triage is it looks like in line 25 that the fragment 
``` 
resource['BillingModeSummary']
```
assumes that attribute will always be present, which is no longer true.
I'm going to take a stab at fixing this.  Initial triage is it looks like in line 25 that the fragment 
``` 
resource['BillingModeSummary']
```
assumes that attribute will always be present, which is no longer true.

Resolving patch:
diff --git a/c7n/resources/dynamodb.py b/c7n/resources/dynamodb.py
--- a/c7n/resources/dynamodb.py
+++ b/c7n/resources/dynamodb.py
@@ -22,7 +22,8 @@ class ConfigTable(query.ConfigSource):
     def load_resource(self, item):
         resource = super(ConfigTable, self).load_resource(item)
         resource['CreationDateTime'] = datetime.fromtimestamp(resource['CreationDateTime'] / 1000.0)
-        if 'LastUpdateToPayPerRequestDateTime' in resource['BillingModeSummary']:
+        if ('BillingModeSummary' in resource and
+                'LastUpdateToPayPerRequestDateTime' in resource['BillingModeSummary']):
             resource['BillingModeSummary'][
                 'LastUpdateToPayPerRequestDateTime'] = datetime.fromtimestamp(
                     resource['BillingModeSummary']['LastUpdateToPayPerRequestDateTime'] / 1000.0)


Test patch: