Repository: awslabs/generative-ai-cdk-constructs
Pull request #250 (2024-02-07 13:44:57)

feat(bedrock): replace PrepareAgent with shouldPrepareAgent prop
Per discussions with @massi-ang and the CloudFormation team, there shouldn't be a resource to call PrepareAgent as it doesn't create anything. This change replaces the custom resource with an optional property called `shouldPrepareAgent` that calls and waits for the agent to be prepared after creating or updating the agent, action group, or knowledge base association custom resources.

Alias creation and updates still prepare the agent first. Developers can choose whether to use `shouldPrepareAgent` or create an alias that tracks the latest changes to ensure that their agent is prepared. This change makes the alias custom resource use the same function to prepare the agent and wait as the others.

This change replaces the complicated `changeId` hashes with the `updatedAt` timestamps returned by the SDK in order to determine when to update the alias.

Custom Resource exceptions are reraised by the retry mechanism.

---
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of the project license.


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/awslabs/generative-ai-cdk-constructs/pull/250?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=awslabs) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Comparison is base [(`d622eca`)](https://app.codecov.io/gh/awslabs/generative-ai-cdk-constructs/commit/d622eca0fe2dba489958c114812aeff451e319cb?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=awslabs) 94.88% compared to head [(`0722c2c`)](https://app.codecov.io/gh/awslabs/generative-ai-cdk-constructs/pull/250?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=awslabs) 94.88%.


<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##             main     #250   +/-   ##
=======================================
  Coverage   94.88%   94.88%           
=======================================
  Files          35       35           
  Lines        8389     8391    +2     
  Branches      244      244           
=======================================
+ Hits         7960     7962    +2     
  Misses        429      429           
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/awslabs/generative-ai-cdk-constructs/pull/250?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=awslabs).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=awslabs).


Patch:
diff --git a/apidocs/classes/bedrock.Agent.md b/apidocs/classes/bedrock.Agent.md
index 4e76325..07b807a 100644
--- a/apidocs/classes/bedrock.Agent.md
+++ b/apidocs/classes/bedrock.Agent.md
@@ -30,15 +30,15 @@ Deploy a Bedrock Agent.
 - [aliasId](bedrock.Agent.md#aliasid)
 - [aliasName](bedrock.Agent.md#aliasname)
 - [cdkTagManager](bedrock.Agent.md#cdktagmanager)
-- [changeIds](bedrock.Agent.md#changeids)
 - [name](bedrock.Agent.md#name)
 - [node](bedrock.Agent.md#node)
-- [prepareAgent](bedrock.Agent.md#prepareagent)
+- [resourceUpdates](bedrock.Agent.md#resourceupdates)
 - [role](bedrock.Agent.md#role)
+- [shouldPrepareAgent](bedrock.Agent.md#shouldprepareagent)
 
 ### Methods
 
-- [\_addPrepareAgentDependency](bedrock.Agent.md#_addprepareagentdependency)
+- [\_addAliasDependency](bedrock.Agent.md#_addaliasdependency)
 - [addActionGroup](bedrock.Agent.md#addactiongroup)
 - [addAlias](bedrock.Agent.md#addalias)
 - [toString](bedrock.Agent.md#tostring)
@@ -120,14 +120,6 @@ cdk.ITaggableV2.cdkTagManager
 
 ___
 
-### changeIds
-
-• `Private` **changeIds**: `string`[] = `[]`
-
-A list of values to indicate if PrepareAgent or an Alias needs to be updated.
-
-___
-
 ### name
 
 • `Readonly` **name**: `string`
@@ -148,13 +140,11 @@ Construct.node
 
 ___
 
-### prepareAgent
-
-• `Private` **prepareAgent**: `CustomResource`
+### resourceUpdates
 
-The prepareAgent custom resource.
+• `Private` **resourceUpdates**: `string`[] = `[]`
 
-Add other resources as dependencies to ensure Prepare Agent is called after they are updated.
+A list of values to indicate if PrepareAgent or an Alias needs to be updated.
 
 ___
 
@@ -164,20 +154,27 @@ ___
 
 The IAM role for the agent.
 
+___
+
+### shouldPrepareAgent
+
+• `Private` `Readonly` **shouldPrepareAgent**: `boolean`
+
+If prepare agent should be called on resource updates.
+
 ## Methods
 
-### \_addPrepareAgentDependency
+### \_addAliasDependency
 
-▸ **_addPrepareAgentDependency**(`resource`, `changeId?`): `void`
+▸ **_addAliasDependency**(`updatedAt`): `void`
 
-Register a dependency for prepareAgent.
+Register a dependency for aliases.
 
 #### Parameters
 
 | Name | Type | Description |
 | :------ | :------ | :------ |
-| `resource` | `IResource` | The resource that will be registered as a dependency. |
-| `changeId?` | `string` | The changeId of the resource that will be registered as a dependency. This is an internal core function and should not be called directly. |
+| `updatedAt` | `string` | The updatedAt of the resource that will be registered as a dependency. This is an internal core function and should not be called directly. |
 
 #### Returns
 
diff --git a/apidocs/interfaces/bedrock.AgentActionGroupProps.md b/apidocs/interfaces/bedrock.AgentActionGroupProps.md
index beae2c7..14e4839 100644
--- a/apidocs/interfaces/bedrock.AgentActionGroupProps.md
+++ b/apidocs/interfaces/bedrock.AgentActionGroupProps.md
@@ -15,6 +15,7 @@
 - [apiSchema](bedrock.AgentActionGroupProps.md#apischema)
 - [description](bedrock.AgentActionGroupProps.md#description)
 - [parentActionGroupSignature](bedrock.AgentActionGroupProps.md#parentactiongroupsignature)
+- [shouldPrepareAgent](bedrock.AgentActionGroupProps.md#shouldprepareagent)
 
 ## Properties
 
@@ -84,3 +85,17 @@ ___
 If you specify this value as AMAZON.UserInput, the agent will prompt additional information from the user when it
 doesn't have enough information to respond to an utterance. Leave this field blank if you don't want the agent to
 prompt additional information.
+
+___
+
+### shouldPrepareAgent
+
+• `Optional` `Readonly` **shouldPrepareAgent**: `boolean`
+
+Whether to prepare the agent for use.
+
+**`Default`**
+
+```ts
+- false
+```
diff --git a/apidocs/interfaces/bedrock.AgentAliasProps.md b/apidocs/interfaces/bedrock.AgentAliasProps.md
index 8c176af..1971431 100644
--- a/apidocs/interfaces/bedrock.AgentAliasProps.md
+++ b/apidocs/interfaces/bedrock.AgentAliasProps.md
@@ -11,7 +11,7 @@
 - [agentId](bedrock.AgentAliasProps.md#agentid)
 - [agentVersion](bedrock.AgentAliasProps.md#agentversion)
 - [aliasName](bedrock.AgentAliasProps.md#aliasname)
-- [changeIds](bedrock.AgentAliasProps.md#changeids)
+- [resourceUpdates](bedrock.AgentAliasProps.md#resourceupdates)
 
 ## Properties
 
@@ -51,9 +51,8 @@ The name for the agent alias.
 
 ___
 
-### changeIds
+### resourceUpdates
 
-• `Optional` `Readonly` **changeIds**: `string`[]
+• `Optional` `Readonly` **resourceUpdates**: `string`[]
 
-The list of change ids to let CloudFormation determine when to update the alias.
-A changeId is a hash of the properties of an agent, an agent/knowledge base association, or an action group.
+The list of resource update timestamps to let CloudFormation determine when to update the alias.
diff --git a/apidocs/interfaces/bedrock.AgentProps.md b/apidocs/interfaces/bedrock.AgentProps.md
index fac574e..8206106 100644
--- a/apidocs/interfaces/bedrock.AgentProps.md
+++ b/apidocs/interfaces/bedrock.AgentProps.md
@@ -19,6 +19,7 @@ Properties for a Bedrock Agent.
 - [knowledgeBases](bedrock.AgentProps.md#knowledgebases)
 - [name](bedrock.AgentProps.md#name)
 - [promptOverrideConfiguration](bedrock.AgentProps.md#promptoverrideconfiguration)
+- [shouldPrepareAgent](bedrock.AgentProps.md#shouldprepareagent)
 
 ## Properties
 
@@ -133,3 +134,17 @@ Overrides for the agent.
 ```ts
 - No overrides are provided.
 ```
+
+___
+
+### shouldPrepareAgent
+
+• `Optional` `Readonly` **shouldPrepareAgent**: `boolean`
+
+Whether to prepare the agent for use.
+
+**`Default`**
+
+```ts
+- false
+```
diff --git a/lambda/bedrock-custom-resources/custom_resources/__init__.py b/lambda/bedrock-custom-resources/custom_resources/__init__.py
index 1bdcd78..9f50d6c 100644
--- a/lambda/bedrock-custom-resources/custom_resources/__init__.py
+++ b/lambda/bedrock-custom-resources/custom_resources/__init__.py
@@ -26,7 +26,6 @@ from .bedrock_agent_knowledgebase import (
     on_event as on_event_bedrock_agent_knowledgebase,
 )
 from .bedrock_agent_action_group import on_event as on_event_bedrock_agent_action_group
-from .bedrock_prepare_agent import on_event as on_event_bedrock_prepare_agent
 
 LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")
 
@@ -50,8 +49,6 @@ def on_event(event: CustomResourceRequest, context):
         return on_event_bedrock_knowledgebase(event, context)
     if resource_type == "Custom::Bedrock-DataSource":
         return on_event_bedrock_datasource(event, context)
-    if resource_type == "Custom::Bedrock-PrepareAgent":
-        return on_event_bedrock_prepare_agent(event, context)
     if resource_type == "Custom::NoOp":
         logger.info("NoOp resource type")
         # Return a response with a physical resource ID that is not empty.
diff --git a/lambda/bedrock-custom-resources/custom_resources/bedrock_agent.py b/lambda/bedrock-custom-resources/custom_resources/bedrock_agent.py
index d9c67cb..15e3443 100644
--- a/lambda/bedrock-custom-resources/custom_resources/bedrock_agent.py
+++ b/lambda/bedrock-custom-resources/custom_resources/bedrock_agent.py
@@ -10,8 +10,6 @@
 # OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions
 # and limitations under the License.
 #
-import pickle  # nosec B403: pickle is used to create a hashable value. The value will never be deserialized.
-
 import uuid
 
 import boto3
@@ -27,6 +25,7 @@ from tenacity import (
 
 from typing import TypedDict, NotRequired, Dict, Literal, Sequence
 
+from .bedrock_prepare_agent import prepare_agent
 from .cr_types import CustomResourceRequest, CustomResourceResponse
 from .exceptions import AWSRetryableError, can_retry
 
@@ -75,13 +74,14 @@ class AgentRequest(TypedDict):
     customerEncryptionKeyArn: NotRequired[str]
     promptOverrideConfiguration: NotRequired[PromptOverride]
     tags: NotRequired[Dict[str, str]]
+    shouldPrepareAgent: NotRequired[str]
 
 
 class AgentResponse(TypedDict):
     agentId: str
     agentArn: NotRequired[str]
     agentName: NotRequired[str]
-    changeId: NotRequired[str]
+    updatedAt: NotRequired[str]
 
 
 session = boto3.session.Session()
@@ -170,23 +170,6 @@ def validate_agent_request(request: AgentRequest) -> AgentRequest:
     return request
 
 
-def create_change_hash(request: AgentRequest) -> str:
-    return str(
-        hash(
-            # nosemgrep - Pickle is used to generate a hash. This object will never be deserialized.
-            pickle.dumps(
-                (
-                    request.get("instruction", None),
-                    request.get("foundationModel", None),
-                    request.get("description", None),
-                    request.get("idleSessionTTLInSeconds", None),
-                    request.get("promptOverrideConfiguration", None),
-                )
-            )
-        )
-    )
-
-
 def on_event(event: CustomResourceRequest[Dict], context):
     logger.debug(f"Received event: {event}")
     request_type = event["RequestType"]
@@ -213,23 +196,31 @@ def on_create(
 ) -> CustomResourceResponse:
     bedrock_agent = session.client("bedrock-agent")
 
+    if "shouldPrepareAgent" in event["ResourceProperties"]:
+        should_prepare_agent = event["ResourceProperties"]["shouldPrepareAgent"].upper() == 'TRUE'
+        del event["ResourceProperties"]["shouldPrepareAgent"]
+    else:
+        should_prepare_agent = False
+
     request = AgentRequest(**event["ResourceProperties"])
     request = validate_agent_request(request)
 
     try:
         response = bedrock_agent.create_agent(clientToken=client_token, **request)
-        return CustomResourceResponse(
+
+    except botocore.exceptions.ClientError as e:
+        can_retry(e)
+    if should_prepare_agent:
+        prepare_agent(response["agent"]["agentId"])
+    return CustomResourceResponse(
             PhysicalResourceId=response["agent"]["agentId"],
             Data=AgentResponse(
                 agentArn=response["agent"]["agentArn"],
                 agentId=response["agent"]["agentId"],
                 agentName=response["agent"]["agentName"],
-                changeId=create_change_hash(request),
+                updatedAt=str(response["agent"]["updatedAt"]),
             ),
         )
-    except botocore.exceptions.ClientError as e:
-        can_retry(e)
-
 
 @retry(
     retry=retry_if_exception_type(AWSRetryableError),
@@ -238,6 +229,11 @@ def on_create(
 )
 def on_update(event: CustomResourceRequest[Dict]) -> CustomResourceResponse:
     bedrock_agent = session.client("bedrock-agent")
+    if "shouldPrepareAgent" in event["ResourceProperties"]:
+        should_prepare_agent = event["ResourceProperties"]["shouldPrepareAgent"].upper() == 'TRUE'
+        del event["ResourceProperties"]["shouldPrepareAgent"]
+    else:
+        should_prepare_agent = False
     request = AgentRequest(**event["ResourceProperties"])
     request = validate_agent_request(request)
     if "tags" in request:
@@ -248,18 +244,19 @@ def on_update(event: CustomResourceRequest[Dict]) -> CustomResourceResponse:
             **request,
             agentId=event["PhysicalResourceId"],
         )
-        return CustomResourceResponse(
-            PhysicalResourceId=response["agent"]["agentId"],
-            Data=AgentResponse(
-                agentArn=response["agent"]["agentArn"],
-                agentId=response["agent"]["agentId"],
-                agentName=response["agent"]["agentName"],
-                changeId=create_change_hash(request),
-            ),
-        )
     except botocore.exceptions.ClientError as e:
         can_retry(e)
-
+    if should_prepare_agent:
+        prepare_agent(response["agent"]["agentId"])
+    return CustomResourceResponse(
+        PhysicalResourceId=response["agent"]["agentId"],
+        Data=AgentResponse(
+            agentArn=response["agent"]["agentArn"],
+            agentId=response["agent"]["agentId"],
+            agentName=response["agent"]["agentName"],
+            updatedAt=str(response["agent"]["updatedAt"]),
+        ),
+    )
 
 @retry(
     retry=retry_if_exception_type(AWSRetryableError),
diff --git a/lambda/bedrock-custom-resources/custom_resources/bedrock_agent_action_group.py b/lambda/bedrock-custom-resources/custom_resources/bedrock_agent_action_group.py
index 453559c..cf99f56 100644
--- a/lambda/bedrock-custom-resources/custom_resources/bedrock_agent_action_group.py
+++ b/lambda/bedrock-custom-resources/custom_resources/bedrock_agent_action_group.py
@@ -24,6 +24,7 @@ from tenacity import (
     wait_exponential_jitter,
 )
 
+from .bedrock_prepare_agent import prepare_agent
 from .cr_types import CustomResourceRequest, CustomResourceResponse
 from .exceptions import AWSRetryableError, can_retry
 
@@ -54,6 +55,7 @@ class AgentActionGroupProps(TypedDict):
     apiSchema: NotRequired[APISchema]
     description: NotRequired[str]
     parentActionGroupSignature: NotRequired[str]
+    shouldPrepareAgent: NotRequired[str]
 
 
 class AgentActionGroupResponse(TypedDict):
@@ -81,17 +83,25 @@ def on_event(event: CustomResourceRequest[AgentActionGroupProps], context):
     retry=retry_if_exception_type(AWSRetryableError),
     stop=stop_after_attempt(3),
     wait=wait_exponential_jitter(1, 3),
+    reraise=True,
 )
 def on_create(
         event: CustomResourceRequest[AgentActionGroupProps]
 ) -> CustomResourceResponse:
     bedrock_agent = boto3.client("bedrock-agent")
+    if "shouldPrepareAgent" in event["ResourceProperties"]:
+        should_prepare_agent = event["ResourceProperties"]["shouldPrepareAgent"].upper() == 'TRUE'
+        del event["ResourceProperties"]["shouldPrepareAgent"]
+    else:
+        should_prepare_agent = False
     try:
         response = bedrock_agent.create_agent_action_group(
             **event["ResourceProperties"],
             agentVersion="DRAFT",
 
         )
+        if should_prepare_agent:
+            prepare_agent(event["ResourceProperties"]["agentId"])
         return CustomResourceResponse(
             PhysicalResourceId=response["agentActionGroup"]["actionGroupId"],
             Data=AgentActionGroupResponse(
@@ -107,18 +117,25 @@ def on_create(
     retry=retry_if_exception_type(AWSRetryableError),
     stop=stop_after_attempt(3),
     wait=wait_exponential_jitter(1, 3),
+    reraise=True,
 )
 def on_update(
         event: CustomResourceRequest[AgentActionGroupProps]
 ) -> CustomResourceResponse:
     bedrock_agent = boto3.client("bedrock-agent")
-
+    if "shouldPrepareAgent" in event["ResourceProperties"]:
+        should_prepare_agent = event["ResourceProperties"]["shouldPrepareAgent"].upper() == 'TRUE'
+        del event["ResourceProperties"]["shouldPrepareAgent"]
+    else:
+        should_prepare_agent = False
     try:
         response = bedrock_agent.update_agent_action_group(
             actionGroupId=event["PhysicalResourceId"],
             **event["ResourceProperties"],
             agentVersion="DRAFT",
         )
+        if should_prepare_agent:
+            prepare_agent(event["ResourceProperties"]["agentId"])
         return CustomResourceResponse(
             PhysicalResourceId=response["agentActionGroup"]["actionGroupId"],
             Data=AgentActionGroupResponse(
@@ -134,12 +151,17 @@ def on_update(
     retry=retry_if_exception_type(AWSRetryableError),
     stop=stop_after_attempt(3),
     wait=wait_exponential_jitter(1, 3),
+    reraise=True,
 )
 def on_delete(
         event: CustomResourceRequest[AgentActionGroupProps]
 ) -> CustomResourceResponse:
     bedrock_agent = boto3.client("bedrock-agent")
-
+    if "shouldPrepareAgent" in event["ResourceProperties"]:
+        should_prepare_agent = event["ResourceProperties"]["shouldPrepareAgent"].upper() == 'TRUE'
+        del event["ResourceProperties"]["shouldPrepareAgent"]
+    else:
+        should_prepare_agent = False
     try:
         bedrock_agent.delete_agent_action_group(
             agentId=event["ResourceProperties"]["agentId"],
@@ -147,6 +169,8 @@ def on_delete(
             actionGroupId=event["PhysicalResourceId"],
             skipResourceInUseCheck=True,
         )
+        if should_prepare_agent:
+            prepare_agent(event["ResourceProperties"]["agentId"])
         return CustomResourceResponse(
             PhysicalResourceId=event["PhysicalResourceId"],
         )
diff --git a/lambda/bedrock-custom-resources/custom_resources/bedrock_agent_alias.py b/lambda/bedrock-custom-resources/custom_resources/bedrock_agent_alias.py
index eb1efbd..7da4e76 100644
--- a/lambda/bedrock-custom-resources/custom_resources/bedrock_agent_alias.py
+++ b/lambda/bedrock-custom-resources/custom_resources/bedrock_agent_alias.py
@@ -46,6 +46,7 @@ from tenacity import (
     wait_exponential_jitter,
 )
 
+from .bedrock_prepare_agent import prepare_agent
 from .cr_types import CustomResourceRequest, CustomResourceResponse
 from .exceptions import AWSRetryableError, can_retry
 
@@ -85,27 +86,12 @@ def on_event(event: CustomResourceRequest[Dict], context):
     raise Exception(f"Invalid request type: {request_type}")
 
 
-@retry(
-    retry=retry_if_exception_type(AWSRetryableError),
-    stop=stop_after_attempt(3),
-    wait=wait_exponential_jitter(1, 3),
-)
-def prepare_agent(agent_id: str, session: boto3.Session) -> str:
-    client = session.client("bedrock-agent")
-
-    try:
-        response = client.prepare_agent(agentId=agent_id)
-        return response["agentStatus"]
-    except botocore.exceptions.ClientError as e:
-        if e.response["Error"]["Code"] == "ResourceNotFoundException":
-            raise Exception(f"Agent {agent_id} not found")
-        can_retry(e)
-
 
 @retry(
     retry=retry_if_exception_type(AWSRetryableError),
     stop=stop_after_attempt(10),
     wait=wait_exponential_jitter(1, 15),
+    reraise=True,
 )
 def wait_for_agent_status(agent_id: str, status: str, session: boto3.Session) -> str:
     client = session.client("bedrock-agent")
@@ -129,6 +115,7 @@ def wait_for_agent_status(agent_id: str, status: str, session: boto3.Session) ->
     retry=retry_if_exception_type(AWSRetryableError),
     stop=stop_after_attempt(3),
     wait=wait_exponential_jitter(1, 3),
+    reraise=True,
 )
 def list_alias_versions(agent_id: str, session: boto3.Session) -> List[int]:
     client = session.client("bedrock-agent")
@@ -154,6 +141,7 @@ def list_alias_versions(agent_id: str, session: boto3.Session) -> List[int]:
     retry=retry_if_exception_type(AWSRetryableError),
     stop=stop_after_attempt(3),
     wait=wait_exponential_jitter(1, 3),
+    reraise=True,
 )
 def list_unused_agent_versions(agent_id: str, session: boto3.Session) -> List[int]:
     client = session.client("bedrock-agent")
@@ -183,11 +171,11 @@ def get_version(routing_configuration: List[Dict]) -> str:
         return None
 
 
-def get_routing_configuration(agent_version: str | None) -> List[Dict[str, str]] | None:
+def get_routing_configuration(agent_version: str | None) -> List[Dict[str, str]]:
     if agent_version:
         routing_configuration = [{"agentVersion": agent_version}]
     else:
-        routing_configuration = None
+        routing_configuration = []
     return routing_configuration
 
 
@@ -195,6 +183,7 @@ def get_routing_configuration(agent_version: str | None) -> List[Dict[str, str]]
     retry=retry_if_exception_type(AWSRetryableError),
     stop=stop_after_attempt(3),
     wait=wait_exponential_jitter(1, 3),
+    reraise=True,
 )
 def create_agent_alias(
         agent_id: str, alias_name: str, session: boto3.Session, agent_version: str = None, client_token: str = None
@@ -227,6 +216,7 @@ def create_agent_alias(
     retry=retry_if_exception_type(AWSRetryableError),
     stop=stop_after_attempt(3),
     wait=wait_exponential_jitter(1, 3),
+    reraise=True,
 )
 def update_agent_alias(
         agent_id: str, agent_alias_id: str, alias_name: str, session: boto3.Session, agent_version: str | None = None
@@ -259,6 +249,7 @@ def update_agent_alias(
     retry=retry_if_exception_type(AWSRetryableError),
     stop=stop_after_attempt(3),
     wait=wait_exponential_jitter(1, 3),
+    reraise=True,
 )
 def delete_agent_version(agent_id: str, version: int, session: boto3.Session) -> None:
     client = session.client("bedrock-agent")
@@ -280,6 +271,7 @@ def delete_agent_version(agent_id: str, version: int, session: boto3.Session) ->
     retry=retry_if_exception_type(AWSRetryableError),
     stop=stop_after_attempt(3),
     wait=wait_exponential_jitter(1, 3),
+    reraise=True,
 )
 def delete_agent_alias(
         agent_id: str, agent_alias_id: str, session: boto3.Session
@@ -314,7 +306,7 @@ def on_create(
     alias_name = event["ResourceProperties"]["aliasName"]
     agent_version = event["ResourceProperties"].get("agentVersion")
 
-    prepare_agent(agent_id, session)
+    prepare_agent(agent_id)
     wait_for_agent_status(agent_id, "PREPARED", session)
     response = create_agent_alias(agent_id, alias_name, session, agent_version, client_token)
     return CustomResourceResponse(
@@ -338,7 +330,7 @@ def on_update(event: CustomResourceRequest[AgentAliasRequest]) -> CustomResource
     alias_name = event["ResourceProperties"]["aliasName"]
     agent_version = event["ResourceProperties"].get("agentVersion")
 
-    prepare_agent(agent_id, session)
+    prepare_agent(agent_id)
     wait_for_agent_status(agent_id, "PREPARED", session)
     versions = list_unused_agent_versions(agent_id, session)
     response = update_agent_alias(agent_id, agent_alias_id, alias_name, session, agent_version)
diff --git a/lambda/bedrock-custom-resources/custom_resources/bedrock_agent_knowledgebase.py b/lambda/bedrock-custom-resources/custom_resources/bedrock_agent_knowledgebase.py
index eacd13d..d6ea7fe 100644
--- a/lambda/bedrock-custom-resources/custom_resources/bedrock_agent_knowledgebase.py
+++ b/lambda/bedrock-custom-resources/custom_resources/bedrock_agent_knowledgebase.py
@@ -25,6 +25,7 @@ from tenacity import (
 
 from typing import TypedDict, NotRequired, Dict
 
+from .bedrock_prepare_agent import prepare_agent
 from .cr_types import CustomResourceRequest, CustomResourceResponse
 from .exceptions import AWSRetryableError, can_retry
 
@@ -38,10 +39,11 @@ class AgentKBProps(TypedDict):
     agentId: str
     knowledgeBaseId: str
     description: NotRequired[str]
+    shouldPrepareAgent: NotRequired[str]
 
 
 class AgentKBResponse(TypedDict):
-    changeId: str
+    updatedAt: str
 
 
 def on_event(event: CustomResourceRequest[AgentKBProps], context):
@@ -65,22 +67,11 @@ def get_physical_id(resource_props: AgentKBProps) -> str:
     return f"{resource_props['agentId']}#{resource_props['knowledgeBaseId']}"
 
 
-def get_change_id(resource_props: AgentKBProps) -> str:
-    return str(
-        hash(
-            resource_props["agentId"]
-            + "#"
-            + resource_props["knowledgeBaseId"]
-            + "#"
-            + resource_props["description"]
-        )
-    )
-
-
 @retry(
     retry=retry_if_exception_type(AWSRetryableError),
     stop=stop_after_attempt(3),
     wait=wait_exponential_jitter(1, 3),
+    reraise=True,
 )
 def on_create(
     event: CustomResourceRequest[AgentKBProps], bedrock_agent
@@ -93,18 +84,22 @@ def on_create(
             description=event["ResourceProperties"]["description"],
             knowledgeBaseState="ENABLED",
         )
-        return CustomResourceResponse(
-            PhysicalResourceId=get_physical_id(event["ResourceProperties"]),
-            Data=AgentKBResponse(changeId=get_change_id(event["ResourceProperties"])),
-        )
     except botocore.exceptions.ClientError as e:
         can_retry(e)
+    if event["ResourceProperties"].get("shouldPrepareAgent", False):
+        prepare_agent(event["ResourceProperties"]["agentId"])
+    return CustomResourceResponse(
+        PhysicalResourceId=get_physical_id(event["ResourceProperties"]),
+        Data=AgentKBResponse(updatedAt=str(response["agentKnowledgeBase"]["updatedAt"])),
+    )
+
 
 
 @retry(
     retry=retry_if_exception_type(AWSRetryableError),
     stop=stop_after_attempt(3),
     wait=wait_exponential_jitter(1, 3),
+    reraise=True,
 )
 def on_update(
     event: CustomResourceRequest[AgentKBProps], bedrock_agent
@@ -117,18 +112,22 @@ def on_update(
             description=event["ResourceProperties"]["description"],
             knowledgeBaseState="ENABLED",
         )
-        return CustomResourceResponse(
-            PhysicalResourceId=get_physical_id(event["ResourceProperties"]),
-            Data=AgentKBResponse(changeId=get_change_id(event["ResourceProperties"])),
-        )
     except botocore.exceptions.ClientError as e:
         can_retry(e)
+    if event["ResourceProperties"].get("shouldPrepareAgent", False):
+        prepare_agent(event["ResourceProperties"]["agentId"])
+    return CustomResourceResponse(
+        PhysicalResourceId=get_physical_id(event["ResourceProperties"]),
+        Data=AgentKBResponse(updatedAt=str(response["agentKnowledgeBase"]["updatedAt"])),
+    )
+
 
 
 @retry(
     retry=retry_if_exception_type(AWSRetryableError),
     stop=stop_after_attempt(3),
     wait=wait_exponential_jitter(1, 3),
+    reraise=True,
 )
 def on_delete(
     event: CustomResourceRequest[AgentKBProps], bedrock_agent
@@ -139,9 +138,6 @@ def on_delete(
             knowledgeBaseId=event["ResourceProperties"]["knowledgeBaseId"],
             agentVersion="DRAFT",
         )
-        return CustomResourceResponse(
-            PhysicalResourceId=get_physical_id(event["ResourceProperties"]),
-        )
     except botocore.exceptions.ClientError as e:
         if e.response["Error"]["Code"] == "ResourceNotFoundException":
             logger.error(
@@ -151,3 +147,9 @@ def on_delete(
                 PhysicalResourceId=get_physical_id(event["ResourceProperties"]),
             )
         can_retry(e)
+    if event["ResourceProperties"].get("shouldPrepareAgent", False):
+        prepare_agent(event["ResourceProperties"]["agentId"])
+    return CustomResourceResponse(
+            PhysicalResourceId=get_physical_id(event["ResourceProperties"]),
+        )
+
diff --git a/lambda/bedrock-custom-resources/custom_resources/bedrock_datasource.py b/lambda/bedrock-custom-resources/custom_resources/bedrock_datasource.py
index 65674b1..7bb36a0 100644
--- a/lambda/bedrock-custom-resources/custom_resources/bedrock_datasource.py
+++ b/lambda/bedrock-custom-resources/custom_resources/bedrock_datasource.py
@@ -122,6 +122,7 @@ def on_event(event: CustomResourceRequest[Dict], context):
     retry=retry_if_exception_type(AWSRetryableError),
     stop=stop_after_attempt(10),
     wait=wait_exponential_jitter(1, 15),
+    reraise=True,
 )
 def on_create(
     event: CustomResourceRequest[DataSourceRequest], client_token=None
@@ -149,6 +150,7 @@ def on_create(
     retry=retry_if_exception_type(AWSRetryableError),
     stop=stop_after_attempt(3),
     wait=wait_exponential_jitter(1, 3),
+    reraise=True,
 )
 def on_update(
     event: CustomResourceRequest[DataSourceRequest],
@@ -175,6 +177,7 @@ def on_update(
     retry=retry_if_exception_type(AWSRetryableError),
     stop=stop_after_attempt(3),
     wait=wait_exponential_jitter(1, 3),
+    reraise=True,
 )
 def on_delete(event: CustomResourceRequest[Dict]) -> CustomResourceResponse:
     bedrock_agent = session.client("bedrock-agent")
diff --git a/lambda/bedrock-custom-resources/custom_resources/bedrock_knowledgebase.py b/lambda/bedrock-custom-resources/custom_resources/bedrock_knowledgebase.py
index 18ab1de..8c74f62 100644
--- a/lambda/bedrock-custom-resources/custom_resources/bedrock_knowledgebase.py
+++ b/lambda/bedrock-custom-resources/custom_resources/bedrock_knowledgebase.py
@@ -61,6 +61,7 @@ def on_event(event: CustomResourceRequest[Dict], context):
     retry=retry_if_exception_type(AWSRetryableError),
     stop=stop_after_attempt(7),
     wait=wait_exponential_jitter(1, 15),
+    reraise=True,
 )
 def on_create(
     event: CustomResourceRequest[Dict], bedrock_agent, client_token=None
@@ -86,6 +87,7 @@ def on_create(
     retry=retry_if_exception_type(AWSRetryableError),
     stop=stop_after_attempt(7),
     wait=wait_exponential_jitter(1, 15),
+    reraise=True,
 )
 def on_update(
     event: CustomResourceRequest[Dict], bedrock_agent
@@ -112,6 +114,7 @@ def on_update(
     retry=retry_if_exception_type(AWSRetryableError),
     stop=stop_after_attempt(3),
     wait=wait_exponential_jitter(1, 3),
+    reraise=True,
 )
 def on_delete(
     event: CustomResourceRequest[Dict], bedrock_agent
diff --git a/lambda/bedrock-custom-resources/custom_resources/bedrock_prepare_agent.py b/lambda/bedrock-custom-resources/custom_resources/bedrock_prepare_agent.py
index 929e8b2..8c7ac75 100644
--- a/lambda/bedrock-custom-resources/custom_resources/bedrock_prepare_agent.py
+++ b/lambda/bedrock-custom-resources/custom_resources/bedrock_prepare_agent.py
@@ -11,29 +11,9 @@
 # and limitations under the License.
 #
 
-"""
-Input should be agentId, alias name, and hashes of any knowledge bases and action groups attached to identify changes.
-
-Creation:
-1. PrepareAgent
-2. Call GetAgent until agentStatus is PREPARED
-3. CreateAgentAlias with version 1
-
-Updates:
-1. PrepareAgent
-2. Call GetAgent until agentStatus is PREPARED
-3. ListAgentVersions to get the latest and previous version numbers excluding DRAFT
-4. Determine the next version number
-5. UpdateAgentAlias with the next version number
-6. Delete all but two most recent versions excluding DRAFT
-
-Deletion:
-1. DeleteAgentAlias
-2. Delete all versions excluding DRAFT
-"""
-
 import logging
 import os
+import time
 from typing import TypedDict, Dict
 
 import boto3
@@ -54,64 +34,61 @@ logger = logging.getLogger(__name__)
 logger.setLevel(LOG_LEVEL)
 
 
-class PrepareAgentRequest(TypedDict):
-    agentId: str
-
+@retry(
+    retry=retry_if_exception_type(AWSRetryableError),
+    stop=stop_after_attempt(3),
+    wait=wait_exponential_jitter(1, 3),
+    reraise=True,
+)
+def get_agent_status(agent_id: str) -> str:
+    client = boto3.client("bedrock-agent")
 
-def on_event(event: CustomResourceRequest[Dict], context):
-    logger.debug(f"Received event: {event}")
-    request_type = event["RequestType"]
+    try:
+        response = client.get_agent(agentId=agent_id)
+        return response["agent"]["agentStatus"]
+    except botocore.exceptions.ClientError as e:
+        can_retry(e)
 
-    if "ServiceToken" in event["ResourceProperties"]:
-        del event["ResourceProperties"]["ServiceToken"]
+@retry(
+    retry=retry_if_exception_type(AWSRetryableError),
+    stop=stop_after_attempt(10),
+    wait=wait_exponential_jitter(1, 15),
+    reraise=True,
+)
+def wait_for_agent(agent_id: str) -> str:
+    terminal_states = {"PREPARED", "NOT_PREPARED", "FAILED"}
 
-    if request_type == "Create":
-        return on_create(event)
-    if request_type == "Update":
-        return on_update(event)
-    if request_type == "Delete":
-        return on_delete(event)
-    raise Exception(f"Invalid request type: {request_type}")
+    status = get_agent_status(agent_id)
+    if status in terminal_states:
+        if status == "FAILED":
+            raise Exception(f"Agent in failed state")
+        else:
+            return status
+    else:
+        logger.info(f"Agent status is {status}, waiting...")
+        raise AWSRetryableError(f"Agent status is {status}")
 
 
 @retry(
     retry=retry_if_exception_type(AWSRetryableError),
     stop=stop_after_attempt(3),
     wait=wait_exponential_jitter(1, 3),
+    reraise=True,
 )
-def prepare_agent(agent_id: str, session: boto3.Session) -> str:
-    client = session.client("bedrock-agent")
+def prepare_agent(agent_id: str) -> str:
+    client = boto3.client("bedrock-agent")
+
+    wait_for_agent(agent_id)
 
     try:
-        response = client.prepare_agent(agentId=agent_id)
-        return response["agentStatus"]
+        client.prepare_agent(agentId=agent_id)
     except botocore.exceptions.ClientError as e:
         if e.response["Error"]["Code"] == "ResourceNotFoundException":
             raise Exception(f"Agent {agent_id} not found")
         can_retry(e)
-
-
-def on_create(event: CustomResourceRequest[PrepareAgentRequest]) -> CustomResourceResponse:
-    session = boto3.session.Session()
-    agent_id = event["ResourceProperties"]["agentId"]
-
-    prepare_agent(agent_id, session)
-    return CustomResourceResponse(
-        PhysicalResourceId=agent_id,
-    )
-
-
-def on_update(event: CustomResourceRequest[PrepareAgentRequest]) -> CustomResourceResponse:
-    session = boto3.session.Session()
-    agent_id = event["ResourceProperties"]["agentId"]
-
-    prepare_agent(agent_id, session)
-    return CustomResourceResponse(
-        PhysicalResourceId=agent_id,
-    )
-
-
-def on_delete(event: CustomResourceRequest[PrepareAgentRequest]) -> CustomResourceResponse:
-    agent_id = event["ResourceProperties"]["agentId"]
-
-    return CustomResourceResponse(PhysicalResourceId=agent_id)
+    status = wait_for_agent(agent_id)
+    if status == "PREPARED":
+        logger.info(f"Agent {agent_id} prepared")
+        return status
+    else:
+        raise Exception(f"Agent {agent_id} not prepared")
diff --git a/package.json b/package.json
index dec7ae7..370462c 100644
--- a/package.json
+++ b/package.json
@@ -41,7 +41,7 @@
   },
   "devDependencies": {
     "@commitlint/config-conventional": "^18.6.0",
-    "@mrgrain/jsii-struct-builder": "^0.7.12",
+    "@mrgrain/jsii-struct-builder": "^0.7.14",
     "@types/jest": "^29.5.12",
     "@types/node": "^18",
     "@typescript-eslint/eslint-plugin": "^6",
@@ -65,7 +65,7 @@
     "standard-version": "^9",
     "ts-jest": "^29.1.2",
     "ts-node": "^10.9.2",
-    "typedoc": "^0.25.7",
+    "typedoc": "^0.25.8",
     "typedoc-plugin-markdown": "^3.17.1",
     "typescript": "^5.3.3"
   },
@@ -74,7 +74,7 @@
     "constructs": "^10.3.0"
   },
   "dependencies": {
-    "cdk-nag": "^2.28.28"
+    "cdk-nag": "^2.28.30"
   },
   "keywords": [
     "aws-cdk",
diff --git a/src/cdk-lib/bedrock/README.md b/src/cdk-lib/bedrock/README.md
index 50f5241..dda0482 100644
--- a/src/cdk-lib/bedrock/README.md
+++ b/src/cdk-lib/bedrock/README.md
@@ -94,7 +94,9 @@ agent.addActionGroup({
 ```
 
 ### Prepare the Agent
-The custom resources return hashes of their properties in their `changeId` attribute. The Agent resource uses them to make sure CloudFormation will prepare the agent on any change.
+The `Agent` and `AgentActionGroup` constructs take an optional parameter `shouldPrepareAgent` to indicate that the Agent should be prepared after any updates to an agent, Knowledge Base association, or action group. This may increase the time to create and update those resources.
+
+Creating an agent alias will also prepare the agent, so if you create an alias with `addAlias` or by providing an `aliasName` when creating the agent then you should not set `shouldPrepareAgent` to ***true*** on other resources.
 
 #### Prompt Overrides
 Bedrock Agents allows you to customize the prompts and LLM configuration for its different steps. You can disable steps or create a new prompt template. Prompt templates can be inserted from plain text files.
diff --git a/src/cdk-lib/bedrock/agent-action-group.ts b/src/cdk-lib/bedrock/agent-action-group.ts
index 6dfbdda..f644b3d 100644
--- a/src/cdk-lib/bedrock/agent-action-group.ts
+++ b/src/cdk-lib/bedrock/agent-action-group.ts
@@ -58,7 +58,12 @@ export interface AgentActionGroupProps {
    * prompt additional information.
    */
   readonly parentActionGroupSignature?: 'AMAZON.UserInput';
-
+  /**
+   * Whether to prepare the agent for use.
+   *
+   * @default - false
+   */
+  readonly shouldPrepareAgent?: boolean;
 }
 
 interface ActionGroupExecutor {
@@ -106,6 +111,7 @@ export class AgentActionGroup extends Construct {
         apiSchema,
         description: props.description,
         parentActionGroupSignature: props.parentActionGroupSignature,
+        shouldPrepareAgent: props.shouldPrepareAgent,
       },
     });
     this.actionGroupId = agentActionGroup.getAttString('actionGroupId');
@@ -143,7 +149,7 @@ export class AgentActionGroup extends Construct {
     );
 
     agentActionGroup.node.addDependency(actionGroupCRPolicy);
-    props.agent._addPrepareAgentDependency(agentActionGroup, agentActionGroup.getAttString('updatedAt'));
+    props.agent._addAliasDependency(agentActionGroup.getAttString('updatedAt'));
   }
 }
 
diff --git a/src/cdk-lib/bedrock/agent-alias.ts b/src/cdk-lib/bedrock/agent-alias.ts
index fb7d21f..ad1eb61 100644
--- a/src/cdk-lib/bedrock/agent-alias.ts
+++ b/src/cdk-lib/bedrock/agent-alias.ts
@@ -30,10 +30,9 @@ export interface AgentAliasProps {
    */
   readonly aliasName?: string;
   /**
-   * The list of change ids to let CloudFormation determine when to update the alias.
-   * A changeId is a hash of the properties of an agent, an agent/knowledge base association, or an action group.
+   * The list of resource update timestamps to let CloudFormation determine when to update the alias.
    */
-  readonly changeIds?: string[];
+  readonly resourceUpdates?: string[];
   /**
    * The version of the agent to associate with the agent alias.
    *
@@ -74,7 +73,7 @@ export class AgentAlias extends Construct implements cdk.ITaggableV2 {
         properties: {
           agentId: props.agentId,
           aliasName: props.aliasName ?? 'latest',
-          changeIds: props.changeIds,
+          resourceUpdates: props.resourceUpdates,
           agentVersion: props.agentVersion,
           tags: this.cdkTagManager.renderedTags,
         },
diff --git a/src/cdk-lib/bedrock/agent.ts b/src/cdk-lib/bedrock/agent.ts
index dce4cbd..f1fb0c7 100644
--- a/src/cdk-lib/bedrock/agent.ts
+++ b/src/cdk-lib/bedrock/agent.ts
@@ -255,6 +255,12 @@ export interface AgentProps {
    * @default - No alias is created.
    */
   readonly aliasName?: string;
+  /**
+   * Whether to prepare the agent for use.
+   *
+   * @default - false
+   */
+  readonly shouldPrepareAgent?: boolean;
 }
 
 /**
@@ -351,19 +357,18 @@ export class Agent extends Construct implements cdk.ITaggableV2 {
    * A list of values to indicate if PrepareAgent or an Alias needs to be updated.
    * @private
    */
-  private changeIds: string[] = [];
+  private resourceUpdates: string[] = [];
   /**
-   * The prepareAgent custom resource.
-   *
-   * Add other resources as dependencies to ensure Prepare Agent is called after they are updated.
+   * If prepare agent should be called on resource updates.
    * @private
    */
-  private prepareAgent: cdk.CustomResource;
+  private readonly shouldPrepareAgent: boolean;
 
   constructor(scope: Construct, id: string, props: AgentProps) {
     super(scope, id);
     validatePromptOverrideConfiguration(props.promptOverrideConfiguration);
 
+    this.shouldPrepareAgent = props.shouldPrepareAgent ?? false;
     validateModel(props.foundationModel);
 
     this.name = props.name ?? generatePhysicalNameV2(
@@ -439,6 +444,7 @@ export class Agent extends Construct implements cdk.ITaggableV2 {
         customerEncryptionKeyArn: props.encryptionKey?.keyArn,
         tags: this.cdkTagManager.renderedTags,
         promptOverrideConfiguration: props.promptOverrideConfiguration,
+        shouldPrepareAgent: this.shouldPrepareAgent,
       },
     });
 
@@ -462,6 +468,7 @@ export class Agent extends Construct implements cdk.ITaggableV2 {
             'bedrock:DeleteAgent',
             'bedrock:UpdateAgent',
             'bedrock:TagResource',
+            'bedrock:GetAgent',
             'bedrock:PrepareAgent',
           ],
           resources: [
@@ -490,16 +497,7 @@ export class Agent extends Construct implements cdk.ITaggableV2 {
       true,
     );
 
-    this.prepareAgent = new cdk.CustomResource(this, 'PrepareAgent', {
-      serviceToken: crProvider.serviceToken,
-      resourceType: 'Custom::Bedrock-PrepareAgent',
-      properties: {
-        agentId: this.agentId,
-        changeIds: cdk.Lazy.list({ produce: () => this.changeIds }),
-      },
-    });
-
-    this._addPrepareAgentDependency(agent, agent.getAttString('changeId'));
+    this._addAliasDependency(agent.getAttString('updatedAt'));
 
     if (props.knowledgeBases && props.knowledgeBases.length > 0) {
       const kbAssocCRPolicy = new iam.Policy(this, 'KBAssocCRPolicy', {
@@ -554,12 +552,13 @@ export class Agent extends Construct implements cdk.ITaggableV2 {
               agentId: this.agentId,
               knowledgeBaseId: kb.knowledgeBaseId,
               description: kb.instruction,
+              shouldPrepareAgent: this.shouldPrepareAgent,
             },
           },
         );
         kbAssoc.node.addDependency(kbAssocCRPolicy);
         kbAssoc.node.addDependency(crProvider);
-        this._addPrepareAgentDependency(kbAssoc, kbAssoc.getAttString('changeId'));
+        this._addAliasDependency(kbAssoc.getAttString('updatedAt'));
       }
     }
 
@@ -581,10 +580,9 @@ export class Agent extends Construct implements cdk.ITaggableV2 {
     const alias = new AgentAlias(this, `AgentAlias-${props.aliasName}`, {
       agentId: this.agentId,
       agentVersion: props.agentVersion,
-      changeIds: cdk.Lazy.list({ produce: () => this.changeIds }),
+      resourceUpdates: cdk.Lazy.list({ produce: () => this.resourceUpdates }),
       aliasName: props.aliasName,
     });
-    alias.node.addDependency(this.prepareAgent);
     return alias;
   }
 
@@ -604,21 +602,20 @@ export class Agent extends Construct implements cdk.ITaggableV2 {
       actionGroupExecutor: props.actionGroupExecutor,
       actionGroupState: props.actionGroupState,
       apiSchema: props.apiSchema,
+      shouldPrepareAgent: this.shouldPrepareAgent,
     });
   }
 
   /**
-   * Register a dependency for prepareAgent.
+   * Register a dependency for aliases.
    *
-   * @param resource - The resource that will be registered as a dependency.
-   * @param changeId - The changeId of the resource that will be registered as a dependency.
+   * @param updatedAt - The updatedAt of the resource that will be registered as a dependency.
    *
    * @internal This is an internal core function and should not be called directly.
    */
-  public _addPrepareAgentDependency(resource: cdk.IResource, changeId?: string) {
-    this.prepareAgent.node.addDependency(resource);
-    if (changeId) {
-      this.changeIds.push(changeId);
+  public _addAliasDependency(updatedAt: string) {
+    if (updatedAt) {
+      this.resourceUpdates.push(updatedAt);
     }
   }
 }
diff --git a/yarn.lock b/yarn.lock
index fcad80d..c624625 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -810,10 +810,10 @@
   dependencies:
     ajv "^8.12.0"
 
-"@mrgrain/jsii-struct-builder@^0.7.12":
-  version "0.7.12"
-  resolved "https://registry.yarnpkg.com/@mrgrain/jsii-struct-builder/-/jsii-struct-builder-0.7.12.tgz#1b74fb0460d8f743c69a10e106d4db31bc739ff4"
-  integrity sha512-pi6KZZa1tcJNgKdoPNrvE69ewWq2zG80qap7P2mGWqPDgqv9p6lYVk2AFMD97fys/7W9sUEilh3yulsmBxoGhg==
+"@mrgrain/jsii-struct-builder@^0.7.14":
+  version "0.7.14"
+  resolved "https://registry.yarnpkg.com/@mrgrain/jsii-struct-builder/-/jsii-struct-builder-0.7.14.tgz#b4278e26c4e6af4f1b35df623a3f81e9ec144280"
+  integrity sha512-kWdYhFDogN832z9oD5TI5kuelfpb3gKV/6isdb6nb7KxnytNokUg2pwTSAmymBPEdUcWzeIF/g39yrRA3WBV+A==
   dependencies:
     "@jsii/spec" "^1.94.0"
 
@@ -1518,10 +1518,10 @@ case@1.6.3, case@^1.6.3:
   resolved "https://registry.yarnpkg.com/case/-/case-1.6.3.tgz#0a4386e3e9825351ca2e6216c60467ff5f1ea1c9"
   integrity sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ==
 
-cdk-nag@^2.28.28:
-  version "2.28.28"
-  resolved "https://registry.yarnpkg.com/cdk-nag/-/cdk-nag-2.28.28.tgz#3b9dc0d929cc25814eca2c3848fa0ca930b1040f"
-  integrity sha512-oX6lwwPhFF3xDokEaaJuTismwCXhVBr+WOYcw/mnWNlMGTeMZ5Te+UalLWsqS0hJCZrfk4Qnx+zBjUD3D7DsPg==
+cdk-nag@^2.28.30:
+  version "2.28.30"
+  resolved "https://registry.yarnpkg.com/cdk-nag/-/cdk-nag-2.28.30.tgz#ca06a82f2fd94737f2e8d13f027a9f01372ede13"
+  integrity sha512-aGwztQoMikzErJRUp/obeGDt3x+OAmJLEDGuHou/5CSsXTd45siZrrFEMeNL7VVoeLIN3TaQGjdNlcytPDwGqA==
 
 chalk@^2.4.2:
   version "2.4.2"
@@ -2086,9 +2086,9 @@ downlevel-dts@^0.11.0:
     typescript next
 
 electron-to-chromium@^1.4.648:
-  version "1.4.662"
-  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.662.tgz#0e014d31687210312c5f601bc9edcae2fa1663ef"
-  integrity sha512-gfl1XVWTQmPHhqEG0kN77SpUxaqPpMb9r83PT4gvKhg7P3irSxru3lW85RxvK1uI1j2CAcTWPjG/HbE0IP/Rtg==
+  version "1.4.665"
+  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.665.tgz#681700bd590b0e5a3be66e3e2874ce62abcf5da5"
+  integrity sha512-UpyCWObBoD+nSZgOC2ToaIdZB0r9GhqT2WahPKiSki6ckkSuKhQNso8V2PrFcHBMleI/eqbKgVQgVC4Wni4ilw==
 
 emittery@^0.13.1:
   version "0.13.1"
@@ -4810,9 +4810,9 @@ spdx-expression-parse@^3.0.0:
     spdx-license-ids "^3.0.0"
 
 spdx-license-ids@^3.0.0:
-  version "3.0.16"
-  resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz#a14f64e0954f6e25cc6587bd4f392522db0d998f"
-  integrity sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==
+  version "3.0.17"
+  resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz#887da8aa73218e51a1d917502d79863161a93f9c"
+  integrity sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==
 
 spdx-license-list@^6.8.0:
   version "6.8.0"
@@ -5245,10 +5245,10 @@ typedoc-plugin-markdown@^3.17.1:
   dependencies:
     handlebars "^4.7.7"
 
-typedoc@^0.25.7:
-  version "0.25.7"
-  resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.25.7.tgz#11e3f527ca80ca3c029cb8e15f362e6d9f715e25"
-  integrity sha512-m6A6JjQRg39p2ZVRIN3NKXgrN8vzlHhOS+r9ymUYtcUP/TIQPvWSq7YgE5ZjASfv5Vd5BW5xrir6Gm2XNNcOow==
+typedoc@^0.25.8:
+  version "0.25.8"
+  resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.25.8.tgz#7d0e1bf12d23bf1c459fd4893c82cb855911ff12"
+  integrity sha512-mh8oLW66nwmeB9uTa0Bdcjfis+48bAjSH3uqdzSuSawfduROQLlXw//WSNZLYDdhmMVB7YcYZicq6e8T0d271A==
   dependencies:
     lunr "^2.3.9"
     marked "^4.3.0"


Test patch:
diff --git a/test/cdk-lib/bedrock/__snapshots__/agent.test.ts.snap b/test/cdk-lib/bedrock/__snapshots__/agent.test.ts.snap
index a5522b3..33f0a4a 100644
--- a/test/cdk-lib/bedrock/__snapshots__/agent.test.ts.snap
+++ b/test/cdk-lib/bedrock/__snapshots__/agent.test.ts.snap
@@ -46,6 +46,7 @@ exports[`Bedrock Agents Agent matches snapshot 1`] = `
           "payload": "mock schema",
         },
         "description": "Use these functions to get information about the books in the Project Gutenburg library.",
+        "shouldPrepareAgent": false,
       },
       "Type": "Custom::Bedrock-AgentActionGroup",
       "UpdateReplacePolicy": "Delete",
@@ -100,7 +101,6 @@ exports[`Bedrock Agents Agent matches snapshot 1`] = `
       "DeletionPolicy": "Delete",
       "DependsOn": [
         "AgentAgentAliasprodAliasCRPolicyACECDEBD",
-        "AgentPrepareAgent9A21CF07",
         "BedrockCRProviderCRRole90406B17",
         "BedrockCRProviderCustomResourcesFunctionLogRetention32CED555",
         "BedrockCRProviderCustomResourcesFunction9A70E3A5",
@@ -123,17 +123,17 @@ exports[`Bedrock Agents Agent matches snapshot 1`] = `
           ],
         },
         "aliasName": "prod",
-        "changeIds": [
+        "resourceUpdates": [
           {
             "Fn::GetAtt": [
               "AgentD78B2CA1",
-              "changeId",
+              "updatedAt",
             ],
           },
           {
             "Fn::GetAtt": [
               "AgentKBAssocKBteststackKBF59CBB076A4D106D",
-              "changeId",
+              "updatedAt",
             ],
           },
           {
@@ -148,9 +148,6 @@ exports[`Bedrock Agents Agent matches snapshot 1`] = `
       "UpdateReplacePolicy": "Delete",
     },
     "AgentAgentAliasprodAliasCRPolicyACECDEBD": {
-      "DependsOn": [
-        "AgentPrepareAgent9A21CF07",
-      ],
       "Metadata": {
         "cdk_nag": {
           "rules_to_suppress": [
@@ -250,6 +247,7 @@ exports[`Bedrock Agents Agent matches snapshot 1`] = `
                 "bedrock:DeleteAgent",
                 "bedrock:UpdateAgent",
                 "bedrock:TagResource",
+                "bedrock:GetAgent",
                 "bedrock:PrepareAgent",
               ],
               "Effect": "Allow",
@@ -391,6 +389,7 @@ exports[`Bedrock Agents Agent matches snapshot 1`] = `
             },
           ],
         },
+        "shouldPrepareAgent": false,
       },
       "Type": "Custom::Bedrock-Agent",
       "UpdateReplacePolicy": "Delete",
@@ -487,54 +486,11 @@ exports[`Bedrock Agents Agent matches snapshot 1`] = `
             "knowledgeBaseId",
           ],
         },
+        "shouldPrepareAgent": false,
       },
       "Type": "Custom::Bedrock-AgentKnowledgeBase",
       "UpdateReplacePolicy": "Delete",
     },
-    "AgentPrepareAgent9A21CF07": {
-      "DeletionPolicy": "Delete",
-      "DependsOn": [
-        "AgentD78B2CA1",
-        "AgentAgentActionGrouptestactiongroupActionGroupD0F433CF",
-        "AgentKBAssocKBteststackKBF59CBB076A4D106D",
-      ],
-      "Properties": {
-        "ServiceToken": {
-          "Fn::GetAtt": [
-            "BedrockCRProviderframeworkonEvent412F4EF1",
-            "Arn",
-          ],
-        },
-        "agentId": {
-          "Fn::GetAtt": [
-            "AgentD78B2CA1",
-            "agentId",
-          ],
-        },
-        "changeIds": [
-          {
-            "Fn::GetAtt": [
-              "AgentD78B2CA1",
-              "changeId",
-            ],
-          },
-          {
-            "Fn::GetAtt": [
-              "AgentKBAssocKBteststackKBF59CBB076A4D106D",
-              "changeId",
-            ],
-          },
-          {
-            "Fn::GetAtt": [
-              "AgentAgentActionGrouptestactiongroupActionGroupD0F433CF",
-              "updatedAt",
-            ],
-          },
-        ],
-      },
-      "Type": "Custom::Bedrock-PrepareAgent",
-      "UpdateReplacePolicy": "Delete",
-    },
     "AgentRole9D587F56": {
       "Properties": {
         "AssumeRolePolicyDocument": {