Skip to content

Instantly share code, notes, and snippets.

@maxcan
Last active February 3, 2022 21:17
Show Gist options
  • Save maxcan/c10e48ebfe6d9b19e22dc67ab9a41860 to your computer and use it in GitHub Desktop.
Save maxcan/c10e48ebfe6d9b19e22dc67ab9a41860 to your computer and use it in GitHub Desktop.
pulumi - fargate / s3 / lambda
// https://github.com/pulumi/examples/blob/master/aws-ts-pulumi-miniflux/index.ts
import * as pulumi from "@pulumi/pulumi";
import * as mime from "mime";
import * as aws from "@pulumi/aws";
import * as awsx from "@pulumi/awsx";
import { PrivateVirtualInterface } from "@pulumi/aws/directconnect";
import { Output } from "@pulumi/pulumi";
import { AnyTxtRecord } from "dns";
import { GraphQLApi } from "@pulumi/aws/appsync";
import { input } from "@pulumi/aws/types";
import { lambda } from "@pulumi/aws/types/enums";
import {
getDomainAndSubdomain,
createWWWAliasRecord,
createAliasRecord,
tenMinutes,
} from "./util";
const config = new pulumi.Config();
const awsConfig = new pulumi.Config("aws");
const awsRegion = awsConfig.get("region");
const env = config.require("ENV");
const isProduction = env.trim().toLowerCase() === "production";
const rootDomain = config.require("ROOT_DOMAIN");
const subdomainAppAdmin = config.require("SUBDOMAIN_APP_ADMIN");
const mkFqdn = (s: string) => `${s}${envFqdnComp}.${rootDomain}`;
const fqdnAppClient = isProduction
? rootDomain
: mkFqdn(config.require("SUBDOMAIN_APP_CLIENT"));
const name = config.require("NAME");
const dbUser = config.require("HASURA_DB_USERNAME");
const dbPass = config.requireSecret("HASURA_DB_PASSWORD");
// const dbPass = config.require("HASURA_DB_PASSWORD");
const dbName = `hasura${env}`;
const mediaBucketName = config.require("AWS_MEDIA_BUCKET");
// Create an AWS resource (S3 Bucket)
const mediaBucket = new aws.s3.Bucket(mediaBucketName, {
bucket: mediaBucketName,
// website: {
// indexDocument: "index.html",
// },
});
const vpc = new awsx.ec2.Vpc(`hasura-${env}`, {});
const cluster = new awsx.ecs.Cluster(`hasura-${env}`, {
name: `hasura-${env}-${name}`,
vpc,
});
const awsEastProvider = new aws.Provider(`aws-east-provider`, {
profile: aws.config.profile,
region: "us-east-1", // Per AWS, ACM certificate must be in the us-east-1 region.
});
console.log(rootDomain);
const envFqdnComp = isProduction ? "" : pulumi.interpolate`-${env}`;
// if config.includeWWW include required subjectAlternativeNames to support the www subdomain
const certificateConfig: aws.acm.CertificateArgs = {
domainName: rootDomain,
validationMethod: "DNS",
subjectAlternativeNames: [pulumi.interpolate`*.${rootDomain}`],
};
const certificate = new aws.acm.Certificate(
`certificate-${env}`,
certificateConfig,
{
provider: awsEastProvider,
}
);
const domainParts = getDomainAndSubdomain(rootDomain);
console.log("🚀 ~ file: index.ts ~ line 73 ~ domainParts", domainParts);
const hostedZoneId = aws.route53
.getZone({ name: domainParts.parentDomain }, { async: true })
.then((zone) => zone.zoneId);
/**
* Create a DNS record to prove that we _own_ the domain we're requesting a certificate for.
* See https://docs.aws.amazon.com/acm/latest/userguide/gs-acm-validate-dns.html for more info.
*/
// const certificateValidationDomain = new aws.route53.Record(
// `${config.require("ROOT_DOMAIN")}-validation`,
// {
// name: certificate.domainValidationOptions[0].resourceRecordName,
// zoneId: hostedZoneId,
// type: certificate.domainValidationOptions[0].resourceRecordType,
// records: [certificate.domainValidationOptions[0].resourceRecordValue],
// ttl: 60 * 3,
// }
// );
// certificateArn = certificateValidation.certificateArn;
// https://www.pulumi.com/docs/guides/crosswalk/aws/api-gateway/
const certValidationRecords: Output<aws.route53.Record[]> =
certificate.domainValidationOptions.apply((domainValidationOptions) => {
const opts = domainValidationOptions
.filter(
(dvo, idx) =>
domainValidationOptions
.map((d) => d.resourceRecordName)
.indexOf(dvo.resourceRecordName) == idx
)
.map((dvo) => {
return new aws.route53.Record(
`validation-${env}-${dvo.resourceRecordValue}`,
{
allowOverwrite: true,
name: dvo.resourceRecordName,
records: [dvo.resourceRecordValue],
ttl: 60,
type: dvo.resourceRecordType,
zoneId: hostedZoneId,
}
);
});
return opts;
});
const sslCertificateValidation = certValidationRecords.apply(
(validations) =>
new aws.acm.CertificateValidation(
`sslCertificateValidation-${env}`,
{
certificateArn: certificate.arn,
validationRecordFqdns: validations.map(
(exampleRecord) => exampleRecord.fqdn
),
},
{ provider: awsEastProvider }
)
);
// Configure an edge-optimized domain for our API Gateway. This will configure a Cloudfront CDN
// distribution behind the scenes and serve our API Gateway at a custom domain name over SSL.
// const webDomain = new aws.apigateway.DomainName("webCdn", {
// certificateArn: sslCertificateValidation.certificateArn,
// domainName: domain,
// });
// const webDomainMapping = new aws.apigateway.BasePathMapping(
// "webDomainMapping",
// {
// restApi: web.restAPI,
// stageName: web.stage.stageName,
// domainName: webDomain.id,
// }
// );
// Create a new subnet group for the database.
const subnetGroup = new aws.rds.SubnetGroup(`dbsubnets-${env}`, {
subnetIds: vpc.publicSubnetIds,
});
// Create a new database, using the subnet and cluster groups.
const db = new aws.rds.Instance(`hasura-${env}`, {
engine: "postgres",
instanceClass: aws.rds.InstanceTypes.T3_Micro,
allocatedStorage: 5,
dbSubnetGroupName: subnetGroup.id,
vpcSecurityGroupIds: cluster.securityGroups.map((g) => g.id),
name: dbName,
username: dbUser,
password: pulumi.interpolate`${dbPass}`,
skipFinalSnapshot: true,
});
// Assemble a connection string for the Miniflux service.
const connectionString = pulumi.interpolate`postgres://${dbUser}:${dbPass}@${db.endpoint}/${dbName}?sslmode=disable`;
const allowHttpsAndEightyEighty = new aws.ec2.SecurityGroup(`lb-sg-01-${env}`, {
name: `lb-sg-${env}-${(Math.random() + "9").substring(4, 10)}`,
vpcId: vpc.id,
ingress: [
{
description: `https-${name}-${env}`,
fromPort: 443,
toPort: 443,
protocol: "tcp",
cidrBlocks: ["0.0.0.0/0"],
},
],
egress: [
{
description: `https-internal-${name}-${env}`,
fromPort: 8080,
toPort: 8080,
protocol: "tcp",
cidrBlocks: ["0.0.0.0/0"],
},
{
description: `https-${name}-${env}`,
fromPort: 443,
toPort: 443,
protocol: "tcp",
cidrBlocks: ["0.0.0.0/0"],
},
],
});
// Create an NetworkListener to forward HTTP traffic on port 8080.
const lb = new awsx.lb.ApplicationLoadBalancer(`hsra-2-${env}`, {
vpc,
securityGroups: [allowHttpsAndEightyEighty.id],
});
const atg = lb.createTargetGroup(`hsra-tg-${env}`, {
port: 8080,
protocol: "HTTP",
deregistrationDelay: 90,
healthCheck: {
unhealthyThreshold: 5,
timeout: 20,
path: "/healthz",
},
});
const listener = atg.createListener(`hsra-2-listener-${env}`, {
protocol: "HTTPS",
vpc,
certificateArn: sslCertificateValidation.certificateArn,
});
// const atg = lb.createTargetGroup(`hasura-${env}`, {
// port: 8000,
// deregistrationDelay: 0,
// });
const hasuraDnsRecord = new aws.route53.Record(
`hasura-${env}`,
{
name: pulumi.interpolate`${config.require(
"SUBDOMAIN_HASURA"
)}.${rootDomain}`,
type: "CNAME",
zoneId: hostedZoneId,
ttl: 60,
records: [listener.endpoint.hostname],
},
{ dependsOn: sslCertificateValidation }
);
// const hasuraImage = awsx.ecs.Image.fromDockerBuild(
// "hasura-migrations-img-x86",
// {
// context: "./hasura-docker",
// args: {
// "--platform": "linux/amd64,linux/arm64",
// },
// }
// );
const hasuraImage = awsx.ecs.Image.fromPath(
"hasura-migrations-img-x86-2",
"./hasura-docker"
);
const hasuraKeyType = config.require("HASURA_JWT_KEY_TYPE");
const hasuraJwtKey = config.requireSecret("HASURA_JWT_KEY");
const hasuraAdminSecret = config.requireSecret("HASURA_GRAPHQL_ADMIN_SECRET");
// Give our Lambda access to the Dynamo DB table, CloudWatch Logs and Metrics.
const role = new aws.iam.Role("mylambda-role", {
assumeRolePolicy: aws.iam.assumeRolePolicyForPrincipal({
Service: "lambda.amazonaws.com",
}),
});
const policy = new aws.iam.RolePolicy(`mylambda-policy-${env}`, {
role,
policy: pulumi.output({
Version: "2012-10-17",
Statement: [
{
Action: ["s3:*", "cloudwatch:*", "logs:*"],
Resource: "*",
Effect: "Allow",
},
],
}),
});
const lambdaEnv: pulumi.Input<input.lambda.FunctionEnvironment> = {
variables: {
APPLE_CONNECT_SHARED_SECRET: config.requireSecret(
"APPLE_CONNECT_SHARED_SECRET"
),
ADMIN_EMAIL_ALLOWED: config.require("ADMIN_EMAIL_ALLOWED"),
ADMIN_EMAIL_SOURCE: config.require("ADMIN_EMAIL_SOURCE"),
AWS_MEDIA_BUCKET: config.require("AWS_MEDIA_BUCKET"),
ENV: config.require("ENV"),
HASURA_GRAPHQL_ADMIN_SECRET: config.requireSecret(
"HASURA_GRAPHQL_ADMIN_SECRET"
),
HASURA_JWT_KEY: config.requireSecret("HASURA_JWT_KEY"),
HASURA_JWT_KEY_TYPE: config.require("HASURA_JWT_KEY_TYPE"),
LAMBDA_LOCAL_PORT: config.require("LAMBDA_LOCAL_PORT"),
NODE_ENV: config.require("ENV"),
REACT_APP_BASE_URI: config.require("REACT_APP_BASE_URI"),
REACT_APP_HASURA_ENDPOINT: config.require("REACT_APP_HASURA_ENDPOINT"),
TWILIO_AUTH_TOKEN: config.requireSecret("TWILIO_AUTH_TOKEN"),
TWILIO_FROM: config.require("TWILIO_FROM"),
TWILIO_SID: config.requireSecret("TWILIO_SID"),
},
};
const lambdaRoutes = {
events: "hasura_event_handler",
gql: "graphql",
ping: "ping",
};
const lambdaApi = new awsx.apigateway.API(`lambda-${env}-${name}`, {
stageName: env,
routes: [
{
path: `/${lambdaRoutes.events}`,
method: "POST",
eventHandler: new aws.lambda.Function(`hasura-events-${env}`, {
runtime: aws.lambda.Runtime.NodeJS14dX,
code: new pulumi.asset.FileArchive(
`../backend-lambdas/.webpack/${config.require(
"NAME"
)}-${env}-lambdas.zip`
),
handler: "src/functions/hasura_event_handler/handler.main",
role: role.arn,
environment: lambdaEnv,
}),
},
{
path: `/${lambdaRoutes.gql}`,
method: "POST",
eventHandler: new aws.lambda.Function(`graphql-${env}`, {
runtime: aws.lambda.Runtime.NodeJS14dX,
code: new pulumi.asset.FileArchive(
`../backend-lambdas/.webpack/${config.require(
"NAME"
)}-${env}-lambdas.zip`
),
handler: "src/functions/graphql/handler.main",
role: role.arn,
environment: lambdaEnv,
}),
},
{
path: `/${lambdaRoutes.ping}`,
method: "POST",
eventHandler: new aws.lambda.Function(`ping-${env}`, {
runtime: aws.lambda.Runtime.NodeJS14dX,
code: new pulumi.asset.FileArchive(
`../backend-lambdas/.webpack/${config.require(
"NAME"
)}-${env}-lambdas.zip`
),
handler: "src/functions/ping/handler.main",
role: role.arn,
environment: lambdaEnv,
}),
},
],
});
const logGroup = new aws.cloudwatch.LogGroup(`hasura-${env}`);
// Create a Fargate service consisting of just one container instance (since that's all we
// really need), passing it the cluster, DB connection and Pulumi config settings.
const hasuraService = new awsx.ecs.FargateService(
`hasura-${env}`,
{
cluster,
desiredCount: 1,
taskDefinitionArgs: {
logGroup: logGroup,
cpu: "1024",
memory: "2048",
containers: {
[`hasura-${env}`]: {
// logConfiguration: {
// logDriver: "awslogs",
// options: {
// "awslogs-group": logGroup.id,
// "awslogs-region": awsRegion!,
// "awslogs-stream-prefix": `hasura-${env}`,
// },
// },
image: hasuraImage,
portMappings: [listener],
environment: [
{
name: "LAMBDA_HASURA_EVENT_HANDLER",
value: pulumi.interpolate`${lambdaApi.url}${lambdaRoutes.events}`,
},
{
name: "LAMBDA_ENDPOINT",
value: pulumi.interpolate`${lambdaApi.url}${lambdaRoutes.gql}`,
},
// { name: "HASURA_GRAPHQL_LOG_LEVEL", value: "debug" },
{ name: "HASURA_GRAPHQL_DATABASE_URL", value: connectionString },
{
name: "HASURA_GRAPHQL_ENABLE_ALLOWLIST",
value: "true",
},
{
name: "HASURA_GRAPHQL_ENABLE_CONSOLE",
value: isProduction ? "false" : "true",
},
{
name: "HASURA_GRAPHQL_ENABLED_APIS",
value: "graphql,metadata",
},
{
name: "HASURA_GRAPHQL_ENABLE_DEV_MODE",
value: isProduction ? "false" : "true",
},
{
name: "HASURA_GRAPHQL_ENABLED_LOG_TYPES",
value: "startup, http-log, webhook-log, websocket-log, query-log",
},
{
name: "HASURA_GRAPHQL_ADMIN_SECRET",
value: hasuraAdminSecret,
},
{
name: "HASURA_GRAPHQL_JWT_SECRET",
value: pulumi.interpolate`{"type": "${hasuraKeyType}", "key": "${hasuraJwtKey}"}`,
},
{ name: "HASURA_GRAPHQL_UNAUTHORIZED_ROLE", value: "anonymous" },
{ name: "HASURA_GRAPHQL_MIGRATIONS_DIR", value: "/migrations" },
{ name: "HASURA_GRAPHQL_METADATA_DIR", value: "/metadata" },
],
},
},
},
},
{
customTimeouts: {
create: "20m",
update: "20m",
delete: "20m",
},
}
);
// const bucketObject = new aws.s3.BucketObject("index.html", {
// acl: "public-read",
// contentType: "text/html",
// bucket: mediaBucket,
// source: new pulumi.asset.FileAsset("index.html"),
// });
// const hasuraLoadBalancer = new awsx.elasticloadbalancingv2.NetworkListener(
// "hasura",
// { port: 80 }
// );
// Export the name of the bucket
export const bucketName = mediaBucket.id;
// export const bucketEndpoint = pulumi.interpolate`http://${mediaBucket.websiteEndpoint}`;
export const dbStr = connectionString;
export const lambdaGql = lambdaApi.url.apply(
(u) => pulumi.interpolate`${u}${lambdaRoutes.gql}`
);
export const hasura = hasuraDnsRecord.name;
// START REACT_ADMIN
// Generate Origin Access Identity to access the private s3 bucket.
const originAccessIdentityAdmin = new aws.cloudfront.OriginAccessIdentity(
"originAccessIdentityAdmin",
{
comment: "this is needed to setup s3 polices and make s3 not public.",
}
);
const fqdnAppAdmin = mkFqdn(subdomainAppAdmin);
// Need to fix the bucket policy so that the principle is "*"
const contentBucketAdmin = new aws.s3.Bucket(
`${env}-admin-${rootDomain}`,
// `${env}-admin-${rootDomain}-static-web`,
{
// bucket: `${env}-admin-${rootDomain}-static-web`, // fqdnAppAdmin,
// Configure S3 to serve bucket contents as a website. This way S3 will automatically convert
// requests for "foo/" to "foo/index.html".
website: {
indexDocument: "index.html",
errorDocument: "404.html",
},
acl: "public-read",
corsRules: [
{
allowedHeaders: ["*"],
allowedMethods: ["PUT", "POST"],
allowedOrigins: ["*"],
exposeHeaders: ["ETag"],
maxAgeSeconds: 3000,
},
],
}
);
// logsBucket is an S3 bucket that will contain the CDN's request logs.
const wwwAccessLogsAdmin = new aws.s3.Bucket(
`${env}-admin-${rootDomain}-logs`,
{
// bucket: pulumi.interpolate`${fqdnAppAdmin}-logs`,
acl: "private",
}
);
// distributionArgs configures the CloudFront distribution. Relevant documentation:
// https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/distribution-web-values-specify.html
// https://www.terraform.io/docs/providers/aws/r/cloudfront_distribution.html
const distributionArgsAdmin: aws.cloudfront.DistributionArgs = {
enabled: true,
// Alternate aliases the CloudFront distribution can be reached at, in addition to https://xxxx.cloudfront.net.
// Required if you want to access the distribution via config.targetDomain as well.
aliases: [fqdnAppAdmin],
// We only specify one origin for this distribution, the S3 content bucket.
origins: [
{
originId: contentBucketAdmin.arn,
domainName: contentBucketAdmin.websiteEndpoint,
customOriginConfig: {
httpPort: 80,
httpsPort: 443,
originProtocolPolicy: "http-only",
originSslProtocols: ["SSLv3", "TLSv1.2"],
},
// s3OriginConfig: {
// originAccessIdentity:
// originAccessIdentityAdmin.cloudfrontAccessIdentityPath,
// },
},
],
defaultRootObject: "index.html",
// A CloudFront distribution can configure different cache behaviors based on the request path.
// Here we just specify a single, default cache behavior which is just read-only requests to S3.
defaultCacheBehavior: {
targetOriginId: contentBucketAdmin.arn,
viewerProtocolPolicy: "redirect-to-https",
allowedMethods: ["GET", "HEAD", "OPTIONS"],
cachedMethods: ["GET", "HEAD", "OPTIONS"],
forwardedValues: {
cookies: { forward: "none" },
queryString: false,
},
minTtl: 0,
defaultTtl: tenMinutes,
maxTtl: tenMinutes,
},
// "All" is the most broad distribution, and also the most expensive.
// "100" is the least broad, and also the least expensive.
priceClass: "PriceClass_100",
// You can customize error responses. When CloudFront receives an error from the origin (e.g. S3 or some other
// web service) it can return a different error code, and return the response for a different resource.
customErrorResponses: [
{ errorCode: 404, responseCode: 404, responsePagePath: "/404.html" },
],
restrictions: {
geoRestriction: {
restrictionType: "none",
},
},
viewerCertificate: {
acmCertificateArn: certificate.arn, // Per AWS, ACM certificate must be in the us-east-1 region.
sslSupportMethod: "sni-only",
},
loggingConfig: {
bucket: wwwAccessLogsAdmin.bucketDomainName,
includeCookies: false,
prefix: fqdnAppAdmin,
},
};
const cdnAdmin = new aws.cloudfront.Distribution(
"cdn-admin",
distributionArgsAdmin
);
// Creates a new Route53 DNS record pointing the domain to the CloudFront distribution.
const bucketPolicyAdmin = new aws.s3.BucketPolicy(`www-access-admin-${env}`, {
bucket: contentBucketAdmin.id, // refer to the bucket created earlier
policy: pulumi
.all([originAccessIdentityAdmin.iamArn, contentBucketAdmin.arn])
.apply(([oaiArn, bucketArn]) =>
JSON.stringify({
Version: "2012-10-17",
Statement: [
{
Effect: "Allow",
Principal: {
AWS: oaiArn,
}, // Only allow Cloudfront read access.
Action: ["s3:GetObject"],
Resource: [`${bucketArn}/*`], // Give Cloudfront access to the entire bucket.
},
],
})
),
});
const aRecordAdmin = createAliasRecord(fqdnAppAdmin, cdnAdmin);
// if (config.includeWWW) {
// const cnameRecord = createWWWAliasRecord(config.targetDomain, cdn);
// }
// Export properties from this stack. This prints them at the end of `pulumi up` and
// makes them easier to access from the pulumi.com.
export const contentBucketAdminUri = pulumi.interpolate`s3://${contentBucketAdmin.bucket}`;
export const contentBucketAdminWebsiteEndpoint =
contentBucketAdmin.websiteEndpoint;
export const cloudFrontDomainAdmin = cdnAdmin.domainName;
export const targetDomainEndpointAdmin = `https://${fqdnAppAdmin}/`;
// END REACT_ADMIN STATIC SITE
// START REACT_CLIENT STATIC SITE
const contentBucketClient = new aws.s3.Bucket(
`${env}-client-${rootDomain}-static-web`,
{
// bucket: `${env}-client-${rootDomain}-static-web`,
// Configure S3 to serve bucket contents as a website. This way S3 will automatically convert
// requests for "foo/" to "foo/index.html".
website: {
indexDocument: "index.html",
errorDocument: "404.html",
},
acl: "public-read",
corsRules: [
{
allowedHeaders: ["*"],
allowedMethods: ["PUT", "POST"],
allowedOrigins: ["*"],
exposeHeaders: ["ETag"],
maxAgeSeconds: 3000,
},
],
}
);
// logsBucket is an S3 bucket that will contain the CDN's request logs.
const wwwAccessLogsClient = new aws.s3.Bucket(
`${env}-client-${rootDomain}-logs`,
{
// bucket: pulumi.interpolate`${fqdnAppClient}-logs`,
acl: "private",
}
);
// distributionArgs configures the CloudFront distribution. Relevant documentation:
// https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/distribution-web-values-specify.html
// https://www.terraform.io/docs/providers/aws/r/cloudfront_distribution.html
const distributionArgsClient: aws.cloudfront.DistributionArgs = {
enabled: true,
// Alternate aliases the CloudFront distribution can be reached at, in addition to https://xxxx.cloudfront.net.
// Required if you want to access the distribution via config.targetDomain as well.
aliases: [fqdnAppClient, `www.${fqdnAppClient}`],
// We only specify one origin for this distribution, the S3 content bucket.
origins: [
{
originId: contentBucketClient.arn,
domainName: contentBucketClient.websiteEndpoint,
customOriginConfig: {
httpPort: 80,
httpsPort: 443,
originProtocolPolicy: "http-only",
originSslProtocols: ["SSLv3", "TLSv1.2"],
},
// s3OriginConfig: {
// originAccessIdentity:
// originAccessIdentityAdmin.cloudfrontAccessIdentityPath,
// },
},
],
defaultRootObject: "index.html",
// A CloudFront distribution can configure different cache behaviors based on the request path.
// Here we just specify a single, default cache behavior which is just read-only requests to S3.
defaultCacheBehavior: {
targetOriginId: contentBucketClient.arn,
viewerProtocolPolicy: "redirect-to-https",
allowedMethods: ["GET", "HEAD", "OPTIONS"],
cachedMethods: ["GET", "HEAD", "OPTIONS"],
forwardedValues: {
cookies: { forward: "none" },
queryString: false,
},
minTtl: 0,
defaultTtl: tenMinutes,
maxTtl: tenMinutes,
},
// "All" is the most broad distribution, and also the most expensive.
// "100" is the least broad, and also the least expensive.
priceClass: "PriceClass_100",
// You can customize error responses. When CloudFront receives an error from the origin (e.g. S3 or some other
// web service) it can return a different error code, and return the response for a different resource.
customErrorResponses: [
{ errorCode: 404, responseCode: 404, responsePagePath: "/404.html" },
],
restrictions: {
geoRestriction: {
restrictionType: "none",
},
},
viewerCertificate: {
acmCertificateArn: certificate.arn, // Per AWS, ACM certificate must be in the us-east-1 region.
sslSupportMethod: "sni-only",
},
loggingConfig: {
bucket: wwwAccessLogsClient.bucketDomainName,
includeCookies: false,
prefix: fqdnAppClient,
},
};
const cdnClient = new aws.cloudfront.Distribution(
"cdn-client",
distributionArgsClient
);
// Creates a new Route53 DNS record pointing the domain to the CloudFront distribution.
const bucketPolicyClient = new aws.s3.BucketPolicy(`www-access-client-${env}`, {
bucket: contentBucketClient.id, // refer to the bucket created earlier
policy: pulumi
.all([originAccessIdentityAdmin.iamArn, contentBucketClient.arn])
.apply(([oaiArn, bucketArn]) =>
JSON.stringify({
Version: "2012-10-17",
Statement: [
{
Effect: "Allow",
Principal: {
AWS: oaiArn,
}, // Only allow Cloudfront read access.
Action: ["s3:GetObject"],
Resource: [`${bucketArn}/*`], // Give Cloudfront access to the entire bucket.
},
],
})
),
});
const aRecordClient = createAliasRecord(fqdnAppClient, cdnClient);
const cnameRecord = createWWWAliasRecord(fqdnAppClient, cdnClient);
// Export properties from this stack. This prints them at the end of `pulumi up` and
// makes them easier to access from the pulumi.com.
export const contentBucketClientUri = pulumi.interpolate`s3://${contentBucketClient.bucket}`;
export const contentBucketClientWebsiteEndpoint =
contentBucketClient.websiteEndpoint;
export const cloudFrontDomainClient = cdnClient.domainName;
export const targetDomainEndpointClient = `https://${fqdnAppClient}/`;
// END REACT_CLIENT STATIC SITE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment