Skip to content

Commit cef2ecb

Browse files
committed
Create OpenSearchResources and OpenSearchCollection constructs
1 parent c6c54fd commit cef2ecb

File tree

4 files changed

+114
-41
lines changed

4 files changed

+114
-41
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import {Construct} from "constructs"
2+
import * as ops from "aws-cdk-lib/aws-opensearchserverless"
3+
4+
export interface OpenSearchCollectionProps {
5+
collectionName: string
6+
principals: Array<string>
7+
}
8+
9+
export class OpenSearchCollection extends Construct {
10+
public readonly collection: ops.CfnCollection
11+
public readonly endpoint: string
12+
13+
constructor(scope: Construct, id: string, props: OpenSearchCollectionProps) {
14+
super(scope, id)
15+
16+
// Encryption policy for collection (AWS-owned key)
17+
const encryptionPolicy = new ops.CfnSecurityPolicy(this, "EncryptionPolicy", {
18+
name: `${props.collectionName}-encryption-policy`,
19+
type: "encryption",
20+
policy: JSON.stringify({
21+
Rules: [{ResourceType: "collection", Resource: [`collection/${props.collectionName}`]}],
22+
AWSOwnedKey: true
23+
})
24+
})
25+
26+
// Network policy for public access (collection & dashboard)
27+
const networkPolicy = new ops.CfnSecurityPolicy(this, "NetworkPolicy", {
28+
name: `${props.collectionName}-network-policy`,
29+
type: "network",
30+
policy: JSON.stringify([{
31+
Rules: [
32+
{ResourceType: "collection", Resource: [`collection/${props.collectionName}`]},
33+
{ResourceType: "dashboard", Resource: [`collection/${props.collectionName}`]}
34+
],
35+
AllowFromPublic: true
36+
}])
37+
})
38+
39+
// OpenSearch collection (VECTORSEARCH type)
40+
this.collection = new ops.CfnCollection(this, "Collection", {
41+
name: props.collectionName,
42+
description: "EPS Assist Vector Store",
43+
type: "VECTORSEARCH"
44+
})
45+
46+
// Ensure collection is created after policies
47+
this.collection.addDependency(encryptionPolicy)
48+
this.collection.addDependency(networkPolicy)
49+
50+
// Access policy for principals (full access to collection & indexes)
51+
const accessPolicy = new ops.CfnAccessPolicy(this, "AccessPolicy", {
52+
name: `${props.collectionName}-access-policy`,
53+
type: "data",
54+
policy: JSON.stringify([{
55+
Rules: [
56+
{ResourceType: "collection", Resource: ["collection/*"], Permission: ["aoss:*"]},
57+
{ResourceType: "index", Resource: ["index/*/*"], Permission: ["aoss:*"]}
58+
],
59+
Principal: props.principals
60+
}])
61+
})
62+
63+
// Ensure access policy applies after collection creation
64+
this.collection.addDependency(accessPolicy)
65+
66+
// Collection endpoint
67+
this.endpoint = `${this.collection.attrId}.${this.collection.stack.region}.aoss.amazonaws.com`
68+
}
69+
}

packages/cdk/nagSuppressions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ export const nagSuppressions = (stack: Stack) => {
225225
reason: "Lambda needs to invoke itself for Slack Bolt lazy handlers.",
226226
appliesTo: [
227227
"Resource::arn:aws:lambda:eu-west-2:591291862413:function:*",
228+
"Resource::arn:aws:lambda:eu-west-2:123456789012:function:*",
228229
"Resource::arn:aws:lambda:eu-west-2:123456789012:function:AmazonBedrock*",
229230
"Resource::arn:aws:lambda:eu-west-2:591291862413:function:AmazonBedrock*"
230231
]
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import {Construct} from "constructs"
2+
import {OpenSearchCollection} from "../constructs/OpenSearchCollection"
3+
import * as iam from "aws-cdk-lib/aws-iam"
4+
5+
const COLLECTION_NAME = "eps-assist-vector-db"
6+
7+
export interface OpenSearchResourcesProps {
8+
bedrockExecutionRole: iam.Role
9+
createIndexFunctionRole: iam.Role
10+
account: string
11+
}
12+
13+
export class OpenSearchResources extends Construct {
14+
public readonly collection: OpenSearchCollection
15+
16+
constructor(scope: Construct, id: string, props: OpenSearchResourcesProps) {
17+
super(scope, id)
18+
19+
this.collection = new OpenSearchCollection(this, "OsCollection", {
20+
collectionName: COLLECTION_NAME,
21+
principals: [
22+
props.bedrockExecutionRole.roleArn,
23+
props.createIndexFunctionRole.roleArn,
24+
`arn:aws:iam::${props.account}:root`
25+
]
26+
})
27+
}
28+
}

packages/cdk/stacks/EpsAssistMeStack.ts

Lines changed: 16 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ import {Apis} from "../resources/Apis"
2020
import {Functions} from "../resources/Functions"
2121
import {Storage} from "../resources/Storage"
2222
import {Secrets} from "../resources/Secrets"
23+
import {OpenSearchResources} from "../resources/OpenSearchResources"
2324

2425
const EMBEDDING_MODEL = "amazon.titan-embed-text-v2:0"
25-
const COLLECTION_NAME = "eps-assist-vector-db"
2626
const VECTOR_INDEX_NAME = "eps-assist-os-index"
2727
const BEDROCK_KB_NAME = "eps-assist-kb"
2828

@@ -148,38 +148,6 @@ export class EpsAssistMeStack extends Stack {
148148
const GUARD_RAIL_ID = guardrail.attrGuardrailId
149149
const GUARD_RAIL_VERSION = guardrailVersion.attrVersion
150150

151-
//Define OpenSearchServerless Collection & depends on policies
152-
const osCollection = new ops.CfnCollection(this, "osCollection", {
153-
name: COLLECTION_NAME,
154-
description: "EPS Assist Vector Store",
155-
type: "VECTORSEARCH"
156-
})
157-
158-
// Define AOSS vector DB encryption policy with AWSOwned key true
159-
const aossEncryptionPolicy = new ops.CfnSecurityPolicy(this, "aossEncryptionPolicy", {
160-
name: "eps-assist-encryption-policy",
161-
type: "encryption",
162-
policy: JSON.stringify({
163-
Rules: [{ResourceType: "collection", Resource: ["collection/eps-assist-vector-db"]}],
164-
AWSOwnedKey: true
165-
})
166-
})
167-
osCollection.addDependency(aossEncryptionPolicy)
168-
169-
// Define Vector DB network policy with AllowFromPublic true. include collection & dashboard
170-
const aossNetworkPolicy = new ops.CfnSecurityPolicy(this, "aossNetworkPolicy", {
171-
name: "eps-assist-network-policy",
172-
type: "network",
173-
policy: JSON.stringify([{
174-
Rules: [
175-
{ResourceType: "collection", Resource: ["collection/eps-assist-vector-db"]},
176-
{ResourceType: "dashboard", Resource: ["collection/eps-assist-vector-db"]}
177-
],
178-
AllowFromPublic: true
179-
}])
180-
})
181-
osCollection.addDependency(aossNetworkPolicy)
182-
183151
// Define createIndexFunction execution role and policy. Managed role 'AWSLambdaBasicExecutionRole'
184152
const createIndexFunctionRole = new iam.Role(this, "CreateIndexFunctionRole", {
185153
assumedBy: new iam.ServicePrincipal("lambda.amazonaws.com"),
@@ -210,7 +178,14 @@ export class EpsAssistMeStack extends Stack {
210178
effect: iam.Effect.ALLOW
211179
}))
212180

213-
const endpoint = `${osCollection.attrId}.${region}.aoss.amazonaws.com`
181+
// Create OpenSearch Resources
182+
const openSearchResources = new OpenSearchResources(this, "OpenSearchResources", {
183+
bedrockExecutionRole,
184+
createIndexFunctionRole,
185+
account
186+
})
187+
188+
const endpoint = openSearchResources.collection.endpoint
214189

215190
// Define a Bedrock knowledge base with type opensearch serverless and titan for embedding model
216191
const bedrockkb = new CfnKnowledgeBase(this, "EpsKb", {
@@ -226,7 +201,7 @@ export class EpsAssistMeStack extends Stack {
226201
storageConfiguration: {
227202
type: "OPENSEARCH_SERVERLESS",
228203
opensearchServerlessConfiguration: {
229-
collectionArn: osCollection.attrArn,
204+
collectionArn: openSearchResources.collection.collection.attrArn,
230205
fieldMapping: {
231206
vectorField: "bedrock-knowledge-base-default-vector",
232207
textField: "AMAZON_BEDROCK_TEXT_CHUNK",
@@ -249,7 +224,7 @@ export class EpsAssistMeStack extends Stack {
249224
slackSigningSecretParameter: secrets.slackSigningSecretParameter,
250225
guardrailId: GUARD_RAIL_ID,
251226
guardrailVersion: GUARD_RAIL_VERSION,
252-
collectionId: osCollection.attrId,
227+
collectionId: openSearchResources.collection.collection.attrId,
253228
knowledgeBaseId: bedrockkb.attrKnowledgeBaseId,
254229
region,
255230
account,
@@ -275,7 +250,7 @@ export class EpsAssistMeStack extends Stack {
275250
]
276251
}])
277252
})
278-
osCollection.addDependency(aossAccessPolicy)
253+
openSearchResources.collection.collection.addDependency(aossAccessPolicy)
279254

280255
const vectorIndex = new cr.AwsCustomResource(this, "VectorIndex", {
281256
installLatestAwsSdk: true,
@@ -287,7 +262,7 @@ export class EpsAssistMeStack extends Stack {
287262
InvocationType: "RequestResponse",
288263
Payload: JSON.stringify({
289264
RequestType: "Create",
290-
CollectionName: osCollection.name,
265+
CollectionName: openSearchResources.collection.collection.name,
291266
IndexName: VECTOR_INDEX_NAME,
292267
Endpoint: endpoint
293268
})
@@ -302,7 +277,7 @@ export class EpsAssistMeStack extends Stack {
302277
InvocationType: "RequestResponse",
303278
Payload: JSON.stringify({
304279
RequestType: "Delete",
305-
CollectionName: osCollection.name,
280+
CollectionName: openSearchResources.collection.collection.name,
306281
IndexName: VECTOR_INDEX_NAME,
307282
Endpoint: endpoint
308283
})
@@ -318,12 +293,12 @@ export class EpsAssistMeStack extends Stack {
318293
})
319294

320295
// Ensure vectorIndex depends on collection
321-
vectorIndex.node.addDependency(osCollection)
296+
vectorIndex.node.addDependency(openSearchResources.collection.collection)
322297

323298
// add a dependency for bedrock kb on the custom resource. Enables vector index to be created before KB
324299
bedrockkb.node.addDependency(vectorIndex)
325300
bedrockkb.node.addDependency(functions.functions.createIndex)
326-
bedrockkb.node.addDependency(osCollection)
301+
bedrockkb.node.addDependency(openSearchResources.collection.collection)
327302
bedrockkb.node.addDependency(bedrockExecutionRole)
328303

329304
// Define a bedrock knowledge base data source with S3 bucket

0 commit comments

Comments
 (0)