-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1616 from kyleknap/datapipeline-role-cli
Changes to create a DataPipeline CLI create-default-roles
- Loading branch information
Showing
6 changed files
with
479 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
# 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. | ||
|
||
# Declare all the constants used by DataPipeline in this file | ||
|
||
# DataPipeline role names | ||
DATAPIPELINE_DEFAULT_SERVICE_ROLE_NAME = "DataPipelineDefaultRole" | ||
DATAPIPELINE_DEFAULT_RESOURCE_ROLE_NAME = "DataPipelineDefaultResourceRole" | ||
|
||
# DataPipeline role arn names | ||
DATAPIPELINE_DEFAULT_SERVICE_ROLE_ARN = ("arn:aws:iam::aws:policy/" | ||
"service-role/AWSDataPipelineRole") | ||
DATAPIPELINE_DEFAULT_RESOURCE_ROLE_ARN = ("arn:aws:iam::aws:policy/" | ||
"service-role/" | ||
"AmazonEC2RoleforDataPipelineRole") | ||
|
||
# Assume Role Policy definitions for roles | ||
DATAPIPELINE_DEFAULT_RESOURCE_ROLE_ASSUME_POLICY = { | ||
"Version": "2008-10-17", | ||
"Statement": [ | ||
{ | ||
"Sid": "", | ||
"Effect": "Allow", | ||
"Principal": {"Service": "ec2.amazonaws.com"}, | ||
"Action": "sts:AssumeRole" | ||
} | ||
] | ||
} | ||
|
||
DATAPIPELINE_DEFAULT_SERVICE_ROLE_ASSUME_POLICY = { | ||
"Version": "2008-10-17", | ||
"Statement": [ | ||
{ | ||
"Sid": "", | ||
"Effect": "Allow", | ||
"Principal": {"Service": ["datapipeline.amazonaws.com", | ||
"elasticmapreduce.amazonaws.com"] | ||
}, | ||
"Action": "sts:AssumeRole" | ||
} | ||
] | ||
} |
212 changes: 212 additions & 0 deletions
212
awscli/customizations/datapipeline/createdefaultroles.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,212 @@ | ||
# 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 | ||
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__) | ||
|
||
|
||
class CreateDefaultRoles(BasicCommand): | ||
|
||
NAME = "create-default-roles" | ||
DESCRIPTION = ('Creates the default IAM role ' + | ||
DATAPIPELINE_DEFAULT_SERVICE_ROLE_NAME + ' and ' + | ||
DATAPIPELINE_DEFAULT_RESOURCE_ROLE_NAME + | ||
'\n which are used while creating an EMR cluster. ' | ||
'\nIf the roles do not exist, create-default-roles ' | ||
'will automatically create them and set their policies.' | ||
'\nIf these roles are already ' | ||
'created create-default-roles' | ||
'will not update their policies.' | ||
'\n') | ||
|
||
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', self._region, self._endpoint_url, | ||
parsed_globals.verify_ssl) | ||
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.