Skip to content

Commit 4a402db

Browse files
committed
Create S3 bucket for file uploads
1 parent b3c492a commit 4a402db

File tree

1 file changed

+84
-0
lines changed

1 file changed

+84
-0
lines changed

backend/src/iac/backend-stack.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import * as apigateway from 'aws-cdk-lib/aws-apigateway';
77
import * as servicediscovery from 'aws-cdk-lib/aws-servicediscovery';
88
import * as iam from 'aws-cdk-lib/aws-iam';
99
import * as elbv2 from 'aws-cdk-lib/aws-elasticloadbalancingv2';
10+
import * as s3 from 'aws-cdk-lib/aws-s3';
1011

1112
import { Construct } from 'constructs';
1213
import { AttributeType, BillingMode, Table } from 'aws-cdk-lib/aws-dynamodb';
@@ -515,6 +516,83 @@ export class BackendStack extends cdk.Stack {
515516
],
516517
});
517518

519+
// Create S3 bucket for file uploads
520+
const uploadBucket = new s3.Bucket(this, `${appName}UploadBucket-${props.environment}`, {
521+
bucketName: `${appName.toLowerCase()}-uploads-${props.environment}-${this.account}`,
522+
removalPolicy: RemovalPolicy.RETAIN,
523+
versioned: true, // Enable versioning in production
524+
lifecycleRules: [
525+
{
526+
noncurrentVersionExpiration: cdk.Duration.days(7),
527+
// Move objects to infrequent access after 30 days
528+
transitions: [
529+
{
530+
storageClass: s3.StorageClass.INFREQUENT_ACCESS,
531+
transitionAfter: cdk.Duration.days(30),
532+
},
533+
],
534+
},
535+
],
536+
cors: [
537+
{
538+
allowedMethods: [
539+
s3.HttpMethods.GET,
540+
s3.HttpMethods.POST,
541+
s3.HttpMethods.PUT,
542+
s3.HttpMethods.DELETE,
543+
],
544+
allowedOrigins: ['*'], // In production, you should restrict this to your domain
545+
allowedHeaders: ['*'],
546+
maxAge: 3000,
547+
},
548+
],
549+
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, // Block all public access for security
550+
});
551+
552+
// Create a policy for authenticated users to upload files
553+
const uploadPolicy = new iam.PolicyStatement({
554+
effect: iam.Effect.ALLOW,
555+
actions: ['s3:PutObject', 's3:GetObject', 's3:DeleteObject'],
556+
resources: [
557+
`${uploadBucket.bucketArn}/*`,
558+
],
559+
conditions: {
560+
// Restrict uploads to PDF and JPG files
561+
'StringLike': {
562+
's3:x-amz-content-type': [
563+
'application/pdf',
564+
'image/jpeg',
565+
'image/jpg'
566+
]
567+
}
568+
}
569+
});
570+
571+
// Create an IAM role for authenticated users
572+
const authenticatedRole = new iam.Role(this, `${appName}AuthenticatedRole-${props.environment}`, {
573+
assumedBy: new iam.FederatedPrincipal(
574+
'cognito-identity.amazonaws.com',
575+
{
576+
StringEquals: {
577+
'cognito-identity.amazonaws.com:aud': userPool.userPoolId,
578+
},
579+
'ForAnyValue:StringLike': {
580+
'cognito-identity.amazonaws.com:amr': 'authenticated',
581+
},
582+
},
583+
'sts:AssumeRoleWithWebIdentity'
584+
),
585+
});
586+
587+
// Attach the upload policy to the authenticated role
588+
authenticatedRole.addToPolicy(uploadPolicy);
589+
590+
// Add environment variable to the container for the S3 bucket name
591+
container.addEnvironment('S3_UPLOAD_BUCKET', uploadBucket.bucketName);
592+
593+
// Grant the task role access to the S3 bucket
594+
uploadBucket.grantReadWrite(taskRole);
595+
518596
// Outputs
519597
new cdk.CfnOutput(this, 'ReportsTableName', {
520598
value: reportsTable.tableName,
@@ -535,5 +613,11 @@ export class BackendStack extends cdk.Stack {
535613
value: nlb.loadBalancerDnsName,
536614
description: 'Network Load Balancer DNS Name',
537615
});
616+
617+
// Add S3 bucket name to outputs
618+
new cdk.CfnOutput(this, 'UploadBucketName', {
619+
value: uploadBucket.bucketName,
620+
description: 'S3 Bucket for file uploads',
621+
});
538622
}
539623
}

0 commit comments

Comments
 (0)