示例#1
0
def parse_profiles(profiles):
    """
    Summary:
        Parse awscli profile_names given as parameter in 1 of 2 forms:
            1. single profilename given
            2. list of profile_names
        Also, function prepends profile_name(s) with a prefix when it detects
        profile_name refers to temp credentials in the local awscli configuration
    Args:
        profiles (str or file):  Profiles parameter can be either:
            - a single profile_name (str)
            - a file containing multiple profile_names, 1 per line
    Returns:
        - list of awscli profilenames, TYPE: list
        OR
        - single profilename, TYPE: str
    """

    profile_list = []

    try:
        if isinstance(profiles, list):
            return [_profile_prefix(x.strip()) for x in profiles]
        elif os.path.isfile(profiles):
            with open(profiles) as f1:
                for line in f1:
                    profile_list.append(_profile_prefix(line.strip()))
        else:
            return _profile_prefix(profiles.strip())
    except Exception as e:
        logger.exception(
            f'{inspect.stack()[0][3]}: Unknown error while converting profile_names from local awscli config: {e}'
        )
        raise
    return profile_list
示例#2
0
def client_wrapper(service, profile='default', region=DEFAULT_REGION):
    """
    Summary.

        Single caller boto3 service wrapper. Instantiates client object while
        using temporary credientials for profile_name, if available in
        local configuration. Tests authentication prior to returning any
        client object.

    Args:
        :service (str): boto3 service abbreviation ('ec2', 's3', etc)
        :profile (str): profile_name of an iam user from local awscli config
        :region (str):  AWS region code, optional

    Returns:
        client (boto3 object)

    """
    profile_name = _profile_prefix(profile)

    try:

        if authenticated(profile_name):
            return boto3_session(service=service,
                                 profile=profile_name,
                                 region=region)

    except ClientError as e:
        logger.exception(
            "%s: Unknown boto3 failure while establishing session (Code: %s Message: %s)"
            % (inspect.stack()[0][3], e.response['Error']['Code'],
               e.response['Error']['Message']))
    return None
示例#3
0
def _profile_prefix(profile, prefix='gcreds'):
    """
    Summary:
        Determines if temporary STS credentials provided via
        local awscli config;
        - if yes, returns profile with correct prefix
        - if no, returns profile (profile_name) unaltered
        - Note:  Caller is parse_profiles(), Not to be called directly
    Args:
        profile (str): profile_name of a valid profile from local awscli config
        prefix (str): prefix prepended to profile containing STS temporary credentials
    Returns:
        awscli profilename, TYPE str
    """

    stderr = ' 2>/dev/null'
    tempProfile = prefix + '-' + profile

    try:
        if subprocess.getoutput(
                f'aws configure get profile.{profile}.aws_access_key_id {stderr}'
        ):
            return profile
        elif subprocess.getoutput(
                f'aws configure get profile.{tempProfile}.aws_access_key_id {stderr}'
        ):
            return tempProfile
    except Exception as e:
        logger.exception(
            f'{inspect.stack()[0][3]}: Unknown error while interrogating local awscli config: {e}'
        )
        raise
    return None
示例#4
0
def authenticated(profile):
    """
    Summary:
        Tests generic authentication status to AWS Account
    Args:
        :profile (str): iam user name from local awscli configuration
    Returns:
        TYPE: bool, True (Authenticated)| False (Unauthenticated)
    """
    try:
        sts_client = boto3_session(service='sts', profile=profile)
        httpstatus = sts_client.get_caller_identity(
        )['ResponseMetadata']['HTTPStatusCode']
        if httpstatus == 200:
            return True

    except ClientError as e:
        if e.response['Error']['Code'] == 'InvalidClientTokenId':
            logger.info(
                '%s: Invalid credentials to authenticate for profile user (%s). Exit. [Code: %d]'
                % (inspect.stack()[0][3], profile,
                   exit_codes['EX_NOPERM']['Code']))
        elif e.response['Error']['Code'] == 'ExpiredToken':
            logger.info(
                '%s: Expired temporary credentials detected for profile user (%s) [Code: %d]'
                % (inspect.stack()[0][3], profile,
                   exit_codes['EX_CONFIG']['Code']))
        else:
            logger.exception(
                '%s: Unknown Boto3 problem. Error: %s' %
                (inspect.stack()[0][3], e.response['Error']['Message']))
    except Exception as e:
        return False
    return False
示例#5
0
def boto3_session(service, region=DEFAULT_REGION, profile=None):
    """
    Summary:
        Establishes boto3 sessions, client
    Args:
        :service (str): boto3 service abbreviation ('ec2', 's3', etc)
        :profile (str): profile_name of an iam user from local awscli config
    Returns:
        TYPE: boto3 client object
    """
    try:

        if profile and profile != 'default':
            session = boto3.Session(profile_name=profile)
            return session.client(service, region_name=region)

    except ClientError as e:
        logger.exception(
            "%s: IAM user or role not found (Code: %s Message: %s)" %
            (inspect.stack()[0][3], e.response['Error']['Code'],
             e.response['Error']['Message']))
        raise
    except ProfileNotFound:
        msg = ('%s: The profile (%s) was not found in your local config' %
               (inspect.stack()[0][3], profile))
        stdout_message(msg, 'FAIL')
        logger.warning(msg)
    return boto3.client(service, region_name=region)
示例#6
0
文件: state.py 项目: fstab50/pyaws
def stopped_instances(region, profile=None, ids=False, debug=False):
    """
    Summary.
        Determines state of all ec2 machines in a region

    Returns:
        :stopped ec2 instances, TYPE: ec2 objects
            OR
        :stopped ec2 instance ids, TYPE: str

    """
    try:
        if profile and profile != 'default':
            session = boto3.Session(profile_name=profile)
            ec2 = session.resource('ec2', region_name=region)
        else:
            ec2 = boto3.resource('ec2', region_name=region)

        instances = ec2.instances.all()

        if ids:
            return [x.id for x in instances if x.state['Name'] == 'stopped']

    except ClientError as e:
        logger.exception(
            "%s: IAM user or role not found (Code: %s Message: %s)" %
            (inspect.stack()[0][3], e.response['Error']['Code'],
             e.response['Error']['Message']))
        raise
    except ProfileNotFound:
        msg = ('%s: The profile (%s) was not found in your local config' %
               (inspect.stack()[0][3], profile))
        stdout_message(msg, 'FAIL')
        logger.warning(msg)
    return [x for x in instances if x.state['Name'] == 'stopped']
示例#7
0
文件: dynamodb.py 项目: fstab50/pyaws
    def query_dynamodb(self, partition_key, key_value):
        """
        Queries DynamoDB table using partition key,
        returns the item matching key value
        """
        try:
            resource_dynamodb = self.boto_dynamodb_resource(self.region)
            table = resource_dynamodb.Table(self.tablename)
            logger.info('Table %s: Table Item Count is: %s' %
                        (table.table_name, table.item_count))

            # query on parition key
            response = table.query(
                KeyConditionExpression=Key(partition_key).eq(str(key_value)))
            if response['Items']:
                item = response['Items'][0]['Account Name']
            else:
                item = 'unIdentified'

        except ClientError as e:
            logger.exception(
                "Couldn\'t query DynamoDB table (Code: %s Message: %s)" %
                (e.response['Error']['Code'], e.response['Error']['Message']))
            return 1
        return item
示例#8
0
文件: dynamodb.py 项目: fstab50/pyaws
    def assume_role(self, aws_account_id, service_role):
        """
        Summary.

            Assumes a DynamoDB role in 'destination' AWS account

        Args:
            aws_account_id (str): 12 digit AWS Account number containing dynamodb table
            service_role (str):  IAM role dynamodb service containing permissions
                allowing interaction with dynamodb

        Returns:
            temporary credentials for service_role when assumed, TYPE: json
        """
        session = boto3.Session()
        sts_client = session.client('sts')

        try:

            # assume role in destination account
            assumed_role = sts_client.assume_role(
                RoleArn="arn:aws:iam::%s:role/%s" %
                (str(aws_account_id), service_role),
                RoleSessionName="DynamoDBReaderSession")

        except ClientError as e:
            logger.exception(
                "Couldn't assume role to read DynamoDB, account " +
                str(aws_account_id) +
                " (switching role) (Code: %s Message: %s)" %
                (e.response['Error']['Code'], e.response['Error']['Message']))
            raise e
        return assumed_role['Credentials']
示例#9
0
def sns_notification(topic_arn,
                     subject,
                     message,
                     account_id=None,
                     account_name=None):
    """
    Summary.

        Sends message to AWS sns service topic provided as a
        parameter

    Args:
        topic_arn (str): sns topic arn
        subject (str): subject of sns message notification
        message (str): message body

    Returns:
        TYPE: Boolean | Success or Failure

    """
    if not (account_id or account_name):
        account_id, account_name = get_account_info()

    # assemble msg
    header = 'AWS Account: %s (%s) | %s' % \
        (str(account_name).upper(), str(account_id), subject)
    msg = '\n%s\n\n%s' % (time.strftime('%c'), message)
    msg_dict = {'default': msg}

    # client
    region = (topic_arn.split('sns:', 1)[1]).split(":", 1)[0]
    client = boto3.client('sns', region_name=region)

    try:
        # sns publish
        response = client.publish(TopicArn=topic_arn,
                                  Subject=header,
                                  Message=json.dumps(msg_dict),
                                  MessageStructure='json')
        if response['ResponseMetadata']['HTTPStatusCode'] == '200':
            return True
        else:
            return False
    except ClientError as e:
        logger.exception('%s: problem sending sns msg (Code: %s Message: %s)' %
                         (inspect.stack()[0][3], e.response['Error']['Code'],
                          e.response['Error']['Message']))
        return False
示例#10
0
文件: dynamodb.py 项目: fstab50/pyaws
 def boto_dynamodb_resource(self, region):
     """
     Initiates boto resource to communicate with AWS API
     """
     try:
         dynamodb_resource = boto3.resource(
             'dynamodb',
             aws_access_key_id=self.aws_credentials['AccessKeyId'],
             aws_secret_access_key=self.aws_credentials['SecretAccessKey'],
             aws_session_token=self.aws_credentials['SessionToken'],
             region_name=region)
     except ClientError as e:
         logger.exception(
             "Unknown problem creating boto3 resource (Code: %s Message: %s)"
             %
             (e.response['Error']['Code'], e.response['Error']['Message']))
         return 1
     return dynamodb_resource
示例#11
0
文件: dynamodb.py 项目: fstab50/pyaws
    def scan_accounts(self, account_type):
        """
        Read method for DynamoDB table
        """

        accounts, account_ids = [], []
        valid_mpc_pkgs = [
            'B', 'RA-PKG-B', 'RA-PKG-C', 'P', 'ATA', 'BUP', 'DXA'
        ]

        types = [x.strip(' ') for x in account_type.split(',')]  # parse types

        try:
            resource_dynamodb = self.boto_dynamodb_resource(self.region)
            table = resource_dynamodb.Table(self.tablename)
            # scan table
            if set(types).issubset(set(valid_mpc_pkgs)):
                for type in types:
                    response = table.scan(
                        FilterExpression=Attr('MPCPackage').eq(type))
                    for account_dict in response['Items']:
                        accounts.append(account_dict)
            elif types[0] == 'All':
                # all valid_mpc_pkgs accounts (commercial accounts)
                response = table.scan(
                    FilterExpression=Attr('MPCPackage').ne("P"))
                for account_dict in response['Items']:
                    accounts.append(account_dict)

        except ClientError as e:
            logger.exception(
                "Couldn\'t scan DynamoDB table (Code: %s Message: %s)" %
                (e.response['Error']['Code'], e.response['Error']['Message']))
            return 1

        if accounts:
            for account in accounts:
                account_info = {}
                account_info['AccountName'] = account['Account Name']
                account_info['AccountId'] = account['Account ID']
                account_ids.append(account_info)
        else:
            logger.info('No items returned from DyanamoDB')
        return account_ids
示例#12
0
文件: env.py 项目: fstab50/pyaws
def read_env_variable(arg, default=None, patterns=None):
    """
    Summary.

        Parse environment variables, validate characters, convert
        type(s). default should be used to avoid conversion of an
        variable and retain string type

    Usage:
        >>> from lambda_utils import read_env_variable
        >>> os.environ['DBUGMODE'] = 'True'
        >>> myvar = read_env_variable('DBUGMODE')
        >>> type(myvar)
        True

        >>> from lambda_utils import read_env_variable
        >>> os.environ['MYVAR'] = '1345'
        >>> myvar = read_env_variable('MYVAR', 'default')
        >>> type(myvar)
        str

    Args:
        :arg (str):  Environment variable name (external name)
        :default (str): Default if no variable found in the environment under
            name in arg parameter
        :patterns (None):  Unused; not user callable. Used preservation of the
            patterns tuple between calls during runtime

    Returns:
        environment variable value, TYPE str

    """
    if patterns is None:
        patterns = (
            (re.compile('^[-+]?[0-9]+$'), int),
            (re.compile('\d+\.\d+'), float),
            (re.compile(r'^(true|false)$',
                        flags=re.IGNORECASE), lambda x: x.lower() == 'true'),
            (re.compile('[a-z/]+', flags=re.IGNORECASE), str),
            (re.compile('[a-z/]+\.[a-z/]+', flags=re.IGNORECASE), str),
        )

    if arg in os.environ:
        var = os.environ[arg]
        if var is None:
            ex = KeyError('environment variable %s not set' % arg)
            logger.exception(ex)
            raise ex
        else:
            if default:
                return str(var)  # force default type (str)
            else:
                for pattern, func in patterns:
                    if pattern.match(var):
                        return func(var)
            # type not identified
            logger.warning(
                '%s: failed to identify environment variable [%s] type. May contain \
                special characters' % (inspect.stack()[0][3], arg))
            return str(var)
    else:
        ex = KeyError('environment variable %s not set' % arg)
        logger.exception(ex)
        raise ex
示例#13
0
def main():
    """ copies ec2 instance tags to attached resources """
    for profile in profiles:
        # derive account alias from profile
        account = '-'.join(profile.split('-')[1:])
        for region in regions:
            #instances = []
            session = boto3.Session(profile_name=profile, region_name=region)
            client = session.client('ec2')
            ec2 = session.resource('ec2')
            instances = get_instances(profile, region)
            volumes = get_volumes(profile, region)
            # print summary
            if SUMMARY_REPORT:
                print('\nFor AWS Account %s, region %s, Found %d Instances\n' %
                      (account, region, len(instances)))
                continue
            # copy tags
            if instances:
                try:
                    base = ec2.instances.filter(InstanceIds=instances)
                    ct = 0
                    for instance in base:
                        ids, after_tags = [], []
                        ct += 1
                        if instance.tags:
                            # filter out tags to prohibited from copy
                            filtered_tags = filter_tags(
                                instance.tags, *NO_COPY_LIST)
                        else:
                            # no tags on instance to copy
                            continue

                        if not valid_tags(filtered_tags):
                            print('\nWARNING:')
                            logger.warning(
                                'Skipping instance ID %s, Invalid Tags\n' %
                                instance.id)
                            continue
                        # collect attached resource ids to be tagged
                        for vol in instance.volumes.all():
                            ids.append(vol.id)
                        for eni in instance.network_interfaces:
                            ids.append(eni.id)
                        logger.info('InstanceID %s, instance %d of %d:' %
                                    (instance.id, ct, len(instances)))
                        logger.info('Resource Ids to tag is:')
                        logger.info(str(ids) + '\n')
                        if DEBUGMODE:
                            # BEFORE tag copy
                            logger.info('BEFORE list of %d tags is:' %
                                        (len(instance.tags)))
                            pretty_print_tags(instance.tags)

                            # AFTER tag copy | put Name tag back into apply tags, ie, after_tags
                            retain_tags = select_tags(instance.tags,
                                                      PRESERVE_TAGS)
                            for tag in (*retain_tags, *filtered_tags):
                                after_tags.append(tag)
                            logger.info(
                                'For InstanceID %s, the AFTER FILTERING list of %d tags is:'
                                % (instance.id, len(after_tags)))
                            logger.info('Tags to apply are:')
                            pretty_print_tags(after_tags)
                        else:
                            logger.info('InstanceID %s, instance %d of %d:' %
                                        (instance.id, ct, len(instances)))
                            if filtered_tags:  # we must have something to apply
                                # apply tags
                                for resourceId in ids:
                                    # retain a copy of tags to preserve if is a volume
                                    if resourceId.startswith('vol-'):
                                        r = client.describe_tags(Filters=[
                                            {
                                                'Name': 'resource-id',
                                                'Values': [resourceId],
                                            },
                                        ])
                                        retain_tags = select_tags(
                                            r['Tags'], PRESERVE_TAGS)
                                        # add retained tags before appling to volume
                                        if retain_tags:
                                            for tag in retain_tags:
                                                filtered_tags.append(tag)
                                    # clear tags
                                    print('\n')
                                    logger.info(
                                        'Clearing tags on resource: %s' %
                                        str(resourceId))
                                    client.delete_tags(Resources=[resourceId],
                                                       Tags=[])

                                    # create new tags
                                    logger.info(
                                        'Applying tags to resource %s\n' %
                                        resourceId)
                                    ec2.create_tags(Resources=[resourceId],
                                                    Tags=filtered_tags)
                                    # delay to throttle API requests
                                    sleep(1)

                except ClientError as e:
                    logger.exception(
                        "%s: Problem (Code: %s Message: %s)" %
                        (inspect.stack()[0][3], e.response['Error']['Code'],
                         e.response['Error']['Message']))
                    raise