diff options
| author | Ruben Beltran del Rio <ruben@unlimited.pizza> | 2023-11-02 22:28:41 +0100 |
|---|---|---|
| committer | Ruben Beltran del Rio <ruben@unlimited.pizza> | 2023-11-02 22:28:41 +0100 |
| commit | 08b55c6db901910dd0540a45257122f9450cbe0a (patch) | |
| tree | 155d28d3ff1b669add11879809d04f6b57071e92 /index.js | |
Add code
Diffstat (limited to 'index.js')
| -rw-r--r-- | index.js | 267 |
1 files changed, 267 insertions, 0 deletions
diff --git a/index.js b/index.js new file mode 100644 index 0000000..b64a1a8 --- /dev/null +++ b/index.js @@ -0,0 +1,267 @@ +import { Config, asset } from '@pulumi/pulumi'; +import { acm, apigateway, cloudfront, iam, lambda, route53, s3 } from '@pulumi/aws'; +import { RestAPI } from '@pulumi/aws-apigateway'; + +const config = new Config(); + +// Bucket Config + +const bucketName = config.require('bucketName'); +const bucket = new s3.BucketV2('captura-backend-bucket', { + name: bucketName, +}); + +const bucketOwnershipControls = new s3.BucketOwnershipControls('captura-backend-bucket-ownership-control', { + bucket: bucket.id, + rule: { + objectOwnership: "BucketOwnerPreferred", + }, +}); + +const bucketPublicAccessBlock = new s3.BucketPublicAccessBlock('captura-backend-bucket-access-block', { + bucket: bucket.id, + blockPublicAcls: false, + blockPublicPolicy: false, + ignorePublicAcls: false, + restrictPublicBuckets: false, +}); + +new s3.BucketLifecycleConfigurationV2('captura-backend-bucket-lifecycle', { + bucket: bucket.id, + rules: [{ + id: 'expireAfter30Days', + status: 'Enabled', + expiration: { + days: 30, + }, + }], +}); +new s3.BucketAclV2('captura-backend-bucket-acl', { + bucket: bucket.id, + acl: 'public-read', +}, { + dependsOn: [ + bucketOwnershipControls, + bucketPublicAccessBlock, + ], +}); + +const bucketCertificate = new acm.Certificate(`captura-backend-bucket-certificate`, { + domainName: config.require('bucketDomain'), + validationMethod: 'DNS' +}); + +const bucketValidationRecord = new route53.Record(`captura-backend-bucket-certificate-validation-record`, { + allowOverwrite: true, + name: bucketCertificate.domainValidationOptions[0].resourceRecordName, + records: [bucketCertificate.domainValidationOptions[0].resourceRecordValue], + type: bucketCertificate.domainValidationOptions[0].resourceRecordType, + ttl: 60, + zoneId: config.require('zoneId') +}); + +const bucketCertificateValidation = new acm.CertificateValidation(`captura-backend-bucket-certificate-validation`, { + certificateArn: bucketCertificate.arn, + validationRecordFqdns: [bucketValidationRecord.fqdn] +}); + +const bucketCdn = new cloudfront.Distribution('captura-backend-bucket-cdn', { + comment: 'captura-backend', + enabled: true, + isIpv6Enabled: true, + defaultRootObject: 'index.html', + aliases: [ + config.require('bucketDomain') + ], + defaultCacheBehavior: { + targetOriginId: bucket.arn, + viewerProtocolPolicy: 'redirect-to-https', + allowedMethods: [ + 'GET', + 'HEAD', + 'OPTIONS' + ], + cachedMethods: [ + 'GET', + 'HEAD', + 'OPTIONS' + ], + forwardedValues: { + queryString: false, + cookies: { + forward: 'none' + }, + headers: [ + 'Origin' + ] + }, + compress: true, + minTtl: 0, + defaultTtl: 3600, + maxTtl: 86400 + }, + customErrorResponses: [ + { + errorCode: 404, + errorCachingMinTtl: 300, + responseCode: 200, + responsePagePath: '/index.html' + } + + ], + origins: [{ + domainName: bucket.bucketRegionalDomainName, + originId: bucket.arn + }], + restrictions: { + geoRestriction: { + restrictionType: 'none' + } + }, + viewerCertificate: { + acmCertificateArn: bucketCertificateValidation.certificateArn, + sslSupportMethod: 'sni-only', + minimumProtocolVersion: 'TLSv1.2_2021' + } +}); + +new route53.Record('captura-backend-bucket-domain', { + zoneId: config.require('zoneId'), + name: config.require('bucketDomain'), + type: 'A', + aliases: [{ + name: bucketCdn.domainName, + zoneId: bucketCdn.hostedZoneId, + evaluateTargetHealth: true + }] +}); + + +// API Config + +const lambdaRole = new iam.Role('captura-backend-lambda-role', { + assumeRolePolicy: JSON.stringify({ + Version: '2012-10-17', + Statement: [ + { + Action: 'sts:AssumeRole', + Principal: { + Service: 'lambda.amazonaws.com', + }, + Effect: 'Allow', + Sid: '', + }, + ], + }), +}); + +new iam.RolePolicy('captura-backend-lambda-s3-policy', { + role: lambdaRole.id, + policy: { + Version: '2012-10-17', + Statement: [{ + Effect: 'Allow', + Action: 's3:PutObject', + Resource: bucket.id.apply((id) => `arn:aws:s3:::${id}/*`), + }, { + Effect: 'Allow', + Action: 's3:PutObjectAcl', + Resource: bucket.id.apply((id) => `arn:aws:s3:::${id}/*`), + }], + }, +}); + +// Allow the API Gateway service to invoke the Lambda function +new iam.RolePolicy('captura-backend-lambda-invoke-policy', { + role: lambdaRole.id, + policy: { + Version: '2012-10-17', + Statement: [{ + Effect: 'Allow', + Action: 'lambda:InvokeFunction', + Resource: '*', + }], + }, +}); + +new iam.RolePolicy('captura-backend-lambda-logs-policy', { + role: lambdaRole.id, + policy: { + Version: '2012-10-17', + Statement: [{ + Effect: 'Allow', + Action: [ + 'logs:CreateLogGroup', + 'logs:CreateLogStream', + 'logs:PutLogEvents' + ], + Resource: 'arn:aws:logs:*:*:*' + }], + }, +}); + +const integrationLambda = new lambda.Function('captura-backend-lambda', { + code: new asset.AssetArchive({ + '.': new asset.FileArchive('./src'), + }), + role: lambdaRole.arn, + handler: 'index.handler', + runtime: 'nodejs18.x', + environment: { + variables: { + S3_BUCKET: bucket.id, + DOMAIN_NAME: config.require('bucketDomain') + } + }, + role: lambdaRole.arn +}); + + +const api = new RestAPI('captura-backend-api', { + routes: [{ + path: '/', + method: 'POST', + eventHandler: integrationLambda, + }], +}); + +const apiCertificate = new acm.Certificate(`captura-backend-api-certificate`, { + domainName: config.require('apiDomain'), + validationMethod: 'DNS' +}); + +const apiValidationRecord = new route53.Record(`captura-backend-api-certificate-validation-record`, { + allowOverwrite: true, + name: apiCertificate.domainValidationOptions[0].resourceRecordName, + records: [apiCertificate.domainValidationOptions[0].resourceRecordValue], + type: apiCertificate.domainValidationOptions[0].resourceRecordType, + ttl: 60, + zoneId: config.require('zoneId') +}); + +new acm.CertificateValidation(`captura-backend-api-certificate-validation`, { + certificateArn: apiCertificate.arn, + validationRecordFqdns: [apiValidationRecord.fqdn] +}); + +const domain = new apigateway.DomainName('captura-backend-api-domain', { + certificateArn: apiCertificate.arn, + domainName: config.require('apiDomain') +}); + +new apigateway.BasePathMapping('captura-backend-base-path', { + restApi: api.api.id, + stageName: api.stage.stageName, + domainName: domain.domainName +}); + +new route53.Record('captura-backend-dns-record', { + zoneId: config.require('zoneId'), + name: config.require('apiDomain'), + type: 'A', + aliases: [{ + name: domain.cloudfrontDomainName, + zoneId: domain.cloudfrontZoneId, + evaluateTargetHealth: true + }], +}); |