# Buggy code
def process_resource(self, client, r, related_tags, tag_keys, tag_action):
    tags = {}
    resource_tags = {
        t["Key"]: t["Value"]
        for t in r.get("Tags", [])
        if not t["Key"].startswith("aws:")
    }

    if tag_keys == "*":
        tags = {k: v for k, v in related_tags.items() if resource_tags.get(k) != v}
    else:
        tags = {
            k: v
            for k, v in related_tags.items()
            if k in tag_keys and resource_tags.get(k) != v
        }
    if not tags:
        return
    tag_action.process_resource_set(
        client, resource_set=[r], tags=[{"Key": k, "Value": v} for k, v in tags.items()]
    )
    return True


# Traceback (botocore.exceptions.ParamValidationError)
policies:
    - name: copy-tags-from-api-to-stage
      resource: rest-stage
      actions:
        - type: copy-related-tag
          resource: rest-api
          skip_missing: True
          key: restApiId
          tags: '*'


(cc) root@ip-10-128-128-174:~# custodian run --assume arn:aws:iam::1234567890:role/Cloud_Custodian_Role -s . kristen-test-api-gateway.yaml -r us-east-1 --cache-period 0 -v --debug
2020-02-21 17:51:47,332: custodian.cache:DEBUG Disabling cache
2020-02-21 17:51:47,333: custodian.commands:DEBUG Loaded file kristen-test-api-gateway.yaml. Contains 1 policies
2020-02-21 17:51:47,458: custodian.output:DEBUG Storing output with <LogFile file://./copy-tags-from-api-to-stage/custodian-run.log>
2020-02-21 17:51:47,545: custodian.policy:DEBUG Running policy:copy-tags-from-api-to-stage resource:rest-stage region:us-east-1 c7n:0.8.46.1
2020-02-21 17:51:47,668: custodian.resources.restapi:DEBUG Filtered from 4 to 4 restapi
2020-02-21 17:51:47,946: custodian.resources.reststage:DEBUG Filtered from 4 to 4 reststage
2020-02-21 17:51:47,947: custodian.policy:INFO policy:copy-tags-from-api-to-stage resource:rest-stage region:us-east-1 count:4 time:0.40
2020-02-21 17:51:48,045: custodian.output:DEBUG metric:ResourceCount Count:4 policy:copy-tags-from-api-to-stage restype:rest-stage scope:policy
2020-02-21 17:51:48,046: custodian.output:DEBUG metric:PolicyException Count:1 policy:copy-tags-from-api-to-stage restype:rest-stage
2020-02-21 17:51:48,046: custodian.output:DEBUG metric:ApiCalls Count:6 policy:copy-tags-from-api-to-stage restype:rest-stage
2020-02-21 17:51:48,046: custodian.output:ERROR Error while executing policy
Traceback (most recent call last):
  File "/root/cc/lib/python3.6/site-packages/c7n/policy.py", line 320, in run
    results = a.process(resources)
  File "/root/cc/lib/python3.6/site-packages/c7n/tags.py", line 1050, in process
    client, r, related_tag_map[related], self.data['tags'], tag_action):
  File "/root/cc/lib/python3.6/site-packages/c7n/tags.py", line 1075, in process_resource
    tags=[{'Key': k, 'Value': v} for k, v in tags.items()])
  File "/root/cc/lib/python3.6/site-packages/c7n/tags.py", line 860, in process_resource_set
    client.tag_resources, ResourceARNList=arns, Tags=tags)
  File "/root/cc/lib/python3.6/site-packages/c7n/tags.py", line 1117, in universal_retry
    response = method(ResourceARNList=ResourceARNList, **kw)
  File "/root/cc/lib/python3.6/site-packages/botocore/client.py", line 276, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/root/cc/lib/python3.6/site-packages/botocore/client.py", line 559, in _make_api_call
    api_params, operation_model, context=request_context)
  File "/root/cc/lib/python3.6/site-packages/botocore/client.py", line 607, in _convert_to_request_dict
    api_params, operation_model)
  File "/root/cc/lib/python3.6/site-packages/botocore/validate.py", line 297, in serialize_to_request
    raise ParamValidationError(report=report.generate_report())
botocore.exceptions.ParamValidationError: Parameter validation failed:
Invalid type for parameter Tags, value: [{'Key': 'BillingCostCenter', 'Value': '05a'}, {'Key': 'Division', 'Value': 'test'}, {'Key': 'Environment', 'Value': 'dev'}, {'Key': 'ResourceContact', 'Value': 'kristen.happ@company.com'}, {'Key': 'ResourcePurpose', 'Value': 'Testing Tagging Policy'}], type: <class 'list'>, valid types: <class 'dict'>
Traceback (most recent call last):
  File "/root/cc/lib/python3.6/site-packages/c7n/cli.py", line 374, in main
    command(config)
  File "/root/cc/lib/python3.6/site-packages/c7n/commands.py", line 141, in _load_policies
    return f(options, list(policies))
  File "/root/cc/lib/python3.6/site-packages/c7n/commands.py", line 283, in run
    policy()
  File "/root/cc/lib/python3.6/site-packages/c7n/policy.py", line 1049, in __call__
    resources = mode.run()
  File "/root/cc/lib/python3.6/site-packages/c7n/policy.py", line 320, in run
    results = a.process(resources)
  File "/root/cc/lib/python3.6/site-packages/c7n/tags.py", line 1050, in process
    client, r, related_tag_map[related], self.data['tags'], tag_action):
  File "/root/cc/lib/python3.6/site-packages/c7n/tags.py", line 1075, in process_resource
    tags=[{'Key': k, 'Value': v} for k, v in tags.items()])
  File "/root/cc/lib/python3.6/site-packages/c7n/tags.py", line 860, in process_resource_set
    client.tag_resources, ResourceARNList=arns, Tags=tags)
  File "/root/cc/lib/python3.6/site-packages/c7n/tags.py", line 1117, in universal_retry
    response = method(ResourceARNList=ResourceARNList, **kw)
  File "/root/cc/lib/python3.6/site-packages/botocore/client.py", line 276, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/root/cc/lib/python3.6/site-packages/botocore/client.py", line 559, in _make_api_call
    api_params, operation_model, context=request_context)
  File "/root/cc/lib/python3.6/site-packages/botocore/client.py", line 607, in _convert_to_request_dict
    api_params, operation_model)
  File "/root/cc/lib/python3.6/site-packages/botocore/validate.py", line 297, in serialize_to_request
    raise ParamValidationError(report=report.generate_report())
botocore.exceptions.ParamValidationError: Parameter validation failed:
Invalid type for parameter Tags, value: [{'Key': 'BillingCostCenter', 'Value': '05a'}, {'Key': 'Division', 'Value': 'test'}, {'Key': 'Environment', 'Value': 'dev'}, {'Key': 'ResourceContact', 'Value': 'kristen.happ@company.com'}, {'Key': 'ResourcePurpose', 'Value': 'Testing Tagging Policy'}], type: <class 'list'>, valid types: <class 'dict'>
> /root/cc/lib/python3.6/site-packages/botocore/validate.py(297)serialize_to_request()
-> raise ParamValidationError(report=report.generate_report())
(Pdb)


(cc) root@ip-10-128-128-174:~# custodian version --debug

Please copy/paste the following info along with any bug reports:

Custodian:   0.8.46.1
Python:      3.6.9 (default, Nov  7 2019, 10:44:02)
             [GCC 8.3.0]
Platform:    posix.uname_result(sysname='Linux', nodename='ip-10-128-128-174', release='4.15.0-1060-aws', version='#62-Ubuntu SMP Tue Feb 11 21:23:22 UTC 2020', machine='x86_64')
Using venv:  False
Docker: False
PYTHONPATH:
[            '/root/cc/bin',
             '/usr/lib/python36.zip',
             '/usr/lib/python3.6',
             '/usr/lib/python3.6/lib-dynload',
             '/root/cc/lib/python3.6/site-packages']

(cc) root@ip-10-128-128-174:~# pip3 show boto3
Name: boto3
Version: 1.11.7
Summary: The AWS SDK for Python
Home-page: https://github.com/boto/boto3
Author: Amazon Web Services
Author-email: UNKNOWN
License: Apache License 2.0
Location: /root/cc/lib/python3.6/site-packages
Requires: s3transfer, jmespath, botocore


# Fixed code
def process_resource(self, client, r, related_tags, tag_keys, tag_action):
    tags = {}
    resource_tags = {
        t["Key"]: t["Value"]
        for t in r.get("Tags", [])
        if not t["Key"].startswith("aws:")
    }

    if tag_keys == "*":
        tags = {k: v for k, v in related_tags.items() if resource_tags.get(k) != v}
    else:
        tags = {
            k: v
            for k, v in related_tags.items()
            if k in tag_keys and resource_tags.get(k) != v
        }
    if not tags:
        return
    if not isinstance(tag_action, UniversalTag):
        tags = [{"Key": k, "Value": v} for k, v in tags.items()]
    tag_action.process_resource_set(client, resource_set=[r], tags=tags)
    return True