# Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). You # may not use this file except in compliance with the License. A copy of # the License is located at # # http://aws.amazon.com/apache2.0/ # # or in the "license" file accompanying this file. This file is # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. # Class to create default roles for datapipeline import logging import warnings from awscli.customizations.datapipeline.constants \ import DATAPIPELINE_DEFAULT_SERVICE_ROLE_NAME, \ DATAPIPELINE_DEFAULT_RESOURCE_ROLE_NAME, \ DATAPIPELINE_DEFAULT_SERVICE_ROLE_ARN, \ DATAPIPELINE_DEFAULT_RESOURCE_ROLE_ARN, \ DATAPIPELINE_DEFAULT_SERVICE_ROLE_ASSUME_POLICY, \ DATAPIPELINE_DEFAULT_RESOURCE_ROLE_ASSUME_POLICY from awscli.customizations.commands import BasicCommand from awscli.customizations.datapipeline.translator \ import display_response, dict_to_string, get_region from botocore.exceptions import ClientError LOG = logging.getLogger(__name__) _DEPRECATION_NOTICE = """ Support for this command has been deprecated and may fail to create these roles if they do not already exist. For more information on managing these policies manually see the following documentation: https://docs.aws.amazon.com/datapipeline/latest/DeveloperGuide/dp-iam-roles.html """ _DESCRIPTION = """ NOTE: {} Creates the default IAM role "{}" and "{}" which are used while creating an EMR cluster. If these roles do not exist, create-default-roles will automatically create them and set their policies. If these roles have already been created create-default-roles will not update their policies. """ class CreateDefaultRoles(BasicCommand): NAME = "create-default-roles" _UNDOCUMENTED = True DESCRIPTION = _DESCRIPTION.format( _DEPRECATION_NOTICE, DATAPIPELINE_DEFAULT_SERVICE_ROLE_NAME, DATAPIPELINE_DEFAULT_RESOURCE_ROLE_NAME, ) def __init__(self, session, formatter=None): super(CreateDefaultRoles, self).__init__(session) def _run_main(self, parsed_args, parsed_globals, **kwargs): """Call to run the commands""" self._region = get_region(self._session, parsed_globals) self._endpoint_url = parsed_globals.endpoint_url self._iam_client = self._session.create_client( 'iam', region_name=self._region, endpoint_url=self._endpoint_url, verify=parsed_globals.verify_ssl ) warnings.warn(_DEPRECATION_NOTICE) return self._create_default_roles(parsed_args, parsed_globals) def _create_role(self, role_name, role_arn, role_policy): """Method to create a role for a given role name and arn if it does not exist """ role_result = None role_policy_result = None # Check if the role with the name exists if self._check_if_role_exists(role_name): LOG.debug('Role ' + role_name + ' exists.') else: LOG.debug('Role ' + role_name + ' does not exist.' ' Creating default role for EC2: ' + role_name) # Create a create using the IAM Client with a particular triplet # (role_name, role_arn, assume_role_policy) role_result = self._create_role_with_role_policy(role_name, role_policy, role_arn) role_policy_result = self._get_role_policy(role_arn) return role_result, role_policy_result def _construct_result(self, dpl_default_result, dpl_default_policy, dpl_default_res_result, dpl_default_res_policy): """Method to create a resultant list of responses for create roles for service and resource role """ result = [] self._construct_role_and_role_policy_structure(result, dpl_default_result, dpl_default_policy) self._construct_role_and_role_policy_structure(result, dpl_default_res_result, dpl_default_res_policy) return result def _create_default_roles(self, parsed_args, parsed_globals): # Setting the role name and arn value (datapipline_default_result, datapipline_default_policy) = self._create_role( DATAPIPELINE_DEFAULT_SERVICE_ROLE_NAME, DATAPIPELINE_DEFAULT_SERVICE_ROLE_ARN, DATAPIPELINE_DEFAULT_SERVICE_ROLE_ASSUME_POLICY) (datapipline_default_resource_result, datapipline_default_resource_policy) = self._create_role( DATAPIPELINE_DEFAULT_RESOURCE_ROLE_NAME, DATAPIPELINE_DEFAULT_RESOURCE_ROLE_ARN, DATAPIPELINE_DEFAULT_RESOURCE_ROLE_ASSUME_POLICY) # Check if the default EC2 Instance Profile for DataPipeline exists. instance_profile_name = DATAPIPELINE_DEFAULT_RESOURCE_ROLE_NAME if self._check_if_instance_profile_exists(instance_profile_name): LOG.debug('Instance Profile ' + instance_profile_name + ' exists.') else: LOG.debug('Instance Profile ' + instance_profile_name + 'does not exist. Creating default Instance Profile ' + instance_profile_name) self._create_instance_profile_with_role(instance_profile_name, instance_profile_name) result = self._construct_result(datapipline_default_result, datapipline_default_policy, datapipline_default_resource_result, datapipline_default_resource_policy) display_response(self._session, 'create_role', result, parsed_globals) return 0 def _get_role_policy(self, arn): """Method to get the Policy for a particular ARN This is used to display the policy contents to the user """ pol_det = self._iam_client.get_policy(PolicyArn=arn) policy_version_details = self._iam_client.get_policy_version( PolicyArn=arn, VersionId=pol_det["Policy"]["DefaultVersionId"]) return policy_version_details["PolicyVersion"]["Document"] def _create_role_with_role_policy( self, role_name, assume_role_policy, role_arn): """Method to create role with a given rolename, assume_role_policy and role_arn """ # Create a role using IAM client CreateRole API create_role_response = self._iam_client.create_role( RoleName=role_name, AssumeRolePolicyDocument=dict_to_string( assume_role_policy)) # Create a role using IAM client AttachRolePolicy API self._iam_client.attach_role_policy(PolicyArn=role_arn, RoleName=role_name) return create_role_response def _construct_role_and_role_policy_structure( self, list_val, response, policy): """Method to construct the message to be displayed to the user""" # If the response is not none they we get the role name # from the response and # append the policy information to the response if response is not None and response['Role'] is not None: list_val.append({'Role': response['Role'], 'RolePolicy': policy}) return list_val def _check_if_instance_profile_exists(self, instance_profile_name): """Method to verify if a particular role exists""" try: # Client call to get the instance profile with that name self._iam_client.get_instance_profile( InstanceProfileName=instance_profile_name) except ClientError as e: # If the instance profile does not exist then the error message # would contain the required message if e.response['Error']['Code'] == 'NoSuchEntity': # No instance profile error. return False else: # Some other error. raise. raise e return True def _check_if_role_exists(self, role_name): """Method to verify if a particular role exists""" try: # Client call to get the role self._iam_client.get_role(RoleName=role_name) except ClientError as e: # If the role does not exist then the error message # would contain the required message. if e.response['Error']['Code'] == 'NoSuchEntity': # No role error. return False else: # Some other error. raise. raise e return True def _create_instance_profile_with_role(self, instance_profile_name, role_name): """Method to create the instance profile with the role""" # Setting the value for instance profile name # Client call to create an instance profile self._iam_client.create_instance_profile( InstanceProfileName=instance_profile_name) # Adding the role to the Instance Profile self._iam_client.add_role_to_instance_profile( InstanceProfileName=instance_profile_name, RoleName=role_name)