Path: blob/develop/awscli/customizations/emrcontainers/update_role_trust_policy.py
1567 views
# Copyright 2020 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 json14import logging1516from awscli.customizations.commands import BasicCommand17from awscli.customizations.emrcontainers.constants \18import TRUST_POLICY_STATEMENT_FORMAT, \19TRUST_POLICY_STATEMENT_ALREADY_EXISTS, \20TRUST_POLICY_UPDATE_SUCCESSFUL21from awscli.customizations.emrcontainers.base36 import Base3622from awscli.customizations.emrcontainers.eks import EKS23from awscli.customizations.emrcontainers.iam import IAM24from awscli.customizations.utils import uni_print, get_policy_arn_suffix2526LOG = logging.getLogger(__name__)272829# Method to parse the arguments to get the region value30def get_region(session, parsed_globals):31region = parsed_globals.region3233if region is None:34region = session.get_config_variable('region')3536return region373839def check_if_statement_exists(expected_statement, actual_assume_role_document):40if actual_assume_role_document is None:41return False4243existing_statements = actual_assume_role_document.get("Statement", [])44for existing_statement in existing_statements:45matches = check_if_dict_matches(expected_statement, existing_statement)46if matches:47return True4849return False505152def check_if_dict_matches(expected_dict, actual_dict):53if len(expected_dict) != len(actual_dict):54return False5556for key in expected_dict:57key_str = str(key)58val = expected_dict[key_str]59if isinstance(val, dict):60if not check_if_dict_matches(val, actual_dict.get(key_str, {})):61return False62else:63if key_str not in actual_dict or actual_dict[key_str] != str(val):64return False6566return True676869class UpdateRoleTrustPolicyCommand(BasicCommand):70NAME = 'update-role-trust-policy'7172DESCRIPTION = BasicCommand.FROM_FILE(73'emr-containers',74'update-role-trust-policy',75'_description.rst'76)7778ARG_TABLE = [79{80'name': 'cluster-name',81'help_text': ("Specify the name of the Amazon EKS cluster with "82"which the IAM Role would be used."),83'required': True84},85{86'name': 'namespace',87'help_text': ("Specify the namespace from the Amazon EKS cluster "88"with which the IAM Role would be used."),89'required': True90},91{92'name': 'role-name',93'help_text': ("Specify the IAM Role name that you want to use"94"with Amazon EMR on EKS."),95'required': True96},97{98'name': 'iam-endpoint',99'no_paramfile': True,100'help_text': ("The IAM endpoint to call for updating the role "101"trust policy. This is optional and should only be"102"specified when a custom endpoint should be called"103"for IAM operations."),104'required': False105},106{107'name': 'dry-run',108'action': 'store_true',109'default': False,110'help_text': ("Print the merged trust policy document to"111"stdout instead of updating the role trust"112"policy directly."),113'required': False114}115]116117def _run_main(self, parsed_args, parsed_globals):118"""Call to run the commands"""119120self._cluster_name = parsed_args.cluster_name121self._namespace = parsed_args.namespace122self._role_name = parsed_args.role_name123self._region = get_region(self._session, parsed_globals)124self._endpoint_url = parsed_args.iam_endpoint125self._dry_run = parsed_args.dry_run126127result = self._update_role_trust_policy(parsed_globals)128uni_print(result)129uni_print("\n")130131return 0132133def _update_role_trust_policy(self, parsed_globals):134"""Method to update trust policy if not done already"""135136base36 = Base36()137138eks_client = EKS(self._session.create_client(139'eks',140region_name=self._region,141verify=parsed_globals.verify_ssl142))143144account_id = eks_client.get_account_id(self._cluster_name)145oidc_provider = eks_client.get_oidc_issuer_id(self._cluster_name)146147base36_encoded_role_name = base36.encode(self._role_name)148LOG.debug('Base36 encoded role name: %s', base36_encoded_role_name)149trust_policy_statement = json.loads(TRUST_POLICY_STATEMENT_FORMAT % {150"AWS_ACCOUNT_ID": account_id,151"OIDC_PROVIDER": oidc_provider,152"NAMESPACE": self._namespace,153"BASE36_ENCODED_ROLE_NAME": base36_encoded_role_name,154"AWS_PARTITION": get_policy_arn_suffix(self._region)155})156157LOG.debug('Computed Trust Policy Statement:\n%s', json.dumps(158trust_policy_statement, indent=2))159iam_client = IAM(self._session.create_client(160'iam',161region_name=self._region,162endpoint_url=self._endpoint_url,163verify=parsed_globals.verify_ssl164))165166assume_role_document = iam_client.get_assume_role_policy(167self._role_name)168matches = check_if_statement_exists(trust_policy_statement,169assume_role_document)170171if not matches:172LOG.debug('Role %s does not have the required trust policy ',173self._role_name)174175existing_statements = assume_role_document.get("Statement")176if existing_statements is None:177assume_role_document["Statement"] = [trust_policy_statement]178else:179existing_statements.append(trust_policy_statement)180181if self._dry_run:182return json.dumps(assume_role_document, indent=2)183else:184LOG.debug('Updating trust policy of role %s', self._role_name)185iam_client.update_assume_role_policy(self._role_name,186assume_role_document)187return TRUST_POLICY_UPDATE_SUCCESSFUL % self._role_name188else:189return TRUST_POLICY_STATEMENT_ALREADY_EXISTS % self._role_name190191192