Skip to content

Commit 51778d6

Browse files
authored
Merge pull request #201 from Anusha-janardhan/topic-release
Added new Lambdafunction.yml to support Lambda function deployment for egress workflow
2 parents c2ad554 + 6255f81 commit 51778d6

4 files changed

Lines changed: 2344 additions & 51 deletions

File tree

SRE/Egress/Setup-templates/egressresources.yml

Lines changed: 149 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,28 @@
1-
# (c) 2022 Amazon Web Services, Inc. or its affiliates. All Rights Reserved.
2-
# This AWS Content is provided subject to the terms of the AWS Customer
3-
# Agreement available at https://aws.amazon.com/agreement or other written
4-
# agreement between Customer and Amazon Web Services, Inc
5-
6-
###############################################################################
7-
81
AWSTemplateFormatVersion: "2010-09-09"
9-
Description: Provision all the required resources for TRE Egress application setup in RG.
2+
Description: >
3+
Combined CloudFormation template for TRE Egress application setup in RG.
4+
Creates egressstore and egressnotification buckets, Lambda function, and all required resources.
5+
Lambda permissions are restricted to only the created buckets and KMS key.
6+
7+
Parameters:
8+
LambdaFunctionName:
9+
Type: String
10+
Description: Name of the Lambda function
11+
AllowedPattern: ^[a-zA-Z0-9\-_.]{3,63}
12+
Default: rg-EgressLambda
13+
S3BucketName:
14+
Type: String
15+
Description: Name of the S3 bucket containing the Lambda zip file
16+
S3ObjectKey:
17+
Type: String
18+
Description: Key of the Lambda zip file in the S3 bucket
19+
Default: egress-copy.zip
1020

1121
Resources:
1222
EgressStoreEncryptionKey:
1323
Type: AWS::KMS::Key
1424
Properties:
15-
Description: >-
16-
Master key used to encrypt objects stored in the egress-store bucket
25+
Description: Master key used to encrypt objects stored in the egress-store bucket
1726
EnableKeyRotation: true
1827
KeyPolicy:
1928
Version: "2012-10-17"
@@ -27,39 +36,13 @@ Resources:
2736
Action:
2837
- "kms:*"
2938
Resource: "*"
30-
# - Sid: Allow API access to create object and update policy for new workspaces
31-
# Effect: Allow
32-
# Principal:
33-
# AWS:
34-
# - !Sub "arn:aws:iam::${AWS::AccountId}:role/RGPortalUser"
35-
# Action:
36-
# - kms:GenerateDataKey
37-
# - kms:DescribeKey
38-
# - kms:GetKeyPolicy
39-
# - kms:PutKeyPolicy
40-
# Resource: "*"
41-
# - Sid: Allow workflows to update key policy for new workspaces
42-
# Effect: Allow
43-
# Principal:
44-
# AWS:
45-
# - !Sub "arn:aws:iam::${AWS::AccountId}:role/RGPortalUser"
46-
# Action:
47-
# - kms:GenerateDataKey
48-
# - kms:Decrypt
49-
# - kms:DescribeKey
50-
# - kms:Encrypt
51-
# - kms:ReEncrypt*
52-
# - kms:GetKeyPolicy
53-
# - kms:PutKeyPolicy
54-
# Resource: "*"
5539

5640
EgressStoreEncryptionKeyAlias:
5741
Type: AWS::KMS::Alias
5842
Properties:
5943
AliasName: !Sub "alias/${AWS::AccountId}-rg/s3/egressstorever"
6044
TargetKeyId: !Ref EgressStoreEncryptionKey
6145

62-
# S3 bucket used to store egress data from workspace
6346
EgressStoreBucket:
6447
Type: AWS::S3::Bucket
6548
Properties:
@@ -72,9 +55,6 @@ Resources:
7255
OwnershipControls:
7356
Rules:
7457
- ObjectOwnership: BucketOwnerPreferred
75-
# LoggingConfiguration:
76-
# DestinationBucketName: "915161961510-treprod-ldn-pj1-logging"
77-
# LogFilePrefix: egressStore/
7858
VersioningConfiguration:
7959
Status: Enabled
8060
CorsConfiguration:
@@ -85,12 +65,12 @@ Resources:
8565
- POST
8666
ExposedHeaders:
8767
- ETag
88-
PublicAccessBlockConfiguration: # Block all public access configuration for the S3 bucket
68+
PublicAccessBlockConfiguration:
8969
BlockPublicAcls: true
9070
BlockPublicPolicy: true
9171
IgnorePublicAcls: true
9272
RestrictPublicBuckets: true
93-
# S3 bucket policy used to store egress data from workspace
73+
9474
EgressStoreBucketPolicy:
9575
Type: AWS::S3::BucketPolicy
9676
Properties:
@@ -117,7 +97,7 @@ Resources:
11797
Condition:
11898
StringNotEquals:
11999
s3:signatureversion: "AWS4-HMAC-SHA256"
120-
# S3 snapshot(egress copy) bucket used to copy the egress data from workspace bucket
100+
121101
EgressNotificationBucket:
122102
Type: AWS::S3::Bucket
123103
Properties:
@@ -130,9 +110,8 @@ Resources:
130110
OwnershipControls:
131111
Rules:
132112
- ObjectOwnership: BucketOwnerPreferred
133-
# LoggingConfiguration:
134-
# DestinationBucketName: "915161961510-treprod-ldn-pj1-logging"
135-
# LogFilePrefix: egressNotificationBucket/
113+
VersioningConfiguration:
114+
Status: Enabled
136115
CorsConfiguration:
137116
CorsRules:
138117
- AllowedOrigins:
@@ -141,14 +120,12 @@ Resources:
141120
- POST
142121
ExposedHeaders:
143122
- ETag
144-
PublicAccessBlockConfiguration: # Block all public access configuration for the S3 bucket
123+
PublicAccessBlockConfiguration:
145124
BlockPublicAcls: true
146125
BlockPublicPolicy: true
147126
IgnorePublicAcls: true
148127
RestrictPublicBuckets: true
149-
VersioningConfiguration:
150-
Status: Enabled
151-
# S3 snapshot(copy egress) bucket policy used to copy the egress data from workspace bucket
128+
152129
EgressNotificationBucketPolicy:
153130
Type: AWS::S3::BucketPolicy
154131
Properties:
@@ -175,14 +152,14 @@ Resources:
175152
Condition:
176153
StringNotEquals:
177154
s3:signatureversion: "AWS4-HMAC-SHA256"
178-
#SNS topic is created as part of this stack but Subscriptions would happen from egress stack
155+
179156
EgressNotificationTopic:
180157
Type: AWS::SNS::Topic
181158
Properties:
182159
DisplayName: "rg-EgressTopicTest"
183160
KmsMasterKeyId: !Ref EgressStoreEncryptionKey
184161
TopicName: "EgressTopicTest"
185-
#Egress Store dynamoDB
162+
186163
EgressStoreDb:
187164
Type: AWS::DynamoDB::Table
188165
Properties:
@@ -203,3 +180,124 @@ Resources:
203180
KeyType: "HASH"
204181
Projection:
205182
ProjectionType: "ALL"
183+
184+
LambdaExecutionRole:
185+
Type: AWS::IAM::Role
186+
Properties:
187+
AssumeRolePolicyDocument:
188+
Version: "2012-10-17"
189+
Statement:
190+
- Effect: "Allow"
191+
Principal:
192+
Service:
193+
- "lambda.amazonaws.com"
194+
Action:
195+
- "sts:AssumeRole"
196+
Policies:
197+
- PolicyName: EgressLambdaBasicExecution
198+
PolicyDocument:
199+
Version: "2012-10-17"
200+
Statement:
201+
- Sid: RGLambdaBasics
202+
Effect: "Allow"
203+
Action:
204+
- "logs:CreateLogGroup"
205+
- "logs:CreateLogStream"
206+
- "logs:PutLogEvents"
207+
Resource: "*"
208+
- Sid: AllowEgressLambdaToCopyFromStoreToNotification
209+
Effect: "Allow"
210+
Action:
211+
- "s3:GetObject"
212+
- "s3:PutObject"
213+
- "s3:ListBucket"
214+
- "s3:DeleteObject"
215+
- "s3:GetBucketLocation"
216+
- "s3:ListBucketMultipartUploads"
217+
- "s3:AbortMultipartUpload"
218+
- "s3:ListMultipartUploadParts"
219+
- "s3:PutObjectAcl"
220+
- "s3:GetObjectAcl"
221+
- "s3:DeleteObjectVersion"
222+
- "s3:GetObjectVersion"
223+
- "s3:PutObjectVersionAcl"
224+
- "s3:GetObjectVersionAcl"
225+
Resource:
226+
- !GetAtt EgressStoreBucket.Arn
227+
- !Join ["/", [!GetAtt EgressStoreBucket.Arn, "*"]]
228+
- !GetAtt EgressNotificationBucket.Arn
229+
- !Join ["/", [!GetAtt EgressNotificationBucket.Arn, "*"]]
230+
- Sid: AllowKMSForEgressBuckets
231+
Effect: "Allow"
232+
Action:
233+
- "kms:Decrypt"
234+
- "kms:Encrypt"
235+
- "kms:GenerateDataKey"
236+
- "kms:DescribeKey"
237+
Resource: !GetAtt EgressStoreEncryptionKey.Arn
238+
239+
LambdaFunction:
240+
Type: AWS::Lambda::Function
241+
Properties:
242+
FunctionName: !Ref LambdaFunctionName
243+
Handler: index.handler
244+
Role: !GetAtt LambdaExecutionRole.Arn
245+
Runtime: nodejs18.x
246+
Code:
247+
S3Bucket: !Ref S3BucketName
248+
S3Key: !Ref S3ObjectKey
249+
Timeout: 900
250+
251+
LambdaFunctionUrl:
252+
Type: AWS::Lambda::Url
253+
Properties:
254+
TargetFunctionArn: !Ref LambdaFunction
255+
AuthType: AWS_IAM
256+
257+
InvokeLambdaPermissionPolicy:
258+
Type: AWS::IAM::ManagedPolicy
259+
Properties:
260+
ManagedPolicyName:
261+
Fn::Sub: "InvokeLambda-${LambdaFunctionName}"
262+
PolicyDocument:
263+
Version: "2012-10-17"
264+
Statement:
265+
- Effect: Allow
266+
Action: "lambda:InvokeFunctionUrl"
267+
Resource: !GetAtt LambdaFunction.Arn
268+
Condition:
269+
StringEquals:
270+
lambda:FunctionUrlAuthType: "AWS_IAM"
271+
272+
Outputs:
273+
EgressStoreBucketArn:
274+
Description: ARN of the Egress Store S3 bucket
275+
Value: !GetAtt EgressStoreBucket.Arn
276+
277+
EgressStoreKmsArn:
278+
Description: ARN of the Egress Store KMS Key
279+
Value: !GetAtt EgressStoreEncryptionKey.Arn
280+
281+
EgressStoreBucketName:
282+
Description: Name of the Egress Store S3 bucket
283+
Value: !Ref EgressStoreBucket
284+
285+
EgressNotificationBucketName:
286+
Description: Name of the Egress Notification S3 bucket
287+
Value: !Ref EgressNotificationBucket
288+
289+
EgressStoreLambdaUrl:
290+
Description: Lambda Function Url
291+
Value: !GetAtt LambdaFunctionUrl.FunctionUrl
292+
293+
EgressStoreLambdaArn:
294+
Description: ARN of the Lambda function
295+
Value: !GetAtt LambdaFunction.Arn
296+
297+
EgressStoreDbTableName:
298+
Description: Name of the Egress Store DynamoDB table
299+
Value: !Ref EgressStoreDb
300+
301+
EgressNotificationTopicArn:
302+
Description: ARN of the Egress Notification SNS Topic
303+
Value: !Ref EgressNotificationTopic

SRE/lambdaresources/index.mjs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import AWS from 'aws-sdk';
2+
import archiver from 'archiver';
3+
import stream from 'stream';
4+
5+
const s3 = new AWS.S3();
6+
7+
export const handler = async (event) => {
8+
const reqBody = JSON.parse(event.body);
9+
const sourceBucket = reqBody.sourceBucket;
10+
const sourcePrefix = reqBody.sourcePrefix;
11+
const destinationBucket = reqBody.destinationBucket;
12+
const destinationPrefix = reqBody.destinationPrefix;
13+
14+
try {
15+
console.log(`Zipping and uploading files from ${sourcePrefix} to ${destinationPrefix}`);
16+
17+
// List objects in the source S3 folder
18+
const listParams = {
19+
Bucket: sourceBucket,
20+
Prefix: sourcePrefix
21+
};
22+
const listedObjects = await s3.listObjectsV2(listParams).promise();
23+
24+
if (listedObjects.Contents.length === 0) {
25+
console.log('No files found in the source folder');
26+
return { statusCode: 404, body: 'No files found in the source folder' };
27+
}
28+
29+
// PassThrough stream and archive setup
30+
const passThroughStream = new stream.PassThrough();
31+
const archive = archiver('zip', { zlib: { level: 9 } });
32+
33+
archive.on('error', (error) => {
34+
throw new Error(`Archiving error: ${error.message}`);
35+
});
36+
37+
archive.pipe(passThroughStream);
38+
39+
// Start S3 upload process asynchronously
40+
const uploadParams = {
41+
Bucket: destinationBucket,
42+
Key: destinationPrefix,
43+
Body: passThroughStream,
44+
ContentType: 'application/zip'
45+
};
46+
const uploadPromise = s3.upload(uploadParams).promise();
47+
48+
// Add files to archive
49+
for (const obj of listedObjects.Contents) {
50+
const fileKey = obj.Key;
51+
const fileStream = s3.getObject({ Bucket: sourceBucket, Key: fileKey }).createReadStream();
52+
const filePath = fileKey.replace(sourcePrefix, '');
53+
54+
if (!filePath) continue; // Skip empty paths
55+
56+
archive.append(fileStream, { name: filePath });
57+
console.log(`Appended file to archive: ${filePath}`);
58+
}
59+
60+
// Finalize the archive to signal completion
61+
await archive.finalize();
62+
63+
// Wait for S3 upload to complete
64+
await uploadPromise;
65+
66+
console.log('Zip file created and uploaded successfully');
67+
68+
return {
69+
statusCode: 201,
70+
headers: { "Content-Type": "application/json" },
71+
body: 'Zip file created and uploaded successfully',
72+
isBase64Encoded: false
73+
};
74+
75+
} catch (error) {
76+
console.error('Error creating or uploading zip file:', error.message);
77+
return {
78+
statusCode: 500,
79+
headers: { "Content-Type": "application/json" },
80+
body: `Error creating or uploading zip file: ${error.message}`,
81+
isBase64Encoded: false
82+
};
83+
}
84+
};

0 commit comments

Comments
 (0)