Search code examples
pythonamazon-web-servicesiotboto3policy

AWS boto3 create_policy() - specify policyDocument


I am using python's boto3 library to interface to AWS IoT. I want to use create a policy using create_policy() API but I do not understand what to use for policyDocument field. I think it is related to the policyStatement, but I can't figure out the syntax. Here's what I have so far.

from __future__ import print_function
import os
import sys
import boto3
from botocore.exceptions import ClientError
from colorama import Fore, Back, Style
from colorama import init
init()

thingType = 'TpmStation'
thingBaseName = thingType + '-'
thingPolicy = thingType + '-Policy-GenDerivedKey'

def eprint(*args, **kwargs):
    print(*args, file=sys.stderr, **kwargs)

try:
    # Use system hosted credentials - see
    # http://docs.aws.amazon.com/cli/latest/userguide/installing.html
    # http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html
    client = boto3.client('iot')

    policyDocument = {}
    policyDocument['Statement'] = []
    policyDocument['Statement'].append({})
    policyDocument['Statement'][0]['Effect'] = 'Allow'
    policyDocument['Statement'][0]['Action'] = []
    policyDocument['Statement'][0]['Action'].append('iot:Connect')
    policyDocument['Statement'][0]['Action'].append('iot:Publish')
    policyDocument['Statement'][0]['Action'].append('iot:Subscribe')
    policyDocument['Statement'][0]['Action'].append('iot:Receive')
    policyDocument['Statement'][0]['Action'].append('iot:GetThingShadow')
    policyDocument['Statement'][0]['Action'].append('iot:UpdateThingShadow')
    policyDocument['Statement'][0]['Resource'] = '*'
    response = client.create_policy(
        policyName = thingPolicy,
        policyDocument = policyDocument
    )
    if 200 != response['ResponseMetadata']['HTTPStatusCode']:
        eprint(Fore.RED + "ERROR: Unable to 'create_thing_type' " + Style.RESET_ALL)
        sys.exit(1)
    print(Fore.GREEN + "Created new policy '" + thingPolicy + "'" +
            Style.RESET_ALL)

except ClientError as e:
    exc_type, exc_obj, exc_tb = sys.exc_info()
    fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
    eprint(Fore.RED + "ERROR in " + fname + ':' + str(exc_tb.tb_lineno) + ' - ' + e.response['Error']['Code'] + ' - ' + e.response['Error']['Message'] + Style.RESET_ALL)
    sys.exit(1)

Solution

  • After many iterations, here's what I found that works

    from __future__ import print_function
    import os
    import sys
    import re
    import boto3
    from botocore.exceptions import ClientError
    from colorama import Fore, Back, Style
    from colorama import init
    init()
    
    thingType = 'TpmStation'
    thingBaseName = thingType + '-'
    thingPolicy = thingType + '-Policy-GenDerivedKey'
    
    def eprint(*args, **kwargs):
        print(*args, file=sys.stderr, **kwargs)
    
    try:
        # Use system hosted credentials - see
        # http://docs.aws.amazon.com/cli/latest/userguide/installing.html
        # http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html
        client = boto3.client('iot')
    
        awsAccount = boto3.client('sts').get_caller_identity().get('Account')
        awsRegion = boto3.session.Session().region_name
        policyDocumentStr = '''
            {
                "Version": "2012-10-17",
                "Statement": [
                    {
                        "Effect": "Allow",
                        "Action": [
                            "iot:Publish"
                        ],
                        "Resource": ["arn:aws:iot:%s:%s:topic/Request"]
                    },
                    {
                        "Effect": "Allow",
                        "Action": [
                            "iot:Subscribe"
                        ],
                        "Resource": ["arn:aws:iot:%s:%s:topicfilter/Response"]
                    },
                    {
                        "Effect": "Allow",
                        "Action": [
                            "iot:Receive"
                        ],
                        "Resource": ["arn:aws:iot:%s:%s:topic/Response"]
                    },
                    {
                        "Effect": "Allow",
                        "Action": ["iot:Connect"],
                        "Resource": ["*"]
                    }
                ]
            }
        '''%(awsRegion, awsAccount, awsRegion, awsAccount, awsRegion, awsAccount)
        pattern = re.compile(r'[\s\r\n]+')
        policyDocumentStr = re.sub(pattern, '', policyDocumentStr)
    
        response = client.create_policy(
            policyName = thingPolicy,
            policyDocument = policyDocumentStr
        )
        if 200 != response['ResponseMetadata']['HTTPStatusCode']:
            eprint(Fore.RED + "ERROR: Unable to 'create_thing_type' " + Style.RESET_ALL)
            sys.exit(1)
        print(Fore.GREEN + "Created new policy '" + thingPolicy + "'" +
                Style.RESET_ALL)
    
    except ClientError as e:
        exc_type, exc_obj, exc_tb = sys.exc_info()
        fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
        eprint(Fore.RED + "ERROR in " + fname + ':' + str(exc_tb.tb_lineno) + ' - ' + e.response['Error']['Code'] + ' - ' + e.response['Error']['Message'] + Style.RESET_ALL)
        sys.exit(1)