bedrock-agentcore-starter-toolkit 0.1.0__py3-none-any.whl → 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of bedrock-agentcore-starter-toolkit might be problematic. Click here for more details.
- bedrock_agentcore_starter_toolkit/cli/cli.py +3 -10
- bedrock_agentcore_starter_toolkit/cli/runtime/commands.py +52 -4
- bedrock_agentcore_starter_toolkit/cli/runtime/configuration_manager.py +20 -11
- bedrock_agentcore_starter_toolkit/notebook/runtime/bedrock_agentcore.py +53 -10
- bedrock_agentcore_starter_toolkit/operations/gateway/README.md +6 -6
- bedrock_agentcore_starter_toolkit/operations/gateway/create_role.py +11 -10
- bedrock_agentcore_starter_toolkit/operations/runtime/configure.py +21 -7
- bedrock_agentcore_starter_toolkit/operations/runtime/create_role.py +404 -0
- bedrock_agentcore_starter_toolkit/operations/runtime/launch.py +329 -53
- bedrock_agentcore_starter_toolkit/operations/runtime/models.py +4 -1
- bedrock_agentcore_starter_toolkit/services/codebuild.py +332 -0
- bedrock_agentcore_starter_toolkit/services/ecr.py +29 -0
- bedrock_agentcore_starter_toolkit/services/runtime.py +91 -1
- bedrock_agentcore_starter_toolkit/utils/logging_config.py +72 -0
- bedrock_agentcore_starter_toolkit/utils/runtime/entrypoint.py +3 -3
- bedrock_agentcore_starter_toolkit/utils/runtime/policy_template.py +74 -0
- bedrock_agentcore_starter_toolkit/utils/runtime/schema.py +12 -2
- bedrock_agentcore_starter_toolkit/utils/runtime/templates/Dockerfile.j2 +10 -25
- bedrock_agentcore_starter_toolkit/utils/runtime/templates/dockerignore.template +0 -1
- bedrock_agentcore_starter_toolkit/utils/runtime/templates/execution_role_policy.json.j2 +98 -0
- bedrock_agentcore_starter_toolkit/utils/runtime/templates/execution_role_trust_policy.json.j2 +21 -0
- {bedrock_agentcore_starter_toolkit-0.1.0.dist-info → bedrock_agentcore_starter_toolkit-0.1.1.dist-info}/METADATA +7 -7
- {bedrock_agentcore_starter_toolkit-0.1.0.dist-info → bedrock_agentcore_starter_toolkit-0.1.1.dist-info}/RECORD +27 -21
- {bedrock_agentcore_starter_toolkit-0.1.0.dist-info → bedrock_agentcore_starter_toolkit-0.1.1.dist-info}/WHEEL +0 -0
- {bedrock_agentcore_starter_toolkit-0.1.0.dist-info → bedrock_agentcore_starter_toolkit-0.1.1.dist-info}/entry_points.txt +0 -0
- {bedrock_agentcore_starter_toolkit-0.1.0.dist-info → bedrock_agentcore_starter_toolkit-0.1.1.dist-info}/licenses/LICENSE.txt +0 -0
- {bedrock_agentcore_starter_toolkit-0.1.0.dist-info → bedrock_agentcore_starter_toolkit-0.1.1.dist-info}/licenses/NOTICE.txt +0 -0
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
"""Creates an execution role to use in the Bedrock AgentCore Runtime module."""
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
import json
|
|
5
|
+
import logging
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
from boto3 import Session
|
|
9
|
+
from botocore.client import BaseClient
|
|
10
|
+
from botocore.exceptions import ClientError
|
|
11
|
+
|
|
12
|
+
from ...utils.runtime.policy_template import (
|
|
13
|
+
render_execution_policy_template,
|
|
14
|
+
render_trust_policy_template,
|
|
15
|
+
validate_rendered_policy,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _generate_deterministic_suffix(agent_name: str, length: int = 10) -> str:
|
|
20
|
+
"""Generate a deterministic suffix for role names based on agent name.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
agent_name: Name of the agent
|
|
24
|
+
length: Length of the suffix (default: 10)
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
Deterministic alphanumeric string in lowercase
|
|
28
|
+
"""
|
|
29
|
+
# Create deterministic hash from agent name
|
|
30
|
+
hash_object = hashlib.sha256(agent_name.encode())
|
|
31
|
+
hex_hash = hash_object.hexdigest()
|
|
32
|
+
|
|
33
|
+
# Take first N characters for AWS resource names
|
|
34
|
+
return hex_hash[:length].lower()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_or_create_runtime_execution_role(
|
|
38
|
+
session: Session,
|
|
39
|
+
logger: logging.Logger,
|
|
40
|
+
region: str,
|
|
41
|
+
account_id: str,
|
|
42
|
+
agent_name: str,
|
|
43
|
+
role_name: Optional[str] = None,
|
|
44
|
+
) -> str:
|
|
45
|
+
"""Get existing execution role or create a new one (idempotent).
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
session: Boto3 session
|
|
49
|
+
logger: Logger instance
|
|
50
|
+
region: AWS region
|
|
51
|
+
account_id: AWS account ID
|
|
52
|
+
agent_name: Agent name for resource scoping
|
|
53
|
+
role_name: Optional custom role name
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
Role ARN
|
|
57
|
+
|
|
58
|
+
Raises:
|
|
59
|
+
RuntimeError: If role creation fails
|
|
60
|
+
"""
|
|
61
|
+
if not role_name:
|
|
62
|
+
# Generate deterministic role name based on agent name
|
|
63
|
+
# This ensures the same agent always gets the same role name
|
|
64
|
+
deterministic_suffix = _generate_deterministic_suffix(agent_name)
|
|
65
|
+
role_name = f"AmazonBedrockAgentCoreSDKRuntime-{region}-{deterministic_suffix}"
|
|
66
|
+
|
|
67
|
+
logger.info("Getting or creating execution role for agent: %s", agent_name)
|
|
68
|
+
logger.info("Using AWS region: %s, account ID: %s", region, account_id)
|
|
69
|
+
logger.info("Role name: %s", role_name)
|
|
70
|
+
|
|
71
|
+
iam = session.client("iam")
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
# Step 1: Check if role already exists
|
|
75
|
+
logger.debug("Checking if role exists: %s", role_name)
|
|
76
|
+
role = iam.get_role(RoleName=role_name)
|
|
77
|
+
existing_role_arn = role["Role"]["Arn"]
|
|
78
|
+
|
|
79
|
+
logger.info("✅ Reusing existing execution role: %s", existing_role_arn)
|
|
80
|
+
logger.debug("Role creation date: %s", role["Role"].get("CreateDate", "Unknown"))
|
|
81
|
+
|
|
82
|
+
# TODO: In future, we could add validation here to ensure the role has correct policies
|
|
83
|
+
# For now, we trust that if the role exists with our naming pattern, it's compatible
|
|
84
|
+
|
|
85
|
+
return existing_role_arn
|
|
86
|
+
|
|
87
|
+
except ClientError as e:
|
|
88
|
+
if e.response["Error"]["Code"] == "NoSuchEntity":
|
|
89
|
+
# Step 2: Role doesn't exist, create it
|
|
90
|
+
logger.info("Role doesn't exist, creating new execution role: %s", role_name)
|
|
91
|
+
|
|
92
|
+
# Inline role creation logic (previously in create_runtime_execution_role)
|
|
93
|
+
logger.info("Starting execution role creation process for agent: %s", agent_name)
|
|
94
|
+
logger.info("✓ Role creating: %s", role_name)
|
|
95
|
+
|
|
96
|
+
try:
|
|
97
|
+
# Render the trust policy template
|
|
98
|
+
trust_policy_json = render_trust_policy_template(region, account_id)
|
|
99
|
+
trust_policy = validate_rendered_policy(trust_policy_json)
|
|
100
|
+
|
|
101
|
+
# Render the execution policy template
|
|
102
|
+
execution_policy_json = render_execution_policy_template(region, account_id, agent_name)
|
|
103
|
+
execution_policy = validate_rendered_policy(execution_policy_json)
|
|
104
|
+
|
|
105
|
+
logger.info("Creating IAM role: %s", role_name)
|
|
106
|
+
|
|
107
|
+
# Create the role with the trust policy
|
|
108
|
+
role = iam.create_role(
|
|
109
|
+
RoleName=role_name,
|
|
110
|
+
AssumeRolePolicyDocument=json.dumps(trust_policy),
|
|
111
|
+
Description=f"Execution role for BedrockAgentCore Runtime - {agent_name}",
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
role_arn = role["Role"]["Arn"]
|
|
115
|
+
logger.info("✓ Role created: %s", role_arn)
|
|
116
|
+
|
|
117
|
+
# Create and attach the inline execution policy
|
|
118
|
+
policy_name = f"BedrockAgentCoreRuntimeExecutionPolicy-{agent_name}"
|
|
119
|
+
|
|
120
|
+
_attach_inline_policy(
|
|
121
|
+
iam_client=iam,
|
|
122
|
+
role_name=role_name,
|
|
123
|
+
policy_name=policy_name,
|
|
124
|
+
policy_document=json.dumps(execution_policy),
|
|
125
|
+
logger=logger,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
logger.info("✓ Execution policy attached: %s", policy_name)
|
|
129
|
+
logger.info("Role creation complete and ready for use with Bedrock AgentCore")
|
|
130
|
+
|
|
131
|
+
return role_arn
|
|
132
|
+
|
|
133
|
+
except ClientError as create_error:
|
|
134
|
+
if create_error.response["Error"]["Code"] == "EntityAlreadyExists":
|
|
135
|
+
try:
|
|
136
|
+
logger.info("Role %s already exists, retrieving existing role...", role_name)
|
|
137
|
+
role = iam.get_role(RoleName=role_name)
|
|
138
|
+
logger.info("✓ Role already exists: %s", role["Role"]["Arn"])
|
|
139
|
+
return role["Role"]["Arn"]
|
|
140
|
+
except ClientError as get_error:
|
|
141
|
+
logger.error("Error getting existing role: %s", get_error)
|
|
142
|
+
raise RuntimeError(f"Failed to get existing role: {get_error}") from get_error
|
|
143
|
+
else:
|
|
144
|
+
logger.error("Error creating role: %s", create_error)
|
|
145
|
+
if create_error.response["Error"]["Code"] == "AccessDenied":
|
|
146
|
+
logger.error(
|
|
147
|
+
"Access denied. Ensure your AWS credentials have sufficient IAM permissions "
|
|
148
|
+
"to create roles and policies."
|
|
149
|
+
)
|
|
150
|
+
elif create_error.response["Error"]["Code"] == "LimitExceeded":
|
|
151
|
+
logger.error(
|
|
152
|
+
"AWS limit exceeded. You may have reached the maximum number of IAM roles "
|
|
153
|
+
"allowed in your account."
|
|
154
|
+
)
|
|
155
|
+
raise RuntimeError(f"Failed to create role: {create_error}") from create_error
|
|
156
|
+
else:
|
|
157
|
+
logger.error("Error checking role existence: %s", e)
|
|
158
|
+
raise RuntimeError(f"Failed to check role existence: {e}") from e
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _create_iam_role_with_policies(
|
|
162
|
+
session: Session,
|
|
163
|
+
logger: logging.Logger,
|
|
164
|
+
role_name: str,
|
|
165
|
+
trust_policy: dict,
|
|
166
|
+
inline_policies: dict, # {policy_name: policy_document}
|
|
167
|
+
description: str,
|
|
168
|
+
) -> str:
|
|
169
|
+
"""Generic IAM role creation with inline policies.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
session: Boto3 session
|
|
173
|
+
logger: Logger instance
|
|
174
|
+
role_name: Name for the IAM role
|
|
175
|
+
trust_policy: Trust policy document (dict)
|
|
176
|
+
inline_policies: Dictionary of {policy_name: policy_document}
|
|
177
|
+
description: Role description
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
Role ARN
|
|
181
|
+
|
|
182
|
+
Raises:
|
|
183
|
+
RuntimeError: If role creation fails
|
|
184
|
+
"""
|
|
185
|
+
iam = session.client("iam")
|
|
186
|
+
|
|
187
|
+
try:
|
|
188
|
+
logger.info("Creating IAM role: %s", role_name)
|
|
189
|
+
|
|
190
|
+
# Create the role with trust policy
|
|
191
|
+
role = iam.create_role(
|
|
192
|
+
RoleName=role_name,
|
|
193
|
+
AssumeRolePolicyDocument=json.dumps(trust_policy),
|
|
194
|
+
Description=description,
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
role_arn = role["Role"]["Arn"]
|
|
198
|
+
logger.info("✓ Role created: %s", role_arn)
|
|
199
|
+
|
|
200
|
+
# Attach inline policies
|
|
201
|
+
for policy_name, policy_document in inline_policies.items():
|
|
202
|
+
logger.info("Attaching inline policy: %s to role: %s", policy_name, role_name)
|
|
203
|
+
_attach_inline_policy(
|
|
204
|
+
iam_client=iam,
|
|
205
|
+
role_name=role_name,
|
|
206
|
+
policy_name=policy_name,
|
|
207
|
+
policy_document=json.dumps(policy_document) if isinstance(policy_document, dict) else policy_document,
|
|
208
|
+
logger=logger,
|
|
209
|
+
)
|
|
210
|
+
logger.info("✓ Policy attached: %s", policy_name)
|
|
211
|
+
|
|
212
|
+
return role_arn
|
|
213
|
+
|
|
214
|
+
except ClientError as e:
|
|
215
|
+
if e.response["Error"]["Code"] == "EntityAlreadyExists":
|
|
216
|
+
try:
|
|
217
|
+
logger.info("Role %s already exists, retrieving existing role...", role_name)
|
|
218
|
+
role = iam.get_role(RoleName=role_name)
|
|
219
|
+
logger.info("✓ Role already exists: %s", role["Role"]["Arn"])
|
|
220
|
+
|
|
221
|
+
# Update existing policies
|
|
222
|
+
for policy_name, policy_document in inline_policies.items():
|
|
223
|
+
logger.info("Updating inline policy: %s on existing role: %s", policy_name, role_name)
|
|
224
|
+
_attach_inline_policy(
|
|
225
|
+
iam_client=iam,
|
|
226
|
+
role_name=role_name,
|
|
227
|
+
policy_name=policy_name,
|
|
228
|
+
policy_document=json.dumps(policy_document)
|
|
229
|
+
if isinstance(policy_document, dict)
|
|
230
|
+
else policy_document,
|
|
231
|
+
logger=logger,
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
return role["Role"]["Arn"]
|
|
235
|
+
except ClientError as get_error:
|
|
236
|
+
logger.error("Error getting existing role: %s", get_error)
|
|
237
|
+
raise RuntimeError(f"Failed to get existing role: {get_error}") from get_error
|
|
238
|
+
else:
|
|
239
|
+
logger.error("Error creating role: %s", e)
|
|
240
|
+
if e.response["Error"]["Code"] == "AccessDenied":
|
|
241
|
+
logger.error(
|
|
242
|
+
"Access denied. Ensure your AWS credentials have sufficient IAM permissions "
|
|
243
|
+
"to create roles and policies."
|
|
244
|
+
)
|
|
245
|
+
elif e.response["Error"]["Code"] == "LimitExceeded":
|
|
246
|
+
logger.error(
|
|
247
|
+
"AWS limit exceeded. You may have reached the maximum number of IAM roles allowed in your account."
|
|
248
|
+
)
|
|
249
|
+
raise RuntimeError(f"Failed to create role: {e}") from e
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def _attach_inline_policy(
|
|
253
|
+
iam_client: BaseClient,
|
|
254
|
+
role_name: str,
|
|
255
|
+
policy_name: str,
|
|
256
|
+
policy_document: str,
|
|
257
|
+
logger: logging.Logger,
|
|
258
|
+
) -> None:
|
|
259
|
+
"""Attach an inline policy to an IAM role.
|
|
260
|
+
|
|
261
|
+
Args:
|
|
262
|
+
iam_client: IAM client instance
|
|
263
|
+
role_name: Name of the role
|
|
264
|
+
policy_name: Name of the policy
|
|
265
|
+
policy_document: Policy document JSON string
|
|
266
|
+
logger: Logger instance
|
|
267
|
+
|
|
268
|
+
Raises:
|
|
269
|
+
RuntimeError: If policy attachment fails
|
|
270
|
+
"""
|
|
271
|
+
try:
|
|
272
|
+
logger.debug("Attaching inline policy %s to role %s", policy_name, role_name)
|
|
273
|
+
logger.debug("Policy document size: %d bytes", len(policy_document))
|
|
274
|
+
|
|
275
|
+
iam_client.put_role_policy(
|
|
276
|
+
RoleName=role_name,
|
|
277
|
+
PolicyName=policy_name,
|
|
278
|
+
PolicyDocument=policy_document,
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
logger.debug("Successfully attached policy %s to role %s", policy_name, role_name)
|
|
282
|
+
except ClientError as e:
|
|
283
|
+
logger.error("Error attaching policy %s to role %s: %s", policy_name, role_name, e)
|
|
284
|
+
if e.response["Error"]["Code"] == "MalformedPolicyDocument":
|
|
285
|
+
logger.error("Policy document is malformed. Check the JSON syntax.")
|
|
286
|
+
elif e.response["Error"]["Code"] == "LimitExceeded":
|
|
287
|
+
logger.error("Policy size limit exceeded or too many policies attached to the role.")
|
|
288
|
+
raise RuntimeError(f"Failed to attach policy {policy_name}: {e}") from e
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def get_or_create_codebuild_execution_role(
|
|
292
|
+
session: Session,
|
|
293
|
+
logger: logging.Logger,
|
|
294
|
+
region: str,
|
|
295
|
+
account_id: str,
|
|
296
|
+
agent_name: str,
|
|
297
|
+
ecr_repository_arn: str,
|
|
298
|
+
source_bucket_name: str,
|
|
299
|
+
) -> str:
|
|
300
|
+
"""Get existing CodeBuild execution role or create a new one (idempotent).
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
session: Boto3 session
|
|
304
|
+
logger: Logger instance
|
|
305
|
+
region: AWS region
|
|
306
|
+
account_id: AWS account ID
|
|
307
|
+
agent_name: Agent name for resource scoping
|
|
308
|
+
ecr_repository_arn: ECR repository ARN for permissions
|
|
309
|
+
source_bucket_name: S3 source bucket name for permissions
|
|
310
|
+
|
|
311
|
+
Returns:
|
|
312
|
+
Role ARN
|
|
313
|
+
|
|
314
|
+
Raises:
|
|
315
|
+
RuntimeError: If role creation fails
|
|
316
|
+
"""
|
|
317
|
+
# Generate deterministic role name based on agent name
|
|
318
|
+
deterministic_suffix = _generate_deterministic_suffix(agent_name)
|
|
319
|
+
role_name = f"AmazonBedrockAgentCoreSDKCodeBuild-{region}-{deterministic_suffix}"
|
|
320
|
+
|
|
321
|
+
logger.info("Getting or creating CodeBuild execution role for agent: %s", agent_name)
|
|
322
|
+
logger.info("Role name: %s", role_name)
|
|
323
|
+
|
|
324
|
+
iam = session.client("iam")
|
|
325
|
+
|
|
326
|
+
try:
|
|
327
|
+
# Step 1: Check if role already exists
|
|
328
|
+
logger.debug("Checking if CodeBuild role exists: %s", role_name)
|
|
329
|
+
role = iam.get_role(RoleName=role_name)
|
|
330
|
+
existing_role_arn = role["Role"]["Arn"]
|
|
331
|
+
|
|
332
|
+
logger.info("Reusing existing CodeBuild execution role: %s", existing_role_arn)
|
|
333
|
+
return existing_role_arn
|
|
334
|
+
|
|
335
|
+
except ClientError as e:
|
|
336
|
+
if e.response["Error"]["Code"] == "NoSuchEntity":
|
|
337
|
+
# Step 2: Role doesn't exist, create it
|
|
338
|
+
logger.info("CodeBuild role doesn't exist, creating new role: %s", role_name)
|
|
339
|
+
|
|
340
|
+
# Define trust policy for CodeBuild service
|
|
341
|
+
trust_policy = {
|
|
342
|
+
"Version": "2012-10-17",
|
|
343
|
+
"Statement": [
|
|
344
|
+
{
|
|
345
|
+
"Effect": "Allow",
|
|
346
|
+
"Principal": {"Service": "codebuild.amazonaws.com"},
|
|
347
|
+
"Action": "sts:AssumeRole",
|
|
348
|
+
"Condition": {"StringEquals": {"aws:SourceAccount": account_id}},
|
|
349
|
+
}
|
|
350
|
+
],
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
# Define permissions policy for CodeBuild operations
|
|
354
|
+
permissions_policy = {
|
|
355
|
+
"Version": "2012-10-17",
|
|
356
|
+
"Statement": [
|
|
357
|
+
{"Effect": "Allow", "Action": ["ecr:GetAuthorizationToken"], "Resource": "*"},
|
|
358
|
+
{
|
|
359
|
+
"Effect": "Allow",
|
|
360
|
+
"Action": [
|
|
361
|
+
"ecr:BatchCheckLayerAvailability",
|
|
362
|
+
"ecr:BatchGetImage",
|
|
363
|
+
"ecr:GetDownloadUrlForLayer",
|
|
364
|
+
"ecr:PutImage",
|
|
365
|
+
"ecr:InitiateLayerUpload",
|
|
366
|
+
"ecr:UploadLayerPart",
|
|
367
|
+
"ecr:CompleteLayerUpload",
|
|
368
|
+
],
|
|
369
|
+
"Resource": ecr_repository_arn,
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
"Effect": "Allow",
|
|
373
|
+
"Action": ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"],
|
|
374
|
+
"Resource": f"arn:aws:logs:{region}:{account_id}:log-group:/aws/codebuild/bedrock-agentcore-*",
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
"Effect": "Allow",
|
|
378
|
+
"Action": ["s3:GetObject"],
|
|
379
|
+
"Resource": f"arn:aws:s3:::{source_bucket_name}/*",
|
|
380
|
+
},
|
|
381
|
+
],
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
# Create role using shared logic
|
|
385
|
+
role_arn = _create_iam_role_with_policies(
|
|
386
|
+
session=session,
|
|
387
|
+
logger=logger,
|
|
388
|
+
role_name=role_name,
|
|
389
|
+
trust_policy=trust_policy,
|
|
390
|
+
inline_policies={"CodeBuildExecutionPolicy": permissions_policy},
|
|
391
|
+
description="CodeBuild execution role for Bedrock AgentCore ARM64 builds",
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
# Wait for IAM propagation to prevent CodeBuild authorization errors
|
|
395
|
+
logger.info("Waiting for IAM role propagation...")
|
|
396
|
+
import time
|
|
397
|
+
|
|
398
|
+
time.sleep(15)
|
|
399
|
+
|
|
400
|
+
logger.info("CodeBuild execution role creation complete: %s", role_arn)
|
|
401
|
+
return role_arn
|
|
402
|
+
else:
|
|
403
|
+
logger.error("Error checking CodeBuild role existence: %s", e)
|
|
404
|
+
raise RuntimeError(f"Failed to check CodeBuild role existence: {e}") from e
|