@@ -49,12 +49,12 @@ def check_opensearch_serverless(collection_id: str, kb_name: str) -> str | None:
4949
5050 if not collection_response .get ("collectionDetails" ):
5151 LOGGER .error (f"No collection details found for ID { collection_id } " )
52- return f"{ kb_name } (OpenSearch Serverless collection not found )"
52+ return f"{ kb_name } (no collection)"
5353
5454 collection_name = collection_response ["collectionDetails" ][0 ].get ("name" )
5555 if not collection_name :
5656 LOGGER .error (f"No collection name found for ID { collection_id } " )
57- return f"{ kb_name } (OpenSearch Serverless collection name not found )"
57+ return f"{ kb_name } (no collection name)"
5858
5959 # Get the specific policy details using the collection name
6060 policy_details = opensearch_serverless_client .get_security_policy (name = collection_name , type = "encryption" )
@@ -65,19 +65,19 @@ def check_opensearch_serverless(collection_id: str, kb_name: str) -> str | None:
6565 LOGGER .info (f"Policy details dict (after getting policy): { json .dumps (policy_details_dict , default = str )} " )
6666
6767 if policy_details_dict .get ("AWSOwnedKey" , False ):
68- LOGGER .info (f"{ kb_name } (OpenSearch Serverless using AWS-owned key instead of CMK)" )
69- return f"{ kb_name } (OpenSearch Serverless using AWS-owned key instead of CMK )"
68+ LOGGER .info (f"{ kb_name } (Using AWS-owned key, not CMK)" )
69+ return f"{ kb_name } (AWS-owned key)"
7070
7171 kms_key_arn = policy_details_dict .get ("KmsARN" , "" )
7272 if not kms_key_arn :
7373 LOGGER .info (f"{ kb_name } (OpenSearch Serverless not using CMK)" )
74- return f"{ kb_name } (OpenSearch Serverless not using CMK)"
74+ return f"{ kb_name } (no CMK)"
7575
7676 return None
7777
7878 except ClientError as e :
7979 LOGGER .error (f"Error checking OpenSearch Serverless collection: { str (e )} " )
80- return f"{ kb_name } (error checking OpenSearch Serverless )"
80+ return f"{ kb_name } (error)"
8181
8282
8383def check_opensearch_domain (domain_name : str , kb_name : str ) -> str | None : # type: ignore # noqa: CFQ004
@@ -94,13 +94,13 @@ def check_opensearch_domain(domain_name: str, kb_name: str) -> str | None: # ty
9494 domain = opensearch_client .describe_domain (DomainName = domain_name )
9595 encryption_config = domain .get ("DomainStatus" , {}).get ("EncryptionAtRestOptions" , {})
9696 if not encryption_config .get ("Enabled" , False ):
97- return f"{ kb_name } (OpenSearch domain encryption not enabled )"
97+ return f"{ kb_name } (encryption disabled )"
9898 kms_key_id = encryption_config .get ("KmsKeyId" , "" )
9999 if not kms_key_id or "aws/opensearch" in kms_key_id :
100- return f"{ kb_name } (OpenSearch domain not using CMK)"
100+ return f"{ kb_name } (no CMK)"
101101 except ClientError as e :
102102 LOGGER .error (f"Error checking OpenSearch domain: { str (e )} " )
103- return f"{ kb_name } (error checking OpenSearch domain )"
103+ return f"{ kb_name } (error)"
104104 return None
105105
106106
@@ -143,7 +143,7 @@ def check_knowledge_base(kb_id: str, kb_name: str) -> tuple[bool, str | None]:
143143 opensearch_config = vector_store .get ("opensearchServerlessConfiguration" ) or vector_store .get ("opensearchConfiguration" )
144144 LOGGER .info (f"OpenSearch config for { kb_name } : { json .dumps (opensearch_config )} " )
145145 if not opensearch_config :
146- return True , f"{ kb_name } (missing OpenSearch configuration )"
146+ return True , f"{ kb_name } (missing config )"
147147
148148 if "collectionArn" in opensearch_config :
149149 collection_id = opensearch_config ["collectionArn" ].split ("/" )[- 1 ]
@@ -152,7 +152,7 @@ def check_knowledge_base(kb_id: str, kb_name: str) -> tuple[bool, str | None]:
152152
153153 domain_endpoint = opensearch_config .get ("endpoint" , "" )
154154 if not domain_endpoint :
155- return True , f"{ kb_name } (missing OpenSearch domain endpoint)"
155+ return True , f"{ kb_name } (no endpoint)"
156156 domain_name = domain_endpoint .split ("." )[0 ]
157157 LOGGER .info (f"Found OpenSearch domain { domain_name } for { kb_name } " )
158158 return True , check_opensearch_domain (domain_name , kb_name )
@@ -164,11 +164,12 @@ def check_knowledge_base(kb_id: str, kb_name: str) -> tuple[bool, str | None]:
164164 raise
165165
166166
167- def evaluate_compliance (rule_parameters : dict ) -> tuple [str , str ]: # noqa: U100, CFQ004
167+ def evaluate_compliance (rule_parameters : dict , request_id : str = "" ) -> tuple [str , str ]: # noqa: U100, CFQ004
168168 """Evaluate if Bedrock Knowledge Base OpenSearch vector stores are encrypted with KMS CMK.
169169
170170 Args:
171171 rule_parameters (dict): Rule parameters from AWS Config rule.
172+ request_id (str): Lambda request ID for CloudWatch log reference.
172173
173174 Returns:
174175 tuple[str, str]: Compliance type and annotation message.
@@ -188,16 +189,21 @@ def evaluate_compliance(rule_parameters: dict) -> tuple[str, str]: # noqa: U100
188189 non_compliant_kbs .append (error )
189190
190191 if not has_opensearch :
191- return "COMPLIANT" , "No OpenSearch vector stores found in knowledge bases"
192+ return "COMPLIANT" , "No OpenSearch vector stores found"
193+
192194 if non_compliant_kbs :
193- return "NON_COMPLIANT" , (
194- "The following knowledge bases have OpenSearch vector stores not encrypted with CMK: " + f"{ '; ' .join (non_compliant_kbs )} "
195- )
196- return "COMPLIANT" , "All knowledge base OpenSearch vector stores are encrypted with KMS CMK"
195+ message = "KBs without CMK encryption: " + ", " .join (non_compliant_kbs )
196+ # Check if message exceeds the 256-character limit
197+ if len (message ) > 256 :
198+ LOGGER .info (f"Full message (truncated in annotation): { message } " )
199+ return "NON_COMPLIANT" , f"Multiple KBs without CMK encryption. See CloudWatch logs ({ request_id } )"
200+ return "NON_COMPLIANT" , message
201+
202+ return "COMPLIANT" , "All KBs properly encrypted with CMK"
197203
198204 except Exception as e :
199205 LOGGER .error (f"Error evaluating Bedrock Knowledge Base OpenSearch encryption: { str (e )} " )
200- return "INSUFFICIENT_DATA" , f"Error evaluating compliance : { str (e )} "
206+ return "INSUFFICIENT_DATA" , f"Error: { str (e )[: 220 ] } "
201207
202208
203209def lambda_handler (event : dict , context : Any ) -> None : # noqa: U100
@@ -209,11 +215,17 @@ def lambda_handler(event: dict, context: Any) -> None: # noqa: U100
209215 """
210216 LOGGER .info ("Evaluating compliance for AWS Config rule" )
211217 LOGGER .info (f"Event: { json .dumps (event )} " )
218+ LOGGER .info (f"Lambda Request ID: { context .aws_request_id } " )
212219
213220 invoking_event = json .loads (event ["invokingEvent" ])
214221 rule_parameters = json .loads (event ["ruleParameters" ]) if "ruleParameters" in event else {}
215222
216- compliance_type , annotation = evaluate_compliance (rule_parameters )
223+ compliance_type , annotation = evaluate_compliance (rule_parameters , context .aws_request_id )
224+
225+ # Ensure annotation doesn't exceed 256 characters
226+ if len (annotation ) > 256 :
227+ LOGGER .info (f"Original annotation (truncated): { annotation } " )
228+ annotation = annotation [:252 ] + "..."
217229
218230 evaluation = {
219231 "ComplianceResourceType" : "AWS::::Account" ,
0 commit comments