@@ -7,6 +7,7 @@ import * as apigateway from 'aws-cdk-lib/aws-apigateway';
77import * as servicediscovery from 'aws-cdk-lib/aws-servicediscovery' ;
88import * as iam from 'aws-cdk-lib/aws-iam' ;
99import * as elbv2 from 'aws-cdk-lib/aws-elasticloadbalancingv2' ;
10+ import * as s3 from 'aws-cdk-lib/aws-s3' ;
1011import * as ssm from 'aws-cdk-lib/aws-ssm' ;
1112
1213import { Construct } from 'constructs' ;
@@ -528,6 +529,78 @@ export class BackendStack extends cdk.Stack {
528529 ] ,
529530 } ) ;
530531
532+ // Create S3 bucket for file uploads
533+ const uploadBucket = new s3 . Bucket ( this , `${ appName } UploadBucket-${ props . environment } ` , {
534+ bucketName : `${ appName . toLowerCase ( ) } -uploads-${ props . environment } -${ this . account } ` ,
535+ removalPolicy : RemovalPolicy . RETAIN ,
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 : [ `${ uploadBucket . bucketArn } /*` ] ,
557+ } ) ;
558+
559+ // Create an IAM role for authenticated users
560+ const authenticatedRole = new iam . Role (
561+ this ,
562+ `${ appName } AuthenticatedRole-${ props . environment } ` ,
563+ {
564+ assumedBy : new iam . FederatedPrincipal (
565+ 'cognito-identity.amazonaws.com' ,
566+ {
567+ StringEquals : {
568+ 'cognito-identity.amazonaws.com:aud' : userPool . userPoolId ,
569+ } ,
570+ 'ForAnyValue:StringLike' : {
571+ 'cognito-identity.amazonaws.com:amr' : 'authenticated' ,
572+ } ,
573+ } ,
574+ 'sts:AssumeRoleWithWebIdentity' ,
575+ ) ,
576+ } ,
577+ ) ;
578+
579+ // Attach the upload policy to the authenticated role
580+ authenticatedRole . addToPolicy ( uploadPolicy ) ;
581+
582+ // Add environment variable to the container for the S3 bucket name
583+ container . addEnvironment ( 'S3_UPLOAD_BUCKET' , uploadBucket . bucketName ) ;
584+
585+ // Grant the task role access to the S3 bucket
586+ uploadBucket . grantReadWrite ( taskRole ) ;
587+
588+ // Add more specific S3 permissions for file processing
589+ taskRole . addToPolicy (
590+ new iam . PolicyStatement ( {
591+ effect : iam . Effect . ALLOW ,
592+ actions : [
593+ 's3:GetObject' ,
594+ 's3:PutObject' ,
595+ 's3:DeleteObject' ,
596+ 's3:ListBucket' ,
597+ 's3:GetObjectTagging' ,
598+ 's3:PutObjectTagging' ,
599+ ] ,
600+ resources : [ uploadBucket . bucketArn , `${ uploadBucket . bucketArn } /*` ] ,
601+ } ) ,
602+ ) ;
603+
531604 // Outputs
532605 new cdk . CfnOutput ( this , 'ReportsTableName' , {
533606 value : reportsTable . tableName ,
@@ -548,5 +621,11 @@ export class BackendStack extends cdk.Stack {
548621 value : nlb . loadBalancerDnsName ,
549622 description : 'Network Load Balancer DNS Name' ,
550623 } ) ;
624+
625+ // Add S3 bucket name to outputs
626+ new cdk . CfnOutput ( this , 'UploadBucketName' , {
627+ value : uploadBucket . bucketName ,
628+ description : 'S3 Bucket for file uploads' ,
629+ } ) ;
551630 }
552631}
0 commit comments