Path: blob/develop/awscli/customizations/emr/createdefaultroles.py
1567 views
# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.1#2# Licensed under the Apache License, Version 2.0 (the "License"). You3# may not use this file except in compliance with the License. A copy of4# the License is located at5#6# http://aws.amazon.com/apache2.0/7#8# or in the "license" file accompanying this file. This file is9# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF10# ANY KIND, either express or implied. See the License for the specific11# language governing permissions and limitations under the License.1213import logging14import re15import botocore.exceptions16import botocore.session17from botocore import xform_name1819from awscli.customizations.utils import get_policy_arn_suffix20from awscli.customizations.emr import configutils21from awscli.customizations.emr import emrutils22from awscli.customizations.emr import exceptions23from awscli.customizations.emr.command import Command24from awscli.customizations.emr.constants import EC225from awscli.customizations.emr.constants import EC2_ROLE_NAME26from awscli.customizations.emr.constants import EC2_SERVICE_PRINCIPAL27from awscli.customizations.emr.constants import ROLE_ARN_PATTERN28from awscli.customizations.emr.constants import EMR29from awscli.customizations.emr.constants import EMR_ROLE_NAME30from awscli.customizations.emr.constants import EMR_AUTOSCALING_ROLE_NAME31from awscli.customizations.emr.constants import APPLICATION_AUTOSCALING32from awscli.customizations.emr.constants import EC2_ROLE_POLICY_NAME33from awscli.customizations.emr.constants import EMR_ROLE_POLICY_NAME34from awscli.customizations.emr.constants \35import EMR_AUTOSCALING_ROLE_POLICY_NAME36from awscli.customizations.emr.constants import EMR_AUTOSCALING_SERVICE_NAME37from awscli.customizations.emr.constants \38import EMR_AUTOSCALING_SERVICE_PRINCIPAL39from awscli.customizations.emr.exceptions import ResolveServicePrincipalError404142LOG = logging.getLogger(__name__)434445def assume_role_policy(serviceprincipal):46return {47"Version": "2008-10-17",48"Statement": [49{50"Sid": "",51"Effect": "Allow",52"Principal": {"Service": serviceprincipal},53"Action": "sts:AssumeRole"54}55]56}575859def get_role_policy_arn(region, policy_name):60region_suffix = get_policy_arn_suffix(region)61role_arn = ROLE_ARN_PATTERN.replace("{{region_suffix}}", region_suffix)62role_arn = role_arn.replace("{{policy_name}}", policy_name)63return role_arn646566def get_service_principal(service, endpoint_host, session=None):67if service == EC2:68return EC2_SERVICE_PRINCIPAL6970suffix, region = _get_suffix_and_region_from_endpoint_host(endpoint_host)71if session is None:72session = botocore.session.Session()7374if service == EMR_AUTOSCALING_SERVICE_NAME:75if region not in session.get_available_regions('emr', 'aws-cn'):76return EMR_AUTOSCALING_SERVICE_PRINCIPAL7778return service + '.' + suffix798081def _get_suffix_and_region_from_endpoint_host(endpoint_host):82suffix_match = _get_regex_match_from_endpoint_host(endpoint_host)8384if suffix_match is not None and suffix_match.lastindex >= 3:85suffix = suffix_match.group(3)86region = suffix_match.group(2)87else:88raise ResolveServicePrincipalError8990return suffix, region919293def _get_regex_match_from_endpoint_host(endpoint_host):94if endpoint_host is None:95return None96regex_match = re.match("(https?://)([^.]+).elasticmapreduce.([^/]*)",97endpoint_host)9899# Supports 'elasticmapreduce.{region}.' and '{region}.elasticmapreduce.'100if regex_match is None:101regex_match = re.match("(https?://elasticmapreduce).([^.]+).([^/]*)",102endpoint_host)103return regex_match104105106class CreateDefaultRoles(Command):107NAME = "create-default-roles"108DESCRIPTION = ('Creates the default IAM role ' +109EC2_ROLE_NAME + ' and ' +110EMR_ROLE_NAME + ' which can be used when creating the'111' cluster using the create-cluster command. The default'112' roles for EMR use managed policies, which are updated'113' automatically to support future EMR functionality.\n'114'\nIf you do not have a Service Role and Instance Profile '115'variable set for your create-cluster command in the AWS '116'CLI config file, create-default-roles will automatically '117'set the values for these variables with these default '118'roles. If you have already set a value for Service Role '119'or Instance Profile, create-default-roles will not '120'automatically set the defaults for these variables in the '121'AWS CLI config file. You can view settings for variables '122'in the config file using the "aws configure get" command.'123'\n')124ARG_TABLE = [125{'name': 'iam-endpoint',126'no_paramfile': True,127'help_text': '<p>The IAM endpoint to call for creating the roles.'128' This is optional and should only be specified when a'129' custom endpoint should be called for IAM operations'130'.</p>'}131]132133def _run_main_command(self, parsed_args, parsed_globals):134135self.iam_endpoint_url = parsed_args.iam_endpoint136137self._check_for_iam_endpoint(self.region, self.iam_endpoint_url)138self.emr_endpoint_url = \139self._session.create_client(140'emr',141region_name=self.region,142endpoint_url=parsed_globals.endpoint_url,143verify=parsed_globals.verify_ssl).meta.endpoint_url144145LOG.debug('elasticmapreduce endpoint used for resolving'146' service principal: ' + self.emr_endpoint_url)147148# Create default EC2 Role for EMR if it does not exist.149ec2_result, ec2_policy = self._create_role_if_not_exists(parsed_globals, EC2_ROLE_NAME,150EC2_ROLE_POLICY_NAME, [EC2])151152# Create default EC2 Instance Profile for EMR if it does not exist.153instance_profile_name = EC2_ROLE_NAME154if self.check_if_instance_profile_exists(instance_profile_name,155parsed_globals):156LOG.debug('Instance Profile ' + instance_profile_name + ' exists.')157else:158LOG.debug('Instance Profile ' + instance_profile_name +159'does not exist. Creating default Instance Profile ' +160instance_profile_name)161self._create_instance_profile_with_role(instance_profile_name,162instance_profile_name,163parsed_globals)164165# Create default EMR Role if it does not exist.166emr_result, emr_policy = self._create_role_if_not_exists(parsed_globals, EMR_ROLE_NAME,167EMR_ROLE_POLICY_NAME, [EMR])168169# Create default EMR AutoScaling Role if it does not exist.170emr_autoscaling_result, emr_autoscaling_policy = \171self._create_role_if_not_exists(parsed_globals, EMR_AUTOSCALING_ROLE_NAME,172EMR_AUTOSCALING_ROLE_POLICY_NAME, [EMR, APPLICATION_AUTOSCALING])173174configutils.update_roles(self._session)175emrutils.display_response(176self._session,177'create_role',178self._construct_result(ec2_result, ec2_policy,179emr_result, emr_policy,180emr_autoscaling_result, emr_autoscaling_policy),181parsed_globals)182183return 0184185def _create_role_if_not_exists(self, parsed_globals, role_name, policy_name, service_names):186result = None187policy = None188189if self.check_if_role_exists(role_name, parsed_globals):190LOG.debug('Role ' + role_name + ' exists.')191else:192LOG.debug('Role ' + role_name + ' does not exist.'193' Creating default role: ' + role_name)194role_arn = get_role_policy_arn(self.region, policy_name)195result = self._create_role_with_role_policy(196role_name, service_names, role_arn, parsed_globals)197policy = self._get_role_policy(role_arn, parsed_globals)198return result, policy199200def _check_for_iam_endpoint(self, region, iam_endpoint):201try:202self._session.create_client('emr', region)203except botocore.exceptions.UnknownEndpointError:204if iam_endpoint is None:205raise exceptions.UnknownIamEndpointError(region=region)206207def _construct_result(self, ec2_response, ec2_policy,208emr_response, emr_policy,209emr_autoscaling_response, emr_autoscaling_policy):210result = []211self._construct_role_and_role_policy_structure(212result, ec2_response, ec2_policy)213self._construct_role_and_role_policy_structure(214result, emr_response, emr_policy)215self._construct_role_and_role_policy_structure(216result, emr_autoscaling_response, emr_autoscaling_policy)217return result218219def _construct_role_and_role_policy_structure(220self, list, response, policy):221if response is not None and response['Role'] is not None:222list.append({'Role': response['Role'], 'RolePolicy': policy})223return list224225def check_if_role_exists(self, role_name, parsed_globals):226parameters = {'RoleName': role_name}227228try:229self._call_iam_operation('GetRole', parameters, parsed_globals)230except botocore.exceptions.ClientError as e:231role_not_found_code = "NoSuchEntity"232error_code = e.response.get('Error', {}).get('Code', '')233if role_not_found_code == error_code:234# No role error.235return False236else:237# Some other error. raise.238raise e239240return True241242def check_if_instance_profile_exists(self, instance_profile_name,243parsed_globals):244parameters = {'InstanceProfileName': instance_profile_name}245try:246self._call_iam_operation('GetInstanceProfile', parameters,247parsed_globals)248except botocore.exceptions.ClientError as e:249profile_not_found_code = 'NoSuchEntity'250error_code = e.response.get('Error', {}).get('Code')251if profile_not_found_code == error_code:252# No instance profile error.253return False254else:255# Some other error. raise.256raise e257258return True259260def _get_role_policy(self, arn, parsed_globals):261parameters = {}262parameters['PolicyArn'] = arn263policy_details = self._call_iam_operation('GetPolicy', parameters,264parsed_globals)265parameters["VersionId"] = policy_details["Policy"]["DefaultVersionId"]266policy_version_details = self._call_iam_operation('GetPolicyVersion',267parameters,268parsed_globals)269return policy_version_details["PolicyVersion"]["Document"]270271def _create_role_with_role_policy(272self, role_name, service_names, role_arn, parsed_globals):273274if len(service_names) == 1:275service_principal = get_service_principal(276service_names[0], self.emr_endpoint_url, self._session)277else:278service_principal = []279for service in service_names:280service_principal.append(get_service_principal(281service, self.emr_endpoint_url, self._session))282283LOG.debug(f'Adding service principal(s) to trust policy: {service_principal}')284285parameters = {'RoleName': role_name}286_assume_role_policy = \287emrutils.dict_to_string(assume_role_policy(service_principal))288parameters['AssumeRolePolicyDocument'] = _assume_role_policy289create_role_response = self._call_iam_operation('CreateRole',290parameters,291parsed_globals)292293parameters = {}294parameters['PolicyArn'] = role_arn295parameters['RoleName'] = role_name296self._call_iam_operation('AttachRolePolicy',297parameters, parsed_globals)298299return create_role_response300301def _create_instance_profile_with_role(self, instance_profile_name,302role_name, parsed_globals):303# Creating an Instance Profile304parameters = {'InstanceProfileName': instance_profile_name}305self._call_iam_operation('CreateInstanceProfile', parameters,306parsed_globals)307# Adding the role to the Instance Profile308parameters = {}309parameters['InstanceProfileName'] = instance_profile_name310parameters['RoleName'] = role_name311self._call_iam_operation('AddRoleToInstanceProfile', parameters,312parsed_globals)313314def _call_iam_operation(self, operation_name, parameters, parsed_globals):315client = self._session.create_client(316'iam', region_name=self.region, endpoint_url=self.iam_endpoint_url,317verify=parsed_globals.verify_ssl)318return getattr(client, xform_name(operation_name))(**parameters)319320321