Ejemplo n.º 1
0
def fetch_cert_and_key(session: SessionProxy, certificate_arn,
                       private_key_secret_name):
    """
    Fetches Certificate and Private Key material from Amazon Certificate Manager and Secrets Manager

    :param session: Boto3 session
    :param certificate_arn: Valid ACM ARN
    :param private_key_secret_name: Valid Secrets Manager Name ID
    :return: Certificate, Certificate Chain, Private Key
    """
    secrets = session.client('secretsmanager')
    acm = session.client('acm')
    log.info('Fetching custom Ingress Certificate from ACM ')
    log.debug('ACM ARN: %s', certificate_arn)
    cert_response = acm.get_certificate(CertificateArn=certificate_arn)
    log.debug('Certificate Data: %s', cert_response)
    cert = cert_response['Certificate']
    cert_chain = cert_response['CertificateChain']

    log.info('Fetching custom Ingress Private Key from Secrets Manager')
    log.debug('Secret Name: %s', private_key_secret_name)
    private_key = secrets.get_secret_value(
        SecretId=private_key_secret_name)['SecretString']
    log.info('Secret fetched successfully')

    return cert, cert_chain, private_key
Ejemplo n.º 2
0
def create(obj: ProvisioningStatus, request: ResourceHandlerRequest,
           callback_context: MutableMapping[str, Any], session: SessionProxy):
    LOG.info("starting NEW RESOURCE with request\n{}".format(request))
    progress: ProgressEvent = ProgressEvent(
        status=OperationStatus.IN_PROGRESS,
        resourceModel=request.desiredResourceState,
        callbackContext=callback_context,
        callbackDelaySeconds=15)
    env_name = get_name_from_request(request)
    if request.desiredResourceState.OwnerArn:
        owner_arn = request.desiredResourceState.OwnerArn
    else:
        owner_arn = "arn:aws:iam::{}:root".format(
            session.client('sts').get_caller_identity()['Account'])
    response = session.client('cloud9').create_environment_ec2(
        ownerArn=owner_arn,
        name=env_name,
        instanceType=request.desiredResourceState.InstanceType,
        automaticStopTimeMinutes=30)
    LOG.info("environment id: {}".format(response['environmentId']))
    model: ResourceModel = request.desiredResourceState
    model.Name = env_name
    model.EnvironmentId = response['environmentId']
    progress.callbackContext["ENVIRONMENT_NAME"] = env_name
    progress.callbackContext["ENVIRONMENT_ID"] = response['environmentId']
    progress.callbackContext["LOCAL_STATUS"] = EnvironmentCreated()
    progress.status = OperationStatus.IN_PROGRESS
    progress.message = "Cloud9 Environment created"
    return progress
Ejemplo n.º 3
0
def get_environment_info(obj: ProvisioningStatus,
                         request: ResourceHandlerRequest,
                         callback_context: MutableMapping[str, Any],
                         session: SessionProxy):
    LOG.info(
        "starting ENVIRONMENT_CREATED with callback_context\n{}\nand request\n{}"
        .format(callback_context, request))
    progress: ProgressEvent = ProgressEvent(
        status=OperationStatus.IN_PROGRESS,
        resourceModel=request.desiredResourceState,
        callbackContext=callback_context,
        callbackDelaySeconds=15)
    environment_id = callback_context["ENVIRONMENT_ID"]
    LOG.info("environment id: {}".format(environment_id))
    try:
        ec2_client = session.client("ec2")
        instance_filter = ec2_client.describe_instances(
            Filters=[{
                'Name': 'tag:aws:cloud9:environment',
                'Values': [environment_id]
            }])
        if len(instance_filter['Reservations']) < 1:
            LOG.info("instance not available from `describe instances` call")
            return progress
        if len(instance_filter['Reservations'][0]['Instances']) < 1:
            LOG.info(
                "instance not available from `describe instances` call, part deux"
            )
            return progress
        instance_id = instance_filter['Reservations'][0]['Instances'][0][
            'InstanceId']
        instance_state = instance_filter['Reservations'][0]['Instances'][0][
            'State']['Name']
        c9_client = session.client("cloud9")
        environment_status = c9_client.describe_environment_status(
            environmentId=environment_id)
        LOG.info("Checking Environment and instance status")
        if (environment_status['status'] == 'ready') and (instance_state
                                                          == 'running'):
            LOG.info("environment is ready and instance is running")
            progress.resourceModel.InstanceId = instance_id
            progress.callbackContext["INSTANCE_ID"] = instance_id
            progress.message = "Cloud9 Environment is stable"
            progress.callbackDelaySeconds = 0
            if request.desiredResourceState.EBSVolumeSize:
                progress.callbackContext["LOCAL_STATUS"] = InstanceStable()
            else:
                progress.callbackContext["LOCAL_STATUS"] = ResizedInstance()

    except Exception as e:
        LOG.info('throwing: {}'.format(e))
        raise (e)
    LOG.info("returning progress from ENVIRONMENT_CREATED {}".format(progress))
    return progress
Ejemplo n.º 4
0
def create_and_attach_instance_profile(obj: ProvisioningStatus,
                                       request: ResourceHandlerRequest,
                                       callback_context: MutableMapping[str,
                                                                        Any],
                                       session: SessionProxy):
    LOG.info(
        "starting ATTACH_INSTANCE_PROFILE with callback_context\n{}\nand request\n{}"
        .format(callback_context, request))
    progress: ProgressEvent = ProgressEvent(
        status=OperationStatus.IN_PROGRESS,
        resourceModel=request.desiredResourceState,
        callbackContext=callback_context,
        callbackDelaySeconds=30)
    instance_profile_arn = callback_context["INSTANCE_PROFILE_ARN"]
    instance_id = callback_context["INSTANCE_ID"]
    try:
        ec2_client = session.client('ec2')
        associate_profile_response = ec2_client.associate_iam_instance_profile(
            IamInstanceProfile={'Arn': instance_profile_arn},
            InstanceId=instance_id)
        callback_context["ASSOCIATION_ID"] = associate_profile_response[
            'IamInstanceProfileAssociation']['AssociationId']
        progress.callbackContext["LOCAL_STATUS"] = ProfileAttached()
    except Exception as e:
        print(e)
    LOG.info(
        "returning progress from ATTACH_INSTANCE_PROFILE {}".format(progress))
    return progress
Ejemplo n.º 5
0
def handle_A(obj: ProvisioningStatus, request: ResourceHandlerRequest,
             callback_context: MutableMapping[str,
                                              Any], session: SessionProxy):
    LOG.info(
        "starting INSTANCE_STABLE with callback_context\n{}\nand request\n{}".
        format(callback_context, request))
    progress: ProgressEvent = ProgressEvent(
        status=OperationStatus.IN_PROGRESS,
        resourceModel=request.desiredResourceState,
        callbackContext=callback_context,
        callbackDelaySeconds=0)
    instance_id = callback_context["INSTANCE_ID"]
    try:
        ec2_client = session.client("ec2")
        if request.desiredResourceState.EBSVolumeSize:
            resize_ebs(instance_id,
                       int(request.desiredResourceState.EBSVolumeSize),
                       ec2_client)
        progress.callbackContext["LOCAL_STATUS"] = ResizedInstance()
        progress.message = "Resized EBS Volume"
    except Exception as e:
        LOG.info('Can\'t resize instance: {}'.format(e))
        raise (e)
    LOG.info("returning progress from INSTANCE_STABLE {}".format(progress))
    return progress
Ejemplo n.º 6
0
def delete_contents_s3(s3_bucket,
                       session: SessionProxy,
                       filter: Optional[Mapping] = None):
    """
    Clear out an S3 bucket. Handy for cleaning up if the CF Resource is deleted. Default is to delete all objects from
    the bucket.

    Optionally, pass a filter that is passed as **kwargs to the boto3.Bucket.objects.filter(**kwargs) call
    :param session: Boto SessionProxy
    :param filter: A Mapping of valid Filters for the AWS S3 API action `GetObjects`
    :param s3_bucket: Name of the S3 bucket to delete objects from
    :return None:
    """
    s3 = session.resource('s3')
    bucket = s3.Bucket(s3_bucket)
    try:
        if filter is None:
            log.debug("Deleting bucket {}...".format(s3_bucket))
            bucket.objects.all().delete()
        else:
            bucket.objects.filter(**filter).delete()
    except ClientError as e:
        # If a client error is thrown, then check that it was a 404 error.
        # If it was a 404 error, then the bucket does not exist.
        error_code = e.response['Error']['Code']
        if error_code == "NoSuchBucket":
            log.debug("{} does not exist, skipping...".format(s3_bucket))
            return
        else:
            log.error(
                "Failed to delete bucket, unhandled exception {}".format(e))
            raise e
    except Exception as e:
        log.error("Failed to delete bucket, unhandled exception {}".format(e))
        raise e
def send_command(obj: ProvisioningStatus, request: ResourceHandlerRequest, callback_context: MutableMapping[str, Any], session: SessionProxy):
    LOG.info("starting SEND_COMMAND with callback_context\n{}\nand request\n{}".format(callback_context, request))
    progress: ProgressEvent = ProgressEvent(
        status=OperationStatus.IN_PROGRESS,
        resourceModel=request.desiredResourceState,
        callbackContext=callback_context,
        callbackDelaySeconds=0
    )
    instance_id = callback_context["INSTANCE_ID"]
    ssm_client = session.client('ssm')
    
    if not ssm_ready(ssm_client, instance_id):
        progress.callbackDelaySeconds=90
        return progress
    if request.desiredResourceState.UserData:
        from io import BytesIO
        s3 = session.resource('s3')
        bucket = s3.Bucket(request.desiredResourceState.UserData.Bucket)
        obj = bucket.Object(request.desiredResourceState.UserData.Object)
        output = BytesIO()
        obj.download_fileobj(output)
        commands = get_preamble() + '\n' + output.getvalue().decode('utf-8') + '\n'
    else:
        commands = get_preamble()
    
    LOG.info("Sending command to %s : %s" % (instance_id, commands))
    try:
        send_command_response = ssm_client.send_command(
            InstanceIds=[instance_id], 
            DocumentName='AWS-RunShellScript', 
            Parameters={'commands': commands.split('\n')},
            CloudWatchOutputConfig={
                'CloudWatchLogGroupName': f'ssm-output-{instance_id}',
                'CloudWatchOutputEnabled': True
            }
        )
        progress.callbackContext["RUN_COMMAND_ID"] = send_command_response['Command']['CommandId']
        progress.callbackContext["LOCAL_STATUS"] = CommandSent()
    except ssm_client.exceptions.InvalidInstanceId:
        LOG.info("Failed to execute SSM command. This happens some times when the box isn't ready yet. we'll retry in a minute.")
    LOG.info("returning progress from SEND_COMMAND {}".format(progress))
    return progress
Ejemplo n.º 8
0
def _mock_get_session_proxy() -> SessionProxy:
    """Create and return a mock SessionProxy with a mock session client"""
    # Create a mock session
    session = MagicMock(name='session', )
    # Create a mock session client
    session.client = MagicMock(
        name='client',
        return_value=MagicMock(name='client', ),
    )
    # Return a SessionProxy with the mock session and client
    return SessionProxy(session, )
Ejemplo n.º 9
0
def delete_s3_file(s3_bucket, file_name, session: SessionProxy):
    """
    Delete an individual file object from S3

    :param session: Boto SessionProxy
    :param s3_bucket: Name of the S3 bucket
    :param file_name: Name of object to delete in S3
    :return:
    """
    client = session.client('s3')
    client.delete_object(Bucket=s3_bucket, Key=file_name)
Ejemplo n.º 10
0
def create_iam_role(obj: ProvisioningStatus, request: ResourceHandlerRequest,
                    callback_context: MutableMapping[str, Any],
                    session: SessionProxy):
    LOG.info(
        "starting CREATE_IAM_ROLE with callback_context\n{}\nand request\n{}".
        format(callback_context, request))
    progress: ProgressEvent = ProgressEvent(
        status=OperationStatus.IN_PROGRESS,
        resourceModel=request.desiredResourceState,
        callbackContext=callback_context,
        callbackDelaySeconds=1)
    environment_id = callback_context["ENVIRONMENT_ID"]
    instance_id = callback_context["INSTANCE_ID"]
    environment_id = callback_context["ENVIRONMENT_ID"]
    environment_name = callback_context["ENVIRONMENT_NAME"]
    iam_client = session.client("iam")
    role_name = get_or_create_role(iam_client,
                                   f'{environment_name}-InstanceProfileRole',
                                   instance_id, environment_id)
    if request.desiredResourceState.Cloud9InstancePolicy:
        LOG.debug(
            f'attempting to add the following inline policy: {request.desiredResourceState.Cloud9InstancePolicy._serialize()}'
        )
        iam_client.put_role_policy(
            RoleName=role_name,
            PolicyName=request.desiredResourceState.Cloud9InstancePolicy.
            PolicyName,
            PolicyDocument=json.dumps(
                request.desiredResourceState.Cloud9InstancePolicy.
                PolicyDocument._serialize()))
    iam_client.attach_role_policy(
        RoleName=role_name,
        PolicyArn='arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore')
    iam_client.attach_role_policy(
        RoleName=role_name,
        PolicyArn='arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy')

    LOG.info("Creating Instance Profile")
    create_instance_profile_response = iam_client.create_instance_profile(
        InstanceProfileName=f'{environment_name}-InstanceProfile',
        Path='/cdk/cloud9/environment')
    LOG.info("Attatching Role to Instance Profile")
    iam_client.add_role_to_instance_profile(
        InstanceProfileName=f'{environment_name}-InstanceProfile',
        RoleName=role_name)
    progress.callbackContext["LOCAL_STATUS"] = RoleCreated()
    progress.callbackContext[
        "INSTANCE_PROFILE_ARN"] = create_instance_profile_response[
            'InstanceProfile']['Arn']
    LOG.info("returning progress from CREATE_IAM_ROLE {}".format(progress))
    return progress
Ejemplo n.º 11
0
def check_file_s3(s3_bucket, key, session: SessionProxy):
    """
    Check if key exists in S3 bucket
    :param session: Boto SessionProxy
    :param s3_bucket: Name of the S3 bucket to check
    :param key: Object key
    :return:
    """
    client = session.client('s3')
    try:
        client.head_object(Bucket=s3_bucket, Key=key)
        log.debug("File at location {} found".format(key))
        return True
    except Exception as e:
        log.debug("File not found at {} and key {}".format(s3_bucket, key))
        return False
Ejemplo n.º 12
0
def stabilize(obj: ProvisioningStatus, request: ResourceHandlerRequest, callback_context: MutableMapping[str, Any], session: SessionProxy):
    LOG.info("starting STABILIZED with callback_context\n{}\nand request\n{}".format(callback_context, request))
    progress: ProgressEvent = ProgressEvent(
        status=OperationStatus.IN_PROGRESS,
        resourceModel=request.desiredResourceState,
        callbackContext=callback_context,
        callbackDelaySeconds=60
    )
    command_id = callback_context["RUN_COMMAND_ID"]
    instance_id = callback_context["INSTANCE_ID"]
    ssm_client = session.client('ssm')
    response = ssm_client.get_command_invocation(CommandId=command_id, InstanceId=instance_id)
    if response['Status'] in ['Pending', 'InProgress', 'Delayed']:
        return progress
    else:
        progress.status = OperationStatus.SUCCESS
    LOG.info("returning progress from STABILIZED {}".format(progress))
    return progress
Ejemplo n.º 13
0
def send_command(obj: ProvisioningStatus, request: ResourceHandlerRequest,
                 callback_context: MutableMapping[str,
                                                  Any], session: SessionProxy):
    LOG.info(
        "starting SEND_COMMAND with callback_context\n{}\nand request\n{}".
        format(callback_context, request))
    progress: ProgressEvent = ProgressEvent(
        status=OperationStatus.IN_PROGRESS,
        resourceModel=request.desiredResourceState,
        callbackContext=callback_context,
        callbackDelaySeconds=0)
    instance_id = callback_context["INSTANCE_ID"]
    ssm_client = session.client('ssm')

    if not ssm_ready(ssm_client, instance_id):
        progress.callbackDelaySeconds = 180
        return progress
    with open('./cdk_cloud9_environment/run_command.sh', 'r') as myfile:
        commands = myfile.read()

    LOG.info("Sending command to %s : %s" % (instance_id, commands))
    try:
        send_command_response = ssm_client.send_command(
            InstanceIds=[instance_id],
            DocumentName='AWS-RunShellScript',
            Parameters={'commands': commands.split('\n')},
            CloudWatchOutputConfig={
                'CloudWatchLogGroupName': f'ssm-output-{instance_id}',
                'CloudWatchOutputEnabled': True
            })
        progress.callbackContext["RUN_COMMAND_ID"] = send_command_response[
            'Command']['CommandId']
        progress.callbackContext["LOCAL_STATUS"] = CommandSent()
    except ssm_client.exceptions.InvalidInstanceId:
        LOG.info(
            "Failed to execute SSM command. This happens some times when the box isn't ready yet. we'll retry in a minute."
        )
    LOG.info("returning progress from SEND_COMMAND {}".format(progress))
    return progress
def _get_cross_session(session: SessionProxy, model: ResourceModel,
                       session_name):
    """Returns session object for cross-account access.

    :return: Can be used to obtain session.client() or session.resource()
    """
    LOG.setLevel(model.LogLevel)

    role_arn = "arn:aws:iam::{}:role{}{}".format(model.AccountId,
                                                 model.AssumeRolePath,
                                                 model.AssumeRoleName)

    LOG.debug("Assuming Cross session role: %s", role_arn)
    assumed_role = session.client("sts").assume_role(
        RoleArn=role_arn, RoleSessionName=session_name)

    session = boto3.Session(
        aws_access_key_id=assumed_role["Credentials"]["AccessKeyId"],
        aws_secret_access_key=assumed_role["Credentials"]["SecretAccessKey"],
        aws_session_token=assumed_role["Credentials"]["SessionToken"])

    return session
Ejemplo n.º 15
0
def create_mock_session():
    mock_session_impl = MagicMock(name="mock_session_impl")
    mock_session_impl.client = MagicMock(return_value=MagicMock(name="mock_afd_client"))
    mock_session = SessionProxy(session=mock_session_impl)
    return mock_session
def get_cross_cfn_client(session: SessionProxy, model: ResourceModel,
                         session_name):
    session = _get_cross_session(session, model, session_name)
    return session.client("cloudformation", region_name=model.Region)