Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aws
GitHub Repository: aws/aws-cli
Path: blob/develop/awscli/customizations/emrcontainers/update_role_trust_policy.py
1567 views
1
# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
#
3
# Licensed under the Apache License, Version 2.0 (the "License"). You
4
# may not use this file except in compliance with the License. A copy of
5
# the License is located at
6
#
7
# http://aws.amazon.com/apache2.0/
8
#
9
# or in the "license" file accompanying this file. This file is
10
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
# ANY KIND, either express or implied. See the License for the specific
12
# language governing permissions and limitations under the License.
13
14
import json
15
import logging
16
17
from awscli.customizations.commands import BasicCommand
18
from awscli.customizations.emrcontainers.constants \
19
import TRUST_POLICY_STATEMENT_FORMAT, \
20
TRUST_POLICY_STATEMENT_ALREADY_EXISTS, \
21
TRUST_POLICY_UPDATE_SUCCESSFUL
22
from awscli.customizations.emrcontainers.base36 import Base36
23
from awscli.customizations.emrcontainers.eks import EKS
24
from awscli.customizations.emrcontainers.iam import IAM
25
from awscli.customizations.utils import uni_print, get_policy_arn_suffix
26
27
LOG = logging.getLogger(__name__)
28
29
30
# Method to parse the arguments to get the region value
31
def get_region(session, parsed_globals):
32
region = parsed_globals.region
33
34
if region is None:
35
region = session.get_config_variable('region')
36
37
return region
38
39
40
def check_if_statement_exists(expected_statement, actual_assume_role_document):
41
if actual_assume_role_document is None:
42
return False
43
44
existing_statements = actual_assume_role_document.get("Statement", [])
45
for existing_statement in existing_statements:
46
matches = check_if_dict_matches(expected_statement, existing_statement)
47
if matches:
48
return True
49
50
return False
51
52
53
def check_if_dict_matches(expected_dict, actual_dict):
54
if len(expected_dict) != len(actual_dict):
55
return False
56
57
for key in expected_dict:
58
key_str = str(key)
59
val = expected_dict[key_str]
60
if isinstance(val, dict):
61
if not check_if_dict_matches(val, actual_dict.get(key_str, {})):
62
return False
63
else:
64
if key_str not in actual_dict or actual_dict[key_str] != str(val):
65
return False
66
67
return True
68
69
70
class UpdateRoleTrustPolicyCommand(BasicCommand):
71
NAME = 'update-role-trust-policy'
72
73
DESCRIPTION = BasicCommand.FROM_FILE(
74
'emr-containers',
75
'update-role-trust-policy',
76
'_description.rst'
77
)
78
79
ARG_TABLE = [
80
{
81
'name': 'cluster-name',
82
'help_text': ("Specify the name of the Amazon EKS cluster with "
83
"which the IAM Role would be used."),
84
'required': True
85
},
86
{
87
'name': 'namespace',
88
'help_text': ("Specify the namespace from the Amazon EKS cluster "
89
"with which the IAM Role would be used."),
90
'required': True
91
},
92
{
93
'name': 'role-name',
94
'help_text': ("Specify the IAM Role name that you want to use"
95
"with Amazon EMR on EKS."),
96
'required': True
97
},
98
{
99
'name': 'iam-endpoint',
100
'no_paramfile': True,
101
'help_text': ("The IAM endpoint to call for updating the role "
102
"trust policy. This is optional and should only be"
103
"specified when a custom endpoint should be called"
104
"for IAM operations."),
105
'required': False
106
},
107
{
108
'name': 'dry-run',
109
'action': 'store_true',
110
'default': False,
111
'help_text': ("Print the merged trust policy document to"
112
"stdout instead of updating the role trust"
113
"policy directly."),
114
'required': False
115
}
116
]
117
118
def _run_main(self, parsed_args, parsed_globals):
119
"""Call to run the commands"""
120
121
self._cluster_name = parsed_args.cluster_name
122
self._namespace = parsed_args.namespace
123
self._role_name = parsed_args.role_name
124
self._region = get_region(self._session, parsed_globals)
125
self._endpoint_url = parsed_args.iam_endpoint
126
self._dry_run = parsed_args.dry_run
127
128
result = self._update_role_trust_policy(parsed_globals)
129
uni_print(result)
130
uni_print("\n")
131
132
return 0
133
134
def _update_role_trust_policy(self, parsed_globals):
135
"""Method to update trust policy if not done already"""
136
137
base36 = Base36()
138
139
eks_client = EKS(self._session.create_client(
140
'eks',
141
region_name=self._region,
142
verify=parsed_globals.verify_ssl
143
))
144
145
account_id = eks_client.get_account_id(self._cluster_name)
146
oidc_provider = eks_client.get_oidc_issuer_id(self._cluster_name)
147
148
base36_encoded_role_name = base36.encode(self._role_name)
149
LOG.debug('Base36 encoded role name: %s', base36_encoded_role_name)
150
trust_policy_statement = json.loads(TRUST_POLICY_STATEMENT_FORMAT % {
151
"AWS_ACCOUNT_ID": account_id,
152
"OIDC_PROVIDER": oidc_provider,
153
"NAMESPACE": self._namespace,
154
"BASE36_ENCODED_ROLE_NAME": base36_encoded_role_name,
155
"AWS_PARTITION": get_policy_arn_suffix(self._region)
156
})
157
158
LOG.debug('Computed Trust Policy Statement:\n%s', json.dumps(
159
trust_policy_statement, indent=2))
160
iam_client = IAM(self._session.create_client(
161
'iam',
162
region_name=self._region,
163
endpoint_url=self._endpoint_url,
164
verify=parsed_globals.verify_ssl
165
))
166
167
assume_role_document = iam_client.get_assume_role_policy(
168
self._role_name)
169
matches = check_if_statement_exists(trust_policy_statement,
170
assume_role_document)
171
172
if not matches:
173
LOG.debug('Role %s does not have the required trust policy ',
174
self._role_name)
175
176
existing_statements = assume_role_document.get("Statement")
177
if existing_statements is None:
178
assume_role_document["Statement"] = [trust_policy_statement]
179
else:
180
existing_statements.append(trust_policy_statement)
181
182
if self._dry_run:
183
return json.dumps(assume_role_document, indent=2)
184
else:
185
LOG.debug('Updating trust policy of role %s', self._role_name)
186
iam_client.update_assume_role_policy(self._role_name,
187
assume_role_document)
188
return TRUST_POLICY_UPDATE_SUCCESSFUL % self._role_name
189
else:
190
return TRUST_POLICY_STATEMENT_ALREADY_EXISTS % self._role_name
191
192