Example #1
0
def detach_managed_policy_from_role(role_name, policy_name):
    ''' Detach a managed policy from a role

        Arguments:
            - role_name: Role name to attach policy to (name, not ARN)
            - policy_name: Managed policy name to attach to policy (not ARN)

        Returns: True if successful

         Note: Sometime if the attach fails (like role does not exist)
               a botocore exception is raised. A future improvement
               will be to catch this and return an error code
    '''
    managed_policy_arn = make_managed_policy_arn(policy_name)

    # Connect to IAM
    iam_client = boto3.client('iam')

    # Detach the policy from the role
    logging.info(f'Detaching policy: {policy_name} from role: {role_name}')
    resp = iam_client.detach_role_policy(RoleName=role_name,
                                         PolicyArn=managed_policy_arn)
    aws_common_utils.check_response_status(resp, check_failure=True)

    # If it falls through, then return success
    return True, {}
Example #2
0
def attach_inline_policy_to_role(role_name, policy_name, policy_document):
    ''' Attach an inline policy to the role

        Arguments:
            - role_name: name of role, a string (not ARN)
            - policy_name: name of policy, a string (not ARN)
            - policy_document: A JSON of the ploic document.
                               See below (or in tests) for example
       Return:
           - True if attach succeds

             Note: Sometime if the attach fails (like role does not exist)
                   a botocore exception is raised. A future improvement
                   will be to catch this and return an error code
    '''

    # Connect to IAM
    iam_client = boto3.client('iam')

    logging.info(
        f'Attaching inline policy: {policy_name} to role: {role_name}')

    resp = iam_client.put_role_policy(RoleName=role_name,
                                      PolicyName=policy_name,
                                      PolicyDocument=policy_document)

    aws_common_utils.check_response_status(resp, check_failure=True)

    # If it falls through, then return success
    return True, {}
Example #3
0
def list_policies(policy_scope='Local',
                  only_attached=True,
                  path_prefix='/',
                  policy_usage_filter='PermissionsPolicy'):
    ''' Return a list of policies

      Arguments:
        - policy_scope: Choices are:
                           All: All policies
                           AWS: All AWS managed policies
                           Local: All customer managed policies (default)
        - only_attached: If True, return only attached policies. If False
                         return all policies (True is default)
        - path_prefix: The path prefix for filtering the results. Default is '/'
        - policy_usage_filter: Choices are:
                  - PermissionsPolicy: Filter only permissions policy (default) 
                  - PermissionsBoundary: Filter only policies set for
                                         permissions boundary

      Returns:
          - policy_names: A list of policy names
          - policy_arns: A list of dicts with ploicy_name: policy_arn
          - full_response: Full response of the API call
    
    #### Note: Pagination is not being used for results. As such, this
               call can handle only upto 100 policies
    '''
    # Connect to IAM
    iam_client = boto3.client('iam')

    # Call List policies API
    logging.info('Getting list of policies from AWS')
    resp = iam_client.list_policies(Scope=policy_scope,
                                    OnlyAttached=only_attached,
                                    PathPrefix=path_prefix,
                                    PolicyUsageFilter=policy_usage_filter)
    aws_common_utils.check_response_status(resp, check_failure=True)

    if resp['IsTruncated']:
        logging.error('List policies returned truncated response. '
                      'Returning all policies that were returned '
                      'Time to switch to pagination . . . ')

    # Get full list of policies
    policies_list_full = resp['Policies']
    # Then get other needed items
    policies_names_list = [x['PolicyName'] for x in policies_list_full]
    # Do a dict comprehension to return policy_name: policy_arn
    policies_name_arn_dict = {
        x['PolicyName']: x['Arn']
        for x in policies_list_full
    }

    return policies_names_list, policies_name_arn_dict, resp
Example #4
0
def delete_role(role_name):
    ''' Delete Role

        Arguments:
            - role_name: Role name to be deleted

        Returns:
            - resp: Full response (minus metadata)
    '''

    # Connect to IAM
    iam_client = boto3.client('iam')

    try:
        resp = iam_client.delete_role(RoleName=role_name)
    except iam_client.exceptions.NoSuchEntityException:
        logging.error(f'IAM: Delete Role: {role_name} does not exist')
        # Construct response
        err_code = aws_error_codes.AWS_IAM_ROLE_DOES_NOT_EXIST
        resp = aws_error_codes.construct_response(err_code)
        return False, resp

    # Check response
    sbool, scode = aws_common_utils.check_response_status(resp)
    if not sbool:
        logging.info(f'AWS API call to IAM delete roles failed with code: '
                     f'{scode}')
        raise aws_exceptions.AWS_API_CallFailed

    err_code = aws_error_codes.AWS_NO_ERROR
    resp = aws_error_codes.construct_response(err_code)
    return True, resp
Example #5
0
def list_s3_buckets(aws_region=aws_settings.AWS_DEFAULT_REGION):
    ''' List all buckets in requested region

        Arguments:
            - aws_region: List buckets in this region
                    Note: Currently only the AWS_DEFAULT_REGION is implemented
        Returns:
            - A list of bucket names
    '''

    # If a region other than default region is specified, raise an error
    if aws_region != aws_settings.AWS_DEFAULT_REGION:
        raise aws_exceptions.AWS_RegionNotImplemented

    s3_client = boto3.client('s3')
    response = s3_client.list_buckets()
    # Check if call succeded
    status_bool, status_code = aws_common_utils.check_response_status(response)
    if not status_bool:
        logging.info(f'AWS API call to S3 list buckets failed with code: '
                     f'{status_code}')
        raise aws_exceptions.AWS_API_CallFailed
    full_list = response['Buckets']
    names_list = [x['Name'] for x in full_list]
    return names_list, full_list
Example #6
0
def delete_table(table_name, aws_region=aws_settings.AWS_DEFAULT_REGION):
    ''' Delete a DynamoDB table

        Arguments:
            - aws_region: AWS region from which to list tables
                          Currently unused
            - table_name: Table name to delete

        Returns:
            - deleted_table_name: Name of deleted table (taken from response)
            - full_response: The full JSON response of the AWS call
                             (minus the response metadata)
                             This appears to be the table description
    '''
    dyndb_client = boto3.client('dynamodb')

    resp = dyndb_client.delete_table(TableName=table_name)
    sbool, scode = aws_common_utils.check_response_status(resp)
    if not sbool:
        logging.info(f'AWS API call to delete DynamoDB table failed '
                     f'with code: {scode}')
        raise aws_exceptions.AWS_API_CallFailed

    deleted_table_name = resp['TableDescription']['TableName']

    return deleted_table_name, resp['TableDescription']
Example #7
0
def list_functions(aws_region=aws_settings.AWS_DEFAULT_REGION):
    ''' List all functions in requested region

        Arguments:
            - aws_region: List buckets in this region. 'ALL' can also be used
        Returns:
            - A tuple with:
                - index 0 being a list of function named
                - index 1 being a list of the entire response
    '''
    func_name_list = []
    func_full_list = []
    lambda_client = boto3.client('lambda')

    # pagination is used in case there are > 1000 functions
    paginator = lambda_client.get_paginator('list_functions')
    # For some reason, specifying MasterRegion is paginate call returns no
    # functions. Debug later
    # pages = paginator.paginate(MasterRegion=aws_region, FunctionVersion='ALL')
    pages = paginator.paginate()

    for page in pages:
        # Check if call succeded
        sbool, scode = aws_common_utils.check_response_status(page)
        if not sbool:
            logging.info(f'AWS API call to list lambda functions failed with '
                         f'code: {scode}')
            raise aws_exceptions.AWS_API_CallFailed
        for func in page['Functions']:
            func_name_list.append(func['FunctionName'])
            func_full_list.append(func)
    return func_name_list, func_full_list
Example #8
0
def attach_managed_policy_to_role(role_name, policy_name):
    ''' Attach a managed policy to a role

        Arguments:
            - role_name: Role name to attach policy to (name, not ARN)
            - policy_name: Managed policy name to attach to policy (not ARN)

        Returns: True if successful
    '''
    managed_policy_arn = make_managed_policy_arn(policy_name)

    # Connect to IAM
    iam_client = boto3.client('iam')

    # Attach the policy to the role
    logging.info(f'Attaching policy: {policy_name} to role: {role_name}')
    resp = iam_client.attach_role_policy(RoleName=role_name,
                                         PolicyArn=managed_policy_arn)
    aws_common_utils.check_response_status(resp, check_failure=True)

    # If it falls through, then return success
    return True, {}
Example #9
0
def create_role(role_name,
                trust_policy,
                path='/',
                description='',
                max_session_duration=3600,
                tags={}):
    ''' Create an IAM Role

        Arguments:
            - role_name: Name of the role
            - trust_policy: Trust relationship policy document (JSON string)
            - path: Path of the role. Defaults to /
            - description: Obvious
            - max_session_duration: In seconds. Default is 60 mins
            - tags: Tags in list of dict format

        Returns:
           - role_arn: Role ARN
           - full_response: The full response (minus the response meta data)
    '''
    # Connect to IAM
    iam_client = boto3.client('iam')

    # Create role
    try:
        logging.info(f'IAM: Creating Role: {role_name}')
        resp = iam_client.create_role(
                   RoleName = role_name,
                   AssumeRolePolicyDocument = trust_policy,
                   Path = path,
                   Description = description,
                   MaxSessionDuration = max_session_duration if     \
                                        max_session_duration else None,
                   Tags = tags
               )
    except iam_client.exceptions.EntityAlreadyExistsException:
        logging.error(f'IAM: Create Role: {role_name} already exists')
        # Construct response
        err_code = aws_error_codes.AWS_IAM_ROLE_ALREADY_EXISTS
        resp = aws_error_codes.construct_response(err_code)
        return False, resp

    # Check response
    sbool, scode = aws_common_utils.check_response_status(resp)
    if not sbool:
        logging.info(f'AWS API call to IAM create role failed with code: '
                     f'{scode}')
        raise aws_exceptions.AWS_API_CallFailed

    return resp['Role']['Arn'], resp
Example #10
0
def delete_policy(policy_name):
    ''' Delete the policy specified by policy_name

        Note 1: Policy must be detached and all versions deleted before
                the policy can be deleted
        Note 2: Need to provide the policy arn here. You can get policy
                arn from get_policy_arn call 
    '''

    # Get policy arn from policy name
    policy_arn = get_policy_arn(policy_name)
    # If policy name does not exist, return error
    if not policy_arn:
        logging.error(f'IAM: Delete Policy: {policy_name} does not exist')
        # Construct response
        err_code = aws_error_codes.AWS_IAM_POLICY_DOES_NOT_EXIST
        resp = aws_error_codes.construct_response(err_code)
        return False, resp

    # Connect to IAM
    iam_client = boto3.client('iam')

    try:
        logging.info(f'Deleting policy ARN: {policy_arn}')
        resp = iam_client.delete_policy(PolicyArn=policy_arn)
        aws_common_utils.check_response_status(resp, check_failure=True)
    except iam_client.exceptions.NoSuchEntityException:
        logging.error(f'IAM: Delete Policy: {policy_arn} does not exist')
        # Construct response
        err_code = aws_error_codes.AWS_IAM_POLICY_DOES_NOT_EXIST
        resp = aws_error_codes.construct_response(err_code)
        return False, resp

    # If above call does not raise an exception, then call has succeded
    # return OK

    return True, {}
Example #11
0
def create_policy(policy_name, policy_document, path='/', description=''):
    ''' Create a policy named: policy_name with the specification
        in: policy_document 

        Arguments:
            - policy_name: Name of the policy to create. A string
            - policy_document: A JSON string that specified the policy.
                               See below (or in tests) for an example
            - path: The path where policy resides. can be any valid path.
            - description: Description of the policy. A string

        Returns:
            - policy_arn: The ARN of the created policy. Null if failed
            - full_response: The full response to the API call (minus metadata)
    '''

    # Connect to IAM
    iam_client = boto3.client('iam')

    try:
        # Now create the policy
        logging.info(f'Creating policy named {policy_name}')
        resp = iam_client.create_policy(PolicyName=policy_name,
                                        PolicyDocument=policy_document,
                                        Path=path,
                                        Description=description)
        aws_common_utils.check_response_status(resp, check_failure=True)

    except iam_client.exceptions.EntityAlreadyExistsException:
        logging.error(f'IAM: Create Policy: {policy_name} already exists')
        # Construct response
        err_code = aws_error_codes.AWS_IAM_POLICY_ALREADY_EXISTS
        resp = aws_error_codes.construct_response(err_code)
        return False, resp

    # Return the created policy ARN and the full response
    return resp['Policy']['Arn'], resp
Example #12
0
def create_s3_bucket(bucket_name,
                     ACL='',
                     CreateBucketConfiguration=DEFAULT_BUCKET_REGION):
    ''' Create an S3 bucket if it does not already exist

        Arguments:
          - bucket_name: Bucket name (string)
          - ACL: Access control. See boto3 documentation
          - CreateBucketConfiguration: For more fine control. 
                                       See boto3 documentation 
    '''
    # First check if bucket already exists
    buckets, _ = list_s3_buckets()
    if bucket_name in buckets:
        logging.info(f'Bucket name: {bucket_name} already exists.')
        return aws_s3_settings.S3_BUCKET_ALREADY_EXISTS
    else:
        logging.info(f'Creating bucket: {bucket_name}')
        s3_client = boto3.client('s3')
        try:
            # Try creating a bucket with given name
            response = s3_client.create_bucket(
                Bucket=bucket_name,
                CreateBucketConfiguration=CreateBucketConfiguration)
        except ClientError as e:
            # If bucket name exists in region (for any user), boto3 throws
            # a BucketAlreadyExists error and below seems to be the standard
            # way to handle this
            if e.response['Error']['Code'] == 'BucketAlreadyExists':
                # Then print the standard exception as it appears to have
                # the best explanation
                logging.error(f'Bucket name: {bucket_name} already exists '
                              'in this region')
                logging.error(e)
            else:
                logging.error('Unexpected error occured')
                logging.error(e)
            return aws_s3_settings.S3_CREATE_BUCKET_FAILED
        sbool, scode = aws_common_utils.check_response_status(response)
        if not sbool:
            logging.info(f'AWS API call to S3 list buckets failed with code: '
                         f'{scode}')
            raise aws_exceptions.AWS_API_CallFailed
            return aws_s3_settings.S3_CREATE_BUCKET_FAILED
        else:
            logging.info(f'Successfully created bucket: {bucket_name}')
            return aws_s3_settings.S3_CREATE_BUCKET_SUCCESS
Example #13
0
def delete_s3_bucket(bucket_name, aws_region=aws_settings.AWS_DEFAULT_REGION):
    ''' Delete bucket in requested region

        Arguments:
            - bucket_name: name of bucket to delete
            - aws_region: Delete bucket in this region
                    Note: Currently only the AWS_DEFAULT_REGION is implemented
        Returns:
            - Success or error code
    '''

    # If a region other than default region is specified, raise an error
    if aws_region != aws_settings.AWS_DEFAULT_REGION:
        raise aws_exceptions.AWS_RegionNotImplemented

    s3_client = boto3.client('s3')
    try:
        response = s3_client.delete_bucket(Bucket=bucket_name)
    except ClientError as e:
        # If an error is thrown, then this is standard way to handle this
        if e.response['Error']['Code'] == 'NoSuchBucket':
            # Then print the standard exception as it appears to have
            # the best explanation
            logging.error(f'No such bucket name: {bucket_name}')
            logging.error(e)
        else:
            logging.error('Unexpected error occured')
            logging.error(e)
        return aws_s3_settings.S3_BUCKET_DELETE_FAILED

    # Check if call succeded
    status_bool, status_code = aws_common_utils.check_response_status(response)
    if not status_bool:
        logging.info(f'AWS API call to S3 list buckets failed with code: '
                     f'{status_code}')
        raise aws_exceptions.AWS_API_CallFailed
        return aws_s3_settings.S3_BUCKET_DELETE_FAILED
    else:
        logging.info(f'Bucket: {bucket_name} successfully deleted')
        return aws_s3_settings.S3_BUCKET_DELETE_SUCCESS
Example #14
0
def describe_table(table_name, aws_region=aws_settings.AWS_DEFAULT_REGION):
    ''' Describe a table in AWS DynamoDB

        Arguments:
            - aws_region: AWS region from which to list tables
                          Currently unused
            - table_name: Name of table to describe

        Returns:
            - table_description: JSON describing the table. Pretty much the
                                 JSON returned by the AWS boto3 call
    '''
    dyndb_client = boto3.client('dynamodb')

    resp = dyndb_client.describe_table(TableName=table_name)
    sbool, scode = aws_common_utils.check_response_status(resp)
    if not sbool:
        logging.info(f'AWS API call to describe DynamoDB table failed '
                     f'with code: {scode}')
        raise aws_exceptions.AWS_API_CallFailed

    return resp['Table']
Example #15
0
def get_role_arn(role_name):
    ''' Get the role specified by role_name

        Arguments:
            - role_name: Name of the role
        Returns:
            - role_arn: being the ARN name
            - full_response: being the full response
    '''
    # Connect to IAM and query the role
    iam_client = boto3.client('iam')
    resp = iam_client.get_role(RoleName=role_name)

    # Check if request worked
    sbool, scode = aws_common_utils.check_response_status(resp)
    if not sbool:
        logging.info(f'AWS API call to IAM get role failed with code: '
                     f'{scode}')
        raise aws_exceptions.AWS_API_CallFailed

    # Split up and return the response
    return resp['Role']['Arn'], resp
Example #16
0
def list_roles(path_prefix='/'):
    ''' List IAM Roles

        ***** Mega Note: Pagination is not being used in this version.
                         So if the number of roles is > 100, it will
                         be truncated to 100 ******
        Arguments:
            - path_prefix: The path prefix foir filtering results

        Returns:
            - roles_list: A list with role names
            - roles_dict: A dict keyed by role names with the attribute of
                          said role as values
            - resp: Full response (minus metadata)
    '''

    # Connect to IAM
    iam_client = boto3.client('iam')

    resp = iam_client.list_roles(PathPrefix=path_prefix)

    # Check response
    sbool, scode = aws_common_utils.check_response_status(resp)
    if not sbool:
        logging.info(f'AWS API call to IAM list roles failed with code: '
                     f'{scode}')
        raise aws_exceptions.AWS_API_CallFailed

    # Now make a distionary of roles
    roles_list_of_dicts = resp['Roles']
    roles_dict = {x['RoleName']: x for x in roles_list_of_dicts}

    roles_list = list(roles_dict.keys())

    # Delete the metata from response before returning
    del resp['ResponseMetadata']

    return roles_list, roles_dict, resp
Example #17
0
def list_tables(aws_region=aws_settings.AWS_DEFAULT_REGION):
    ''' List all DynamoDB in current AWS region
        ###### Mega Note: ######
            This code does not use pagination. As such, the first 100
            tables *ONLY* are returned. If there are more than 100 tables,
            code needs to be extended like the list lambda functions

        Arguments:
            - aws_region: AWS region from which to list tables
                          Currently unused
        Returns:
            - table_names: A list of table names
    '''
    dyndb_client = boto3.client('dynamodb')

    resp = dyndb_client.list_tables()
    sbool, scode = aws_common_utils.check_response_status(resp)
    if not sbool:
        logging.info(f'AWS API call to list DynamoDB table failed '
                     f'with code: {scode}')
        raise aws_exceptions.AWS_API_CallFailed

    return resp['TableNames']
Example #18
0
def get_function(func_name):
    ''' Get information about func_name

        Arguments:
            - func_name: Name of the function for which information is requested
        Returns: A dict with keys:
             - Configuration: Which has info line func ARN, runtime, mem size
             - Code: The (zipped) function code. 
                     I beleive this is valid for 10 min
             - Tags: Tags for the function
    '''
    lambda_client = boto3.client('lambda')
    resp = lambda_client.get_function(FunctionName=func_name)
    # Check for the response
    sbool, scode = aws_common_utils.check_response_status(resp)
    if not sbool:
        logging.info(f'AWS API call to list lambda functions failed with '
                     f'code: {scode}')
        raise aws_exceptions.AWS_API_CallFailed

    # If call succeded, simply delete the 'ResponseMetadata' key from the
    # response dict and return the remaining
    del (resp['ResponseMetadata'])
    return (resp)
Example #19
0
def create_table(table_name,
                 table_schema,
                 primary_key,
                 secondary_key=None,
                 billing_mode='PAY_PER_REQUEST',
                 other_attributes={},
                 aws_region=aws_settings.AWS_DEFAULT_REGION):
    ''' Create a DynamoDB table

        Arguments:
            - table_name:  Name of table. Name needs to follow AWS name rules
            - table_schema: The schema of the table specified as a list of
                            dicts. See example way below in the file.
                            At the very least, the primary (and the optional 
                            secondary) key schemas need to be defines
            - primary_key:  The indexing key
            - secondary_key: The optional sorting key
            - billing_mode: Default is PAY_PER_REQUEST. Set to a dictionary
                            per API if other modes are needed
            - other_attributes: Other attributes as a dict. Currently not
                                implemented
        Returns:
            - table_arn: The ARN of the created table
            - table_id: The ID of the created table
            - table_description: The full JSON response from AWS 
                                 (minus the response metadata)
    '''
    dyndb_client = boto3.client('dynamodb')

    # Define the attributes definition dict outside the create_table call.
    # This is because primary and secondary key types have to be specified
    # and then this dict updated with any other attributes
    # Also making implicit asumption here that pri key is string and
    # sec key is numerical. More coding needed if more flexibiliuty is needed

    # Now update it with any other attributes
    # attrs_definition.update(other_attributes)

    table = dyndb_client.create_table(
        TableName=table_name,
        KeySchema=[
            {
                'AttributeName': primary_key,
                'KeyType': 'HASH'  # HASH = primary key in AWS land
            },
            {
                'AttributeName': secondary_key,
                'KeyType': 'RANGE'  # RANG = sort key in AWS land
            } if secondary_key else {}
        ],
        AttributeDefinitions=[{
            'AttributeName': primary_key,
            'AttributeType': 'S'
        }, {
            'AttributeName': secondary_key,
            'AttributeType': 'N'
        } if secondary_key else {}],
        BillingMode=billing_mode)

    sbool, scode = aws_common_utils.check_response_status(table)
    if not sbool:
        logging.info(f'AWS API call to create DynamoDB '
                     f'table {table_name} failed with code: {scode}')
        raise aws_exceptions.AWS_API_CallFailed

    # Return the table ARN, table ID and the entire response
    table_arn = table['TableDescription']['TableArn']
    table_id = table['TableDescription']['TableId']
    return table_arn, table_id, table['TableDescription']