Q:

boto3 giving AccessDenied, is there a way to lookup the missing permissions?

I've been trying to figure out why this step in my script isn't working, as far as I know, my user has permissions to do everything on our AWS account, so I'm a bit confused. I was wondering if there was a way you could get a return from botocore or boto3 itself identifying the missing iam permission for your user rather than figure out exactly the right permissions for the job you need to do it yourself?
Initially I had zone_id configured from here
>>> dns_record = connection.change_resource_record_sets(
...     HostedZoneId=zone_id,
...     ChangeBatch={
...         'Changes': [
...             {
...                 'Action': action,
...                 'ResourceRecordSet': {
...                     'Name': name,
...                     'Type': record_type,
...                     'TTL': 60,
...                     'AliasTarget': {
...                         'HostedZoneId': zone_id,
...                         'DNSName': destination,
...                         'EvaluateTargetHealth': eval_health
...                     },
...                 }
...             },
...         ]
...     }
... )

Traceback (most recent call last):
  File "<stdin>", line 14, in <module>
  File "/usr/lib/python2.7/site-packages/botocore/client.py", line 253, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/usr/lib/python2.7/site-packages/botocore/client.py", line 543, in _make_api_call
    raise error_class(parsed_response, operation_name)
botocore.exceptions.ClientError: An error occurred (AccessDenied) when calling the ChangeResourceRecordSets operation: User: arn:aws:iam::1337:user/rumbles is not authorized to access this resource
>>> 
>>> print connection, zone_id, action, name, record_type, destination, eval_health
<botocore.client.Route53 object at 0x7f9049d64590> Z117KPS5GTRQ2G UPSERT test-rumbles.domain.com A some-elb-1337.us-east-1.elb.amazonaws.com False

If I change zone_id to the Zone of our AWS domain I get:
>>> zone_id = "Z2401337RNHANU"
>>> 
>>> try:
...
An error occurred (InvalidInput) when calling the ChangeResourceRecordSets operation: Invalid request
{'ResponseMetadata': {'RetryAttempts': 0, 'HTTPStatusCode': 400, 'RequestId': '2bfde962-0e81-11e7-b312-53961dec3e91', 'HTTPHeaders': {'x-amzn-requestid': '2bfde962-0e81-11e7-b312-53961dec3e91', 'date': 'Tue, 21 Mar 2017 21:56:00 GMT', 'content-length': '259', 'content-type': 'text/xml', 'connection': 'close'}}, 'Error': {'Message': 'Invalid request', 'Code': 'InvalidInput', 'Type': 'Sender'}}

I've had to look in to these issues before, I know that I just have to find the right IAM permission for this particular job (assuming I am on the right path), but I was wondering if there is some way to get this information returned from botocore? Rather than spending my time manually having to look it up, can't I get the iam rule that denied me access returned by my except somehow?
Or am I talking about a feature request I haven't found/created yet?

A:

Your answer is coming from invalid zone id's - the first boto3 call of HostedZoneId="" needs to be the hosted zone ID of YOUR url and the second is referencing the alias target's hosted zone id. 
You'll get access denied if they're the same, as you're trying to change the records for an Amazon owned URL. 
From the docs http://boto3.readthedocs.io/en/latest/reference/services/route53.html#Route53.Client.change_resource_record_sets
First reference of HostedZoneId 
HostedZoneId (string) --
[REQUIRED]
The ID of the hosted zone that contains the resource record sets that you want to change.
Second reference of HostedZoneId - nested in AliasTarget
-HostedZoneId (string) -- [REQUIRED]
Alias resource records sets only : The value used depends on where you want to route traffic:
ELB load balancer
Specify the value of the hosted zone ID for the load balancer. Use the following methods to get the hosted zone ID:
Elastic Load Balancing table in the "AWS Regions and Endpoints" chapter of the Amazon Web Services General Reference : Use the value in the "Amazon Route 53 Hosted Zone ID" column that corresponds with the region that you created your load balancer in.
AWS Management Console : Go to the Amazon EC2 page, click Load Balancers in the navigation pane, select the load balancer, and get the value of the Hosted zone field on the Description tab.
Elastic Load Balancing API : Use DescribeLoadBalancers to get the value of CanonicalHostedZoneNameId . For more information, see the applicable guide:
Classic Load Balancer: DescribeLoadBalancers
Application Load Balancer: DescribeLoadBalancers
AWS CLI : Use describe-load-balancers to get the value of CanonicalHostedZoneNameID .
An Amazon S3 bucket configured as a static website
Specify the hosted zone ID for the region that you created the bucket in. For more information about valid values, see the Amazon Simple Storage Service Website Endpoints table in the "AWS Regions and Endpoints" chapter of the Amazon Web Services General Reference .
Your second issue - the 400 is coming from zone_id being the same again - so now that your first zone_id is correct - it's now looking for the AWS owned URL in YOUR url...
try spliting it up into like this
dns_record = connection.change_resource_record_sets(
HostedZoneId=my_url_zone_id,
ChangeBatch={
    'Changes': [
        {
            'Action': action,
            'ResourceRecordSet': {
                'Name': name,
                'Type': record_type,
                'AliasTarget': {
                    'HostedZoneId': elb_zone_id,
                    'DNSName': destination,
                    'EvaluateTargetHealth': eval_health
                },
            }
        },
    ]
}

where elb_zone_id = the zone_id of the elb and 
where my_url_zone_id = the zone_id of your specific URL

