@aws-cdk/core#Construct TypeScript Examples
The following examples show how to use
@aws-cdk/core#Construct.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: globalCodeCommit.ts From aws-boilerplate with MIT License | 6 votes |
export class GlobalCodeCommit extends Construct {
repository: Repository;
static getCodeRepositoryName(envSettings: EnvironmentSettings) {
return `${envSettings.projectName}-code`;
}
static getCodeRepoUserNameOutputExportName(envSettings: EnvironmentSettings) {
return `${envSettings.projectName}-codeRepoUserName`
}
static getCodeRepoCloneUrlHttpOutputExportName(envSettings: EnvironmentSettings) {
return `${envSettings.projectName}-codeRepoCloneUrlHttp`
}
constructor(scope: Construct, id: string, props: EnvConstructProps) {
super(scope, id);
this.repository = new Repository(this, 'CodeRepo', {
repositoryName: GlobalCodeCommit.getCodeRepositoryName(props.envSettings),
description: `${props.envSettings.projectName} code mirror repository used to source CodePipeline`
});
const user = new User(this, 'CodeRepoUser', {
userName: `${props.envSettings.projectName}-code`
});
this.repository.grantPullPush(user);
new CfnOutput(this, "CodeRepoUserName", {
exportName: GlobalCodeCommit.getCodeRepoUserNameOutputExportName(props.envSettings),
value: user.userName,
});
new CfnOutput(this, "CodeRepoCloneUrlHttp", {
exportName: GlobalCodeCommit.getCodeRepoCloneUrlHttpOutputExportName(props.envSettings),
value: this.repository.repositoryCloneUrlHttp,
});
}
}
Example #2
Source File: datadog-integration-aws.ts From cdk-datadog-resources with Apache License 2.0 | 6 votes |
constructor(scope: Construct, id: string, props: DatadogIntegrationAWSProps) {
const cfnProperties = camelcaseKeys(props, {
deep: true,
pascalCase: true,
});
// @ts-ignore
delete Object.assign(cfnProperties, { AccountID: cfnProperties.AccountId }).AccountId;
new CfnResource(scope, id, {
type: 'Datadog::Integrations::AWS',
properties: { ...cfnProperties },
});
}
Example #3
Source File: globalECR.ts From aws-boilerplate with MIT License | 6 votes |
export class GlobalECR extends Construct {
backendRepository: Repository;
static getBackendRepositoryName(envSettings: EnvironmentSettings) {
return `${envSettings.projectName}-backend`;
}
constructor(scope: Construct, id: string, props: EnvConstructProps) {
super(scope, id);
this.backendRepository = new Repository(this, "ECRBackendRepository", {
repositoryName: GlobalECR.getBackendRepositoryName(props.envSettings),
});
}
}
Example #4
Source File: certificates-stack.ts From hasura-cdk with MIT License | 6 votes |
constructor(scope: Construct, id: string, props: CertificatesStackProps) {
super(scope, id, props);
const hostedZone = PublicHostedZone.fromHostedZoneAttributes(this, 'HasuraHostedZone', {
hostedZoneId: props.hostedZoneId,
zoneName: props.hostedZoneName,
});
const hasura = new DnsValidatedCertificate(this, 'HasuraCertificate', {
hostedZone,
domainName: props.hasuraHostname,
});
const actions = new DnsValidatedCertificate(this, 'ActionsCertificate', {
hostedZone,
domainName: props.actionsHostname,
});
this.certificates = {
hasura,
actions,
};
}
Example #5
Source File: function.ts From aws-cdk-webpack-lambda-function with MIT License | 6 votes |
constructor(scope: Construct, id: string, props: WebpackFunctionProps) {
const { runtime, handlerDir, outputBasename, handler } = preProcess(props);
super(scope, id, {
...props,
runtime,
code: Code.fromAsset(handlerDir),
handler: `${outputBasename}.${handler}`,
});
}
Example #6
Source File: auth-stack.ts From MDDL with MIT License | 6 votes |
constructor(scope: Construct, id: string, props: Props) {
super(scope, id, props)
const { userPoolName, emailSender, customDomain } = props
// create the user pool
const { userPool, uiCustomization } = this.addUserPool(
userPoolName,
emailSender,
)
this.userPoolId = userPool.userPoolId
// attach the custom domain
if (customDomain) {
this.addCustomDomain(userPool, customDomain, uiCustomization)
this.authUrl = `https://${customDomain.domain}`
} else {
this.authUrl = userPool.userPoolProviderUrl
}
this.addTriggers(userPool)
// SNS Topics for SES Bounce Event
new Topic(this, 'SesBounceTopic', {
displayName: 'Bounce notifications topic',
})
// SNS Topics for SES Complaint Event
new Topic(this, 'SesComplaintsTopic', {
displayName: 'Complaints notifications topic',
})
// SNS Topics for SES Delivery Event
new Topic(this, 'SesDeliveryTopic', {
displayName: 'Delivery Notifications topic',
})
}
Example #7
Source File: datadog-dashboard.ts From cdk-datadog-resources with Apache License 2.0 | 6 votes |
constructor(scope: Construct, id: string, props: DatadogDashboardProps) {
const cfnProperties = camelcaseKeys(props, {
deep: true,
pascalCase: true,
});
new CfnResource(scope, id, {
type: 'Datadog::Dashboards::Dashboard',
properties: { ...cfnProperties },
});
}
Example #8
Source File: ciEntrypoint.ts From aws-boilerplate with MIT License | 6 votes |
constructor(scope: Construct, id: string, props: CiEntrypointProps) {
super(scope, id);
this.artifactsBucket = new Bucket(this, "ArtifactsBucket", {
versioned: true,
});
this.codeBuildProject = this.createBuildProject(this.artifactsBucket, props);
const deployBranches = props.envSettings.deployBranches;
if (deployBranches.length > 0) {
this.triggerFunction = new Function(this, 'TriggerLambda', {
runtime: Runtime.NODEJS_12_X,
handler: 'index.handler',
code: Code.fromAsset(path.join(__dirname, 'functions', 'trigger-entrypoint')),
environment: {
PROJECT_ENV_NAME: props.envSettings.projectEnvName,
DEPLOY_BRANCHES: JSON.stringify(deployBranches),
PROJECT_NAME: this.codeBuildProject.projectName
}
});
this.triggerFunction.addToRolePolicy(new PolicyStatement({
actions: [
'codebuild:StartBuild',
],
resources: [this.codeBuildProject.projectArn]
}));
props.codeRepository.onCommit('OnDeployCommit', {
target: new targets.LambdaFunction(this.triggerFunction)
});
}
}
Example #9
Source File: api-stack.ts From awsmug-serverless-graphql-api with MIT License | 5 votes |
constructor(scope: Construct, id: string, props: LambdaStackProps) {
super(scope, id, props);
this.rdsPassword = Secret.fromSecretNameV2(
this,
"rdsPassword",
"rdsPassword"
);
this.handler = new Function(this, "graphql", {
runtime: Runtime.NODEJS_14_X,
code: Code.fromAsset("app"),
handler: "build/src/graphql.handler",
vpc: props.vpc,
vpcSubnets: {
subnetType: SubnetType.ISOLATED,
},
securityGroup: SecurityGroup.fromSecurityGroupId(
this,
"inboundDbAccessSecurityGroup" + "rdsLambda",
props.inboundDbAccessSecurityGroup
),
environment: {
TYPEORM_URL: `postgres://${
props.rdsDbUser
}:${this.rdsPassword.secretValue.toString()}@${props.rdsEndpoint}:${
props.rdsPort
}/${props.rdsDbName}`,
TYPEORM_SYNCHRONIZE: "true",
TYPEORM_LOGGING: "true",
TYPEORM_ENTITIES: "./build/src/entity/*.entity.js",
},
});
this.api = new LambdaRestApi(this, "graphql-api", {
handler: this.handler,
proxy: false,
});
this.graphql = this.api.root.addResource("graphql");
this.graphql.addMethod("ANY");
this.apiPathOutput = new CfnOutput(this, "apiPath", {
value: this.api.root.path,
description: "Path of the API",
});
}
Example #10
Source File: amazon-efs-integrations-stack.ts From amazon-efs-integrations with MIT No Attribution | 5 votes |
constructor(scope: Construct, id: string, props: AmazonEfsIntegrationsStackProps) {
if (props.createEfsAccessPoints && !props.createEfsFilesystem) {
throw new Error('`createEfsFileSystem` must be set to true if `createEfsAccessPoints` is true');
}
super(scope, id, props);
const vpc = new Vpc(this, 'EfsIntegrationDemo', {maxAzs: 2});
const efsSecurityGroup = new SecurityGroup(this, 'EfsSecurityGroup', {securityGroupName: 'efs-demo-fs', vpc});
let fileSystem;
let efsAccessPoints;
if (props.createEfsFilesystem) {
/* tslint:disable-next-line:no-unused-expression */
fileSystem = new FileSystem(this, 'EfsIntegrationDemoFileSystem', {
encrypted: true,
fileSystemName: 'efs-demo-fs',
securityGroup: efsSecurityGroup,
vpc,
});
if (props.createEfsAccessPoints) {
efsAccessPoints = new EfsAccessPoints(
fileSystem,
props.createEcsOnEc2Service,
props.createEcsOnFargateService,
);
}
}
if (props.createEcsOnEc2Service || props.createEcsOnFargateService) {
const cluster = new Cluster(this, 'EcsCluster', {vpc});
let ecsOnEc2Service;
let ecsOnFargateService;
if (props.createEcsOnEc2Service) {
cluster.addCapacity('DefaultAutoScalingGroup', {
instanceType: new InstanceType('t2.large'),
maxCapacity: 2,
minCapacity: 2,
});
ecsOnEc2Service = EcsEfsIntegrationService.create(
ServiceType.EC2,
cluster,
fileSystem,
efsAccessPoints
) as ApplicationLoadBalancedEc2Service;
efsSecurityGroup.connections.allowFrom(ecsOnEc2Service.service, Port.tcp(2049));
}
if (props.createEcsOnFargateService) {
ecsOnFargateService = EcsEfsIntegrationService.create(
ServiceType.FARGATE,
cluster,
fileSystem,
efsAccessPoints
) as ApplicationLoadBalancedFargateService;
efsSecurityGroup.connections.allowFrom(ecsOnFargateService.service, Port.tcp(2049));
}
if (props.createEfsAccessPoints && fileSystem && efsAccessPoints) {
// tslint:disable-next-line: no-unused-expression
new EfsFileSystemPolicy(
fileSystem,
efsAccessPoints,
ecsOnEc2Service,
ecsOnFargateService,
);
}
}
}
Example #11
Source File: actions-stack.ts From hasura-cdk with MIT License | 5 votes |
constructor(scope: Construct, id: string, props: ActionsStackProps) {
super(scope, id, props);
const hostedZone = PublicHostedZone.fromHostedZoneAttributes(this, 'HasuraHostedZone', {
hostedZoneId: props.hostedZoneId,
zoneName: props.hostedZoneName,
});
const api = new RestApi(this, 'ActionsApi', {
domainName: {
domainName: props.actionsHostname,
certificate: props.certificates.actions,
},
restApiName: 'Actions',
description: 'Endpoint For Hasura Actions',
deployOptions: {
loggingLevel: MethodLoggingLevel.INFO,
dataTraceEnabled: true,
},
});
// API DNS record
new ARecord(this, 'ActionsApiAliasRecord', {
zone: hostedZone,
recordName: props.actionsHostname,
target: AddressRecordTarget.fromAlias(new route53_targets.ApiGateway(api)),
});
// Create a lambda layer to contain node_modules
const handlerDependenciesLayer = new RetainedLambdaLayerVersion(this, 'ActionHandlerDependencies', {
contentLocation: 'actions/dependencies-layer',
description: 'Dependencies layer',
compatibleRuntimes: [Runtime.NODEJS_12_X],
});
const actionHandler = new Function(this, 'ActionHandler', {
functionName: `${props.appName}-ActionHandler`,
handler: 'handler.handler',
memorySize: 1024,
runtime: Runtime.NODEJS_12_X,
code: Code.fromAsset(path.join(__dirname, '../../actions/dist/')),
timeout: Duration.seconds(4),
layers: [handlerDependenciesLayer],
});
const handlerResource = api.root.addResource('handler');
const actionHandlerIntegration = new LambdaIntegration(actionHandler);
handlerResource.addMethod('POST', actionHandlerIntegration);
handlerResource.addMethod('GET', actionHandlerIntegration);
}
Example #12
Source File: applicationMultipleTargetGroupsFargateService.ts From aws-boilerplate with MIT License | 5 votes |
/**
* Constructs a new instance of the ApplicationMultipleTargetGroupsFargateService class.
*/
constructor(scope: Construct, id: string, props: ApplicationMultipleTargetGroupsFargateServiceProps) {
super(scope, id, props);
this.assignPublicIp = props.assignPublicIp !== undefined ? props.assignPublicIp : false;
if (props.taskDefinition && props.taskImageOptions) {
throw new Error('You must specify only one of TaskDefinition or TaskImageOptions.');
} else if (props.taskDefinition) {
this.taskDefinition = props.taskDefinition;
} else if (props.taskImageOptions) {
const taskImageOptions = props.taskImageOptions;
this.taskDefinition = new FargateTaskDefinition(this, 'TaskDef', {
memoryLimitMiB: props.memoryLimitMiB,
cpu: props.cpu,
executionRole: props.executionRole,
taskRole: props.taskRole,
family: props.family,
});
for (const taskImageOptionsProps of taskImageOptions) {
const containerName = taskImageOptionsProps.containerName !== undefined ? taskImageOptionsProps.containerName : 'web';
const container = this.taskDefinition.addContainer(containerName, {
image: taskImageOptionsProps.image,
logging: this.logDriver,
environment: taskImageOptionsProps.environment,
secrets: taskImageOptionsProps.secrets,
command: ["sh", "-c", "/bin/chamber exec $CHAMBER_SERVICE_NAME -- ./scripts/run.sh"]
});
if (taskImageOptionsProps.containerPorts) {
for (const containerPort of taskImageOptionsProps.containerPorts) {
container.addPortMappings({
containerPort
});
}
}
}
} else {
throw new Error('You must specify one of: taskDefinition or image');
}
if (!this.taskDefinition.defaultContainer) {
throw new Error('At least one essential container must be specified');
}
if (this.taskDefinition.defaultContainer.portMappings.length === 0) {
this.taskDefinition.defaultContainer.addPortMappings({
containerPort: 80
});
}
this.service = this.createFargateService(props);
if (props.targetGroups) {
this.addPortMappingForTargets(this.taskDefinition.defaultContainer, props.targetGroups);
this.targetGroup = this.registerECSTargets(this.service, this.taskDefinition.defaultContainer, props.targetGroups);
}
}
Example #13
Source File: data-store-stack.ts From MDDL with MIT License | 4 votes |
constructor(scope: Construct, id: string, props: Props) {
super(scope, id, props)
// read out config and set defaults
const { vpcConfig = {}, rdsConfig = {}, providedKmsKey } = props
const {
cidrBlock = '10.0.0.0/16',
maxAzs = 2,
natGatewaysCount = 2,
} = vpcConfig
const {
backupRetentionDays = 30,
minCapacity = 1,
maxCapacity = 8,
} = rdsConfig
let kmsKey: IKey
if (providedKmsKey) {
// import key
kmsKey = Key.fromKeyArn(this, 'ProvidedKey', providedKmsKey.keyArn)
} else {
// create new KMS key for the data store to use
kmsKey = new Key(this, 'Key', {
description: `KMS Key for ${this.stackName} stack`,
enableKeyRotation: true,
})
// allow RDS to use the KMS key
// https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Overview.Encryption.Keys.html
kmsKey.addToResourcePolicy(
new PolicyStatement({
actions: ['kms:Decrypt', 'kms:GenerateDataKey*'],
resources: ['*'],
principals: [new AnyPrincipal()],
conditions: {
StringEquals: {
'kms:ViaService': `rds.${this.region}.amazonaws.com`,
'kms:CallerAccount': this.account,
},
},
}),
)
}
// create the VPC
this.vpc = new Vpc(this, 'Vpc', {
cidr: cidrBlock,
maxAzs: maxAzs,
enableDnsHostnames: false,
enableDnsSupport: true,
natGateways: natGatewaysCount,
subnetConfiguration: [
{
cidrMask: 28,
name: 'isolated',
subnetType: SubnetType.ISOLATED,
},
{
cidrMask: 28,
name: 'private',
subnetType: SubnetType.PRIVATE,
},
{
cidrMask: 28,
name: 'public',
subnetType: SubnetType.PUBLIC,
},
],
})
// create the root DB credentials
const rdsRootCredentialsSecret = new Secret(
this,
'RdsClusterCredentialsSecret',
{
secretName: `${this.stackName}-rds-root-credentials`,
generateSecretString: {
secretStringTemplate: JSON.stringify({
username: 'root',
}),
excludePunctuation: true,
includeSpace: false,
generateStringKey: 'password',
excludeCharacters: '"@/\\',
passwordLength: 30,
},
encryptionKey: kmsKey,
},
)
// configure RDS subnet group
const rdsSubnetGroup = new CfnDBSubnetGroup(this, 'RdsSubnetGroup', {
dbSubnetGroupDescription: `Subnet group for RDS ${this.stackName}`,
subnetIds: this.vpc.isolatedSubnets.map((s) => s.subnetId),
})
// configure RDS security group
const rdsSecurityGroup = new SecurityGroup(this, 'RdsSecurityGroup', {
vpc: this.vpc,
allowAllOutbound: false,
description: `${this.stackName} security group for RDS`,
})
// create the DB cluster
const rdsPort = 3306
const databaseName = 'root'
const rdsCluster = new CfnDBCluster(this, 'RdsCluster', {
engineMode: 'serverless',
engine: 'aurora-mysql',
engineVersion: '5.7.mysql_aurora.2.07.1',
enableHttpEndpoint: true,
databaseName,
kmsKeyId: kmsKey.keyId,
masterUsername: rdsRootCredentialsSecret
.secretValueFromJson('username')
.toString(),
masterUserPassword: rdsRootCredentialsSecret
.secretValueFromJson('password')
.toString(),
backupRetentionPeriod: backupRetentionDays,
scalingConfiguration: {
maxCapacity,
minCapacity,
autoPause: false,
},
deletionProtection: true,
dbSubnetGroupName: rdsSubnetGroup.ref,
dbClusterParameterGroupName: 'default.aurora-mysql5.7',
// The following three lines cause errors on updates, so they are commented out.
// If you need custom values for them, uncomment before first deployment.
// preferredMaintenanceWindow: maintenanceWindowWeekly,
// preferredBackupWindow: backupWindowDaily,
// port: rdsPort,
storageEncrypted: true,
vpcSecurityGroupIds: [rdsSecurityGroup.securityGroupId],
})
rdsCluster.applyRemovalPolicy(RemovalPolicy.SNAPSHOT)
rdsCluster.node.addDependency(this.vpc, kmsKey)
this.rdsEndpoint = rdsCluster.attrEndpointAddress
// add database networking
this.rdsAccessSecurityGroup = new SecurityGroup(
this,
'RdsAccessSecurityGroup',
{
vpc: this.vpc,
description: `${this.stackName} security group for RDS Access`,
},
)
rdsSecurityGroup.addIngressRule(
this.rdsAccessSecurityGroup,
Port.tcp(rdsPort),
)
rdsSecurityGroup.addEgressRule(
this.rdsAccessSecurityGroup,
Port.tcp(rdsPort),
)
this.rdsAccessSecurityGroup.addIngressRule(
rdsSecurityGroup,
Port.tcp(rdsPort),
)
const { layer: mysqlLayer } = this.addMysqlLayer()
// configure function used by the city stacks to create a new DB and user within this cluster
this.createDbUserFunction = new Function(this, 'CreateDbUserFunction', {
code: Code.fromAsset(path.join('build', 'create-db-and-user.zip')),
handler: 'index.handler',
runtime: Runtime.NODEJS_12_X,
vpc: this.vpc,
timeout: Duration.seconds(60),
securityGroups: [this.rdsAccessSecurityGroup],
layers: [mysqlLayer],
environment: {
DB_HOST: rdsCluster.attrEndpointAddress,
DB_USER: rdsRootCredentialsSecret
.secretValueFromJson('username')
.toString(),
DB_PASSWORD: rdsRootCredentialsSecret
.secretValueFromJson('password')
.toString(),
DB_DEFAULT_DATABASE: databaseName,
},
})
}
Example #14
Source File: fargateServiceResources.ts From aws-boilerplate with MIT License | 4 votes |
export class FargateServiceResources extends Construct {
mainVpc: IVpc;
mainCluster: ICluster;
backendRepository: IRepository;
publicLoadBalancer: IApplicationLoadBalancer;
publicLoadBalancerSecurityGroup: ISecurityGroup;
fargateContainerSecurityGroup: ISecurityGroup;
private readonly envSettings: EnvironmentSettings;
constructor(scope: Construct, id: string, props: EnvConstructProps) {
super(scope, id);
this.envSettings = props.envSettings;
this.mainVpc = this.retrieveMainVpc();
this.fargateContainerSecurityGroup = this.retrieveFargateContainerSecurityGroup();
this.mainCluster = this.retrieveMainCluster(this.mainVpc);
this.publicLoadBalancer = this.retrievePublicLoadBalancer(this.mainVpc);
this.publicLoadBalancerSecurityGroup = this.retrievePublicLoadBalancerSecurityGroup(props);
this.backendRepository = this.retrieveBackendECRRepositories();
}
private retrieveMainVpc() {
const stack = Stack.of(this);
return Vpc.fromVpcAttributes(this, "EC2MainVpc", {
vpcId: Fn.importValue(MainVpc.getVpcArnOutputExportName(this.envSettings)),
publicSubnetIds: [
Fn.importValue(MainVpc.getPublicSubnetOneIdOutputExportName(this.envSettings)),
Fn.importValue(MainVpc.getPublicSubnetTwoIdOutputExportName(this.envSettings)),
],
availabilityZones: [
stack.availabilityZones[0],
stack.availabilityZones[1],
],
});
}
private retrieveFargateContainerSecurityGroup() {
return SecurityGroup.fromSecurityGroupId(this, "FargateContainerSecurityGroup",
Fn.importValue(MainECSCluster.getFargateContainerSecurityGroupIdOutputExportName(this.envSettings)));
}
private retrieveMainCluster(vpc: IVpc) {
return Cluster.fromClusterAttributes(this, "ECSMainCluster", {
vpc,
securityGroups: [],
clusterName: MainECSCluster.getClusterName(this.envSettings),
});
}
private retrievePublicLoadBalancer(vpc: IVpc) {
const securityGroupId = Fn.importValue(
MainECSCluster.getPublicLoadBalancerSecurityGroupIdOutputExportName(this.envSettings));
const loadBalancerArn = Fn.importValue(MainECSCluster.getLoadBalancerArnOutputExportName(this.envSettings));
const loadBalancerDnsName = Fn.importValue(MainECSCluster.getLoadBalancerDnsNameOutput(this.envSettings));
const loadBalancerCanonicalHostedZoneId = Fn.importValue(
MainECSCluster.getLoadBalancerCanonicalHostedZoneIdOutputExportName(this.envSettings));
return ApplicationLoadBalancer.fromApplicationLoadBalancerAttributes(this,
"MainPublicLoadBalancer", {
vpc,
loadBalancerArn,
securityGroupId,
loadBalancerDnsName,
loadBalancerCanonicalHostedZoneId
});
}
private retrievePublicLoadBalancerSecurityGroup(props: EnvConstructProps): ISecurityGroup {
return SecurityGroup.fromSecurityGroupId(this, "LbSecurityGroup",
Fn.importValue(MainECSCluster.getPublicLoadBalancerSecurityGroupIdOutputExportName(props.envSettings)));
}
private retrieveBackendECRRepositories() {
return Repository.fromRepositoryName(this, "ECRBackendRepository",
GlobalECR.getBackendRepositoryName(this.envSettings));
}
}
Example #15
Source File: city-stack.ts From MDDL with MIT License | 4 votes |
constructor(scope: Construct, id: string, props: Props) {
super(scope, id, props)
// initialise properties
this.bucketNames = {}
// read out config
const {
dataStoreStack,
authStack,
apiDomainConfig,
webAppDomainConfig,
hostedZoneAttributes,
jwtAuth,
additionalCallbackUrls = [],
emailSender,
agencyEmailDomainsWhitelist = [],
monitoring = {},
throttling = {},
providedKmsKey,
} = props
// check jwt auth is given if auth stack is not
if (!authStack && !jwtAuth) {
throw new Error(
'jwtAuth must be provided when authStack is not provided in stack ' +
this.stackName,
)
}
// read in the signing key parameter if its provided
if (jwtAuth && jwtAuth.signingKeyParameterPath) {
const signingKeyParameter = StringParameter.fromStringParameterName(
this,
'SigningKeyParameter',
jwtAuth.signingKeyParameterPath,
)
this.environmentVariables[EnvironmentVariables.AUTH_SIGNING_KEY] =
signingKeyParameter.stringValue
} else if (jwtAuth && jwtAuth.integrationType === 'NYCID_OAUTH') {
// verify requirements for NYC.ID
throw new Error(
'jwtConfiguration.signingKeyParameterPath must be provided for NYCID_OAUTH integration',
)
}
// check data store stack is given - it is "optional" to allow for configuration
// to be put together at runtime
if (!dataStoreStack) {
throw new Error('dataStoreStack must be provided')
}
// set VPC details for lambda access
const { vpc, rdsAccessSecurityGroup, rdsEndpoint } = dataStoreStack
this.lambdaVpc = vpc
this.lambdaSecurityGroups = [rdsAccessSecurityGroup]
this.rdsEndpoint = rdsEndpoint
// check the cloudfront certificate is in north virginia
if (
webAppDomainConfig &&
webAppDomainConfig.certificateArn &&
!webAppDomainConfig.certificateArn.toLowerCase().includes('us-east-1')
) {
throw new Error(
'webAppDomainConfig.certificateArn must be a certificate in us-east-1',
)
}
// reference hosted zone
const hostedZone: IHostedZone | undefined = hostedZoneAttributes
? HostedZone.fromHostedZoneAttributes(
this,
`HostedZone`,
hostedZoneAttributes,
)
: undefined
// add hosting for the web app
const { domain: webAppDomain } = this.addHosting(
'WebApp',
webAppDomainConfig,
hostedZone,
)
// add auth stack integration
const { jwtConfiguration } = this.addAuthIntegration(
`https://${webAppDomain}`,
[`https://${webAppDomain}/authorize`, ...additionalCallbackUrls],
[`https://${webAppDomain}`, ...additionalCallbackUrls],
authStack,
apiDomainConfig,
jwtAuth,
)
// create/reference the city key used for encryption of resources in this stack
if (providedKmsKey) {
this.kmsKey = Key.fromKeyArn(this, 'ProvidedKey', providedKmsKey.keyArn)
} else {
const { kmsKey } = this.addKmsKey()
this.kmsKey = kmsKey
}
// create the DB and access credentials for this city
const { secret } = this.addDbAndCredentials(
this.kmsKey,
dataStoreStack.createDbUserFunction,
)
// create the mysql lambda layer
const { layer: mySqlLayer } = this.addMysqlLayer()
// create the graphics magick lambda layer
const { layer: gmLayer } = this.addGraphicsMagickLayer()
// add api
const { api, authorizer, corsOrigins, defaultStage } = this.addApi(
webAppDomain,
jwtConfiguration,
apiDomainConfig,
hostedZone,
)
const apiProps: ApiProps = {
api,
authorizer,
dbSecret: secret,
mySqlLayer,
gmLayer,
}
// create dead letter queue
const { queue: deadLetterQueue } = this.createDeadLetterQueue(this.kmsKey)
// create uploads bucket
this.uploadsBucket = this.createUploadsBucket(
this.kmsKey,
corsOrigins,
).bucket
// create audit log group and queue
this.auditLogGroup = this.createAuditLogGroup(this.kmsKey).logGroup
this.auditLogQueue = this.createAuditLogQueue(
this.kmsKey,
deadLetterQueue,
).queue
// create email processing queue
this.emailProcessorQueue = this.createEmailProcessorQueue(
this.kmsKey,
deadLetterQueue,
).queue
this.environmentVariables = {
...this.environmentVariables,
[EnvironmentVariables.DOCUMENTS_BUCKET]: this.uploadsBucket.bucketName,
[EnvironmentVariables.USERINFO_ENDPOINT]:
jwtConfiguration.userInfoEndpoint,
[EnvironmentVariables.WEB_APP_DOMAIN]: webAppDomain,
[EnvironmentVariables.EMAIL_SENDER]: `${emailSender.name} <${emailSender.address}>`,
[EnvironmentVariables.AGENCY_EMAIL_DOMAINS_WHITELIST]: agencyEmailDomainsWhitelist.join(
',',
),
[EnvironmentVariables.ACTIVITY_CLOUDWATCH_LOG_GROUP]: this.auditLogGroup
.logGroupName,
[EnvironmentVariables.ACTIVITY_RECORD_SQS_QUEUE_URL]: this.auditLogQueue
.queueUrl,
[EnvironmentVariables.EMAIL_PROCESSOR_SQS_QUEUE_URL]: this
.emailProcessorQueue.queueUrl,
[EnvironmentVariables.ENVIRONMENT_NAME]: this.stackName,
[EnvironmentVariables.AUTH_INTEGRATION_TYPE]: jwtConfiguration.integrationType
? jwtConfiguration.integrationType
: 'OAUTH',
}
if (jwtConfiguration.emailUnverifiedRedirectEndpoint) {
this.environmentVariables[
EnvironmentVariables.AUTH_EMAIL_UNVERIFIED_REDIRECT
] = jwtConfiguration.emailUnverifiedRedirectEndpoint
}
if (monitoring) {
const { sentryDsn } = monitoring
if (sentryDsn) {
this.environmentVariables[EnvironmentVariables.SENTRY_DSN] = sentryDsn
}
}
this.createAuditLogQueueProcessor(this.auditLogQueue)
this.createEmailQueueProcessor(
this.emailProcessorQueue,
emailSender.address,
)
// add user routes
this.addUserRoutes(apiProps)
// add document routes
this.addDocumentRoutes(apiProps)
// add collection routes
this.addCollectionRoutes(apiProps)
// add account delegate routes
this.addAccountDelegateRoutes(apiProps)
// add database migrations
this.runMigrations(secret, mySqlLayer)
// set up throttling
this.configureThrottling(defaultStage, throttling)
// set up alerts
if (monitoring.alertsSnsTopicArn) {
this.configureAlerts(monitoring.alertsSnsTopicArn, api, deadLetterQueue, [
this.auditLogQueue,
this.emailProcessorQueue,
])
}
}
Example #16
Source File: ciPipeline.ts From aws-boilerplate with MIT License | 4 votes |
export class CiPipeline extends Construct {
buildStageName = 'Build';
deployStageName = 'Deploy';
static getSourceOutputArtifact(envSettings: EnvironmentSettings) {
return Artifact.artifact(`${envSettings.projectEnvName}-source`)
}
constructor(scope: Construct, id: string, props: CiPipelineProps) {
super(scope, id);
const pipeline = this.createPipeline(props);
this.configureEnv(pipeline, props);
}
private configureEnv(pipeline: Pipeline, props: CiPipelineProps) {
const sourceOutputArtifact = CiPipeline.getSourceOutputArtifact(props.envSettings);
const buildStage = this.selectStage(this.buildStageName, pipeline);
const deployStage = this.selectStage(this.deployStageName, pipeline);
new ComponentsCiConfig(this, "ComponentsConfig", {
buildStage,
deployStage,
envSettings: props.envSettings,
inputArtifact: sourceOutputArtifact,
});
new BackendCiConfig(this, "BackendConfig", {
buildStage,
deployStage,
envSettings: props.envSettings,
inputArtifact: sourceOutputArtifact,
backendRepository: props.backendRepository,
});
new WebappCiConfig(this, "WebAppConfig", {
envSettings: props.envSettings,
buildStage,
deployStage,
inputArtifact: sourceOutputArtifact,
});
new ServerlessCiConfig(this, "WorkersConfig", {
name: 'workers',
envSettings: props.envSettings,
buildStage,
deployStage,
inputArtifact: sourceOutputArtifact,
});
new UploadVersionCiConfig(this, "UploadVersionConfig", {
envSettings: props.envSettings,
buildStage,
deployStage,
inputArtifact: sourceOutputArtifact,
});
}
private selectStage(name: string, pipeline: Pipeline) {
const stage = pipeline.stages.find(stage => stage.stageName === name);
if (!stage) {
throw Error(`Stage ${name} hasn't been found!`);
}
return stage;
}
private createPipeline(props: CiPipelineProps) {
return new Pipeline(this, "Pipeline", {
pipelineName: `${props.envSettings.projectEnvName}-ci`,
stages: [{
stageName: "Source",
actions: [
new S3SourceAction({
actionName: `${props.envSettings.projectEnvName}-source`,
bucket: props.entrypointArtifactBucket,
bucketKey: CiEntrypoint.getArtifactsName(props.envSettings),
output: CiPipeline.getSourceOutputArtifact(props.envSettings),
trigger: S3Trigger.POLL,
}),
],
}, {
stageName: this.buildStageName,
actions: [],
}, {
stageName: this.deployStageName,
actions: [],
}],
});
}
}
Example #17
Source File: hasura-stack.ts From hasura-cdk with MIT License | 4 votes |
constructor(scope: Construct, id: string, props: HasuraStackProps) {
super(scope, id, props);
const hostedZone = PublicHostedZone.fromHostedZoneAttributes(this, 'HasuraHostedZone', {
hostedZoneId: props.hostedZoneId,
zoneName: props.hostedZoneName,
});
const hasuraDatabaseName = props.appName;
const hasuraDatabase = new DatabaseInstance(this, 'HasuraDatabase', {
instanceIdentifier: props.appName,
databaseName: hasuraDatabaseName,
engine: DatabaseInstanceEngine.POSTGRES,
instanceType: InstanceType.of(InstanceClass.BURSTABLE3, InstanceSize.MICRO),
masterUsername: 'syscdk',
storageEncrypted: true,
allocatedStorage: 20,
maxAllocatedStorage: 100,
vpc: props.vpc,
deletionProtection: false,
multiAz: props.multiAz,
removalPolicy: RemovalPolicy.DESTROY,
});
const hasuraUsername = 'hasura';
const hasuraUserSecret = new DatabaseSecret(this, 'HasuraDatabaseUser', {
username: hasuraUsername,
masterSecret: hasuraDatabase.secret,
});
hasuraUserSecret.attach(hasuraDatabase); // Adds DB connections information in the secret
// Output the Endpoint Address so it can be used in post-deploy
new CfnOutput(this, 'HasuraDatabaseUserSecretArn', {
value: hasuraUserSecret.secretArn,
});
new CfnOutput(this, 'HasuraDatabaseMasterSecretArn', {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
value: hasuraDatabase.secret!.secretArn,
});
const hasuraDatabaseUrlSecret = new Secret(this, 'HasuraDatabaseUrlSecret', {
secretName: `${props.appName}-HasuraDatabaseUrl`,
});
new CfnOutput(this, 'HasuraDatabaseUrlSecretArn', {
value: hasuraDatabaseUrlSecret.secretArn,
});
const hasuraAdminSecret = new Secret(this, 'HasuraAdminSecret', {
secretName: `${props.appName}-HasuraAdminSecret`,
});
new CfnOutput(this, 'HasuraAdminSecretArn', {
value: hasuraAdminSecret.secretArn,
});
const hasuraJwtSecret = new Secret(this, 'HasuraJwtSecret', {
secretName: `${props.appName}-HasuraJWTSecret`,
});
new CfnOutput(this, 'HasuraJwtSecretArn', {
value: hasuraJwtSecret.secretArn,
});
// Create a load-balanced Fargate service and make it public
const fargate = new ApplicationLoadBalancedFargateService(this, 'HasuraFargateService', {
serviceName: props.appName,
vpc: props.vpc,
cpu: 256,
desiredCount: props.multiAz ? 2 : 1,
taskImageOptions: {
image: ContainerImage.fromRegistry('hasura/graphql-engine:v1.2.1'),
containerPort: 8080,
enableLogging: true,
environment: {
HASURA_GRAPHQL_ENABLE_CONSOLE: 'true',
HASURA_GRAPHQL_PG_CONNECTIONS: '100',
HASURA_GRAPHQL_LOG_LEVEL: 'debug',
},
secrets: {
HASURA_GRAPHQL_DATABASE_URL: ECSSecret.fromSecretsManager(hasuraDatabaseUrlSecret),
HASURA_GRAPHQL_ADMIN_SECRET: ECSSecret.fromSecretsManager(hasuraAdminSecret),
HASURA_GRAPHQL_JWT_SECRET: ECSSecret.fromSecretsManager(hasuraJwtSecret),
},
},
memoryLimitMiB: 512,
publicLoadBalancer: true, // Default is false
certificate: props.certificates.hasura,
domainName: props.hasuraHostname,
domainZone: hostedZone,
assignPublicIp: true,
});
fargate.targetGroup.configureHealthCheck({
enabled: true,
path: '/healthz',
healthyHttpCodes: '200',
});
hasuraDatabase.connections.allowFrom(fargate.service, new Port({
protocol: Protocol.TCP,
stringRepresentation: 'Postgres Port',
fromPort: 5432,
toPort: 5432,
}));
}
Example #18
Source File: applicationMultipleTargetGroupsFargateServiceBase.ts From aws-boilerplate with MIT License | 4 votes |
/**
* The base class for ApplicationMultipleTargetGroupsEc2Service and ApplicationMultipleTargetGroupsFargateService classes.
*/
export abstract class ApplicationMultipleTargetGroupsServiceBase extends Construct {
/**
* The desired number of instantiations of the task definition to keep running on the service.
*/
readonly desiredCount: number;
/**
* The default Application Load Balancer for the service (first added load balancer).
*/
readonly loadBalancer: IApplicationLoadBalancer;
/**
* The default listener for the service (first added listener).
*/
readonly listener: IApplicationListener;
/**
* The cluster that hosts the service.
*/
readonly cluster: ICluster;
protected logDriver?: LogDriver;
protected listeners: IApplicationListener[];
protected targetGroups: ApplicationTargetGroup[];
private readonly loadBalancers: IApplicationLoadBalancer[];
/**
* Constructs a new instance of the ApplicationMultipleTargetGroupsServiceBase class.
*/
protected constructor(scope: Construct, id: string, props: ApplicationMultipleTargetGroupsServiceBaseProps) {
super(scope, id);
this.listeners = [];
this.targetGroups = [];
this.loadBalancers = [];
this.validateInput(props);
this.cluster = props.cluster;
this.desiredCount = props.desiredCount || 1;
if (props.taskImageOptions) {
for (const taskImageOptionsProps of props.taskImageOptions) {
this.logDriver = this.createLogDriver(taskImageOptionsProps.enableLogging, taskImageOptionsProps.logDriver);
}
}
for (const lbProps of props.loadBalancers) {
const internetFacing = lbProps.publicLoadBalancer !== undefined ? lbProps.publicLoadBalancer : true;
const {loadBalancer: lb} = lbProps;
this.loadBalancers.push(lb);
for (const listener of lbProps.listeners) {
this.listeners.push(listener);
}
this.createDomainName(lb, internetFacing, lbProps.domainName, lbProps.domainZone);
}
// set up default load balancer and listener.
this.loadBalancer = this.loadBalancers[0];
this.listener = this.listeners[0];
}
protected createAWSLogDriver(prefix: string): AwsLogDriver {
return new AwsLogDriver({streamPrefix: prefix});
}
protected findListener(name?: string): IApplicationListener {
if (!name) {
return this.listener;
}
for (const listener of this.listeners) {
if (listener.node.id === name) {
return listener;
}
}
throw new Error(`Listener ${name} is not defined. Did you define listener with name ${name}?`);
}
protected registerECSTargets(service: BaseService, container: ContainerDefinition, targets: ApplicationTargetProps[]): ApplicationTargetGroup {
targets?.forEach((targetProps, index) => {
const idSuffix = index > 0 ? index : '';
const targetGroup = new ApplicationTargetGroup(this, `TargetGroup${idSuffix}`, {
vpc: service.cluster.vpc,
port: targetProps.containerPort,
healthCheck: {
path: '/lbcheck',
protocol: ELBProtocol.HTTP,
interval: Duration.seconds(6),
timeout: Duration.seconds(5),
healthyThresholdCount: 2,
unhealthyThresholdCount: 2,
},
deregistrationDelay: Duration.seconds(10),
targetType: TargetType.IP,
targets: [
service.loadBalancerTarget({
containerName: container.containerName,
containerPort: targetProps.containerPort,
protocol: targetProps.protocol,
}),
],
});
this.findListener(targetProps.listener).addTargetGroups(`ECSTargetGroup${idSuffix}${container.containerName}${targetProps.containerPort}`, {
hostHeader: targetProps.hostHeader,
pathPattern: targetProps.pathPattern,
priority: targetProps.priority,
targetGroups: [targetGroup],
});
this.targetGroups.push(targetGroup);
});
if (this.targetGroups.length === 0) {
throw new Error('At least one target group should be specified.');
}
return this.targetGroups[0];
}
protected addPortMappingForTargets(container: ContainerDefinition, targets: ApplicationTargetProps[]): void {
for (const target of targets) {
if (!container.findPortMapping(target.containerPort, target.protocol || Protocol.TCP)) {
container.addPortMappings({
containerPort: target.containerPort,
protocol: target.protocol
});
}
}
}
/**
* Create log driver if logging is enabled.
*/
private createLogDriver(enableLoggingProp?: boolean, logDriverProp?: LogDriver) {
const enableLogging = enableLoggingProp !== undefined ? enableLoggingProp : true;
return logDriverProp !== undefined
? logDriverProp : enableLogging
? this.createAWSLogDriver(this.node.id) : undefined;
}
private validateInput(props: ApplicationMultipleTargetGroupsServiceBaseProps) {
if (props.cluster && props.vpc) {
throw new Error('You can only specify either vpc or cluster. Alternatively, you can leave both blank');
}
if (props.desiredCount !== undefined && props.desiredCount < 1) {
throw new Error('You must specify a desiredCount greater than 0');
}
if (props.loadBalancers) {
if (props.loadBalancers.length === 0) {
throw new Error('At least one load balancer must be specified');
}
for (const lbProps of props.loadBalancers) {
if (lbProps.listeners.length === 0) {
throw new Error('At least one listener must be specified');
}
}
}
}
private createDomainName(loadBalancer: IApplicationLoadBalancer, internetFacing: boolean, name?: string, zone?: IHostedZone) {
let domainName = loadBalancer.loadBalancerDnsName;
if (typeof name !== 'undefined') {
if (typeof zone === 'undefined') {
throw new Error('A Route53 hosted domain zone name is required to configure the specified domain name');
}
if (internetFacing) {
const record = new ARecord(this, `DNS${loadBalancer.node.id}`, {
zone,
recordName: name,
target: RecordTarget.fromAlias(new LoadBalancerTarget(loadBalancer)),
});
domainName = record.domainName;
}
}
return domainName;
}
}
Example #19
Source File: index.ts From aws-cdk-dynamodb-seeder with Apache License 2.0 | 4 votes |
export class Seeder extends Construct {
protected props: Props;
constructor(scope: Construct, id: string, props: Props) {
super(scope, id);
if (!props.setup || !Array.isArray(props.setup)) throw new Error('setup value must be an array of JSON objects');
this.props = props;
const destinationBucket = new Bucket(this, 'acds-bucket', {
removalPolicy: RemovalPolicy.DESTROY,
});
tmp.setGracefulCleanup();
tmp.dir((err, dir) => {
if (err) throw err;
this.writeTempFile(dir, 'setup.json', props.setup);
if (props.teardown) {
this.writeTempFile(dir, 'teardown.json', props.teardown);
}
new BucketDeployment(this, id, {
sources: [Source.asset(dir)],
destinationBucket,
retainOnDelete: false,
});
});
const fn = new Function(this, 'handler', {
runtime: Runtime.NODEJS_12_X,
handler: 'index.handler',
timeout: Duration.seconds(900),
code: Code.fromInline(`
console.log('function loaded');
const AWS = require('aws-sdk');
const s3 = new AWS.S3();
const writeTypeFromAction = (action) => {
if (action === "Put")
return "Item";
if (action === "Delete")
return "Key";
}
const run = async (filename, action) => {
console.log('reading from s3');
const data = await s3.getObject({
Bucket: "${destinationBucket.bucketName}",
Key: filename
}).promise();
console.log('finished reading from s3');
console.log('transforming seed data');
const seed = JSON.parse(data.Body.toString());
console.log('finished transforming seed data');
const documentClient = new AWS.DynamoDB.DocumentClient({
convertEmptyValues: true
});
console.log('sending data to dynamodb');
do {
const requests = [];
const batch = seed.splice(0, 25);
for (let i = 0; i < batch.length; i++) {
requests.push({
[action + "Request"]: {
[writeTypeFromAction(action)]: batch[i]
}
});
}
await documentClient.batchWrite({
RequestItems: {
'${props.table.tableName}': [...requests]
}
}).promise();
}
while (seed.length > 0);
console.log('finished sending data to dynamodb');
}
exports.handler = async (event) => {
if (event.mode === "delete")
await run("teardown.json", "Delete");
if (event.mode === "create" || event.mode === "update")
await run("setup.json", "Put");
}`),
});
destinationBucket.grantRead(fn);
props.table.grantWriteData(fn);
const onEvent = new AwsCustomResource(this, 'on-event', {
onCreate: {
...this.callLambdaOptions(),
parameters: {
FunctionName: fn.functionArn,
InvokeArgs: JSON.stringify({
mode: 'create',
}),
},
},
onDelete: props.teardown
? {
...this.callLambdaOptions(),
parameters: {
FunctionName: fn.functionArn,
InvokeArgs: JSON.stringify({
mode: 'delete',
}),
},
}
: undefined,
onUpdate: props.refreshOnUpdate
? {
...this.callLambdaOptions(),
parameters: {
FunctionName: fn.functionArn,
InvokeArgs: JSON.stringify({
mode: 'update',
}),
},
}
: undefined,
policy: AwsCustomResourcePolicy.fromSdkCalls({ resources: AwsCustomResourcePolicy.ANY_RESOURCE }),
});
fn.grantInvoke(onEvent);
}
private callLambdaOptions(): AwsSdkCall {
return {
service: 'Lambda',
action: 'invokeAsync',
apiVersion: '2015-03-31',
physicalResourceId: {
id: `${this.props.table.tableArn}-seeder`,
},
};
}
private writeTempFile(dir: string, filename: string, data: Item[] | ItemKey[]): void {
const buffer = Buffer.from(JSON.stringify(data));
const filepath = dir + '/' + filename;
fs.writeFileSync(filepath, buffer);
}
}
Example #20
Source File: stack.ts From keycloak-on-aws with Apache License 2.0 | 4 votes |
constructor(scope: Construct, id: string, props: KeycloakStackProps = {}) {
super(scope, id, props);
const dbMsg = props.auroraServerless ? 'using aurora serverless' : 'rds mysql';
const vpcMsg = props.fromExistingVPC ? 'existing vpc' : 'new vpc';
this.setDescription(`(SO8021) - Deploy keycloak ${dbMsg} with ${vpcMsg}. template version: ${process.env.VERSION}`);
const certificateArnParam = this.makeParam('CertificateArn', {
type: 'String',
description: 'Certificate Arn for Application Load Balancer',
minLength: 5,
});
this.addGroupParam({ 'Application Load Balancer Settings': [certificateArnParam] });
this._keycloakSettings.certificateArn = certificateArnParam.valueAsString;
if (!props.auroraServerless) {
const databaseInstanceType = this.makeParam('DatabaseInstanceType', {
type: 'String',
description: 'Instance type to be used for the core instances',
allowedValues: INSTANCE_TYPES,
default: 'r5.large',
});
this.addGroupParam({ 'Database Instance Settings': [databaseInstanceType] });
this._keycloakSettings.databaseInstanceType = new ec2.InstanceType(databaseInstanceType.valueAsString);
}
if (props.fromExistingVPC) {
const vpcIdParam = this.makeParam('VpcId', {
type: 'AWS::EC2::VPC::Id',
description: 'Your VPC Id',
});
const pubSubnetsParam = this.makeParam('PubSubnets', {
type: 'List<AWS::EC2::Subnet::Id>',
description: 'Public subnets (Choose two)',
});
const privSubnetsParam = this.makeParam('PrivSubnets', {
type: 'List<AWS::EC2::Subnet::Id>',
description: 'Private subnets (Choose two)',
});
const dbSubnetsParam = this.makeParam('DBSubnets', {
type: 'List<AWS::EC2::Subnet::Id>',
description: 'Database subnets (Choose two)',
});
this.addGroupParam({ 'VPC Settings': [vpcIdParam, pubSubnetsParam, privSubnetsParam, dbSubnetsParam] });
const azs = ['a', 'b'];
const vpc = ec2.Vpc.fromVpcAttributes(this, 'VpcAttr', {
vpcId: vpcIdParam.valueAsString,
vpcCidrBlock: Aws.NO_VALUE,
availabilityZones: azs,
publicSubnetIds: azs.map((_, index) => Fn.select(index, pubSubnetsParam.valueAsList)),
privateSubnetIds: azs.map((_, index) => Fn.select(index, privSubnetsParam.valueAsList)),
isolatedSubnetIds: azs.map((_, index) => Fn.select(index, dbSubnetsParam.valueAsList)),
});
Object.assign(this._keycloakSettings, {
vpc,
publicSubnets: { subnets: vpc.publicSubnets },
privateSubnets: { subnets: vpc.privateSubnets },
databaseSubnets: { subnets: vpc.isolatedSubnets },
});
}
const minContainersParam = this.makeParam('MinContainers', {
type: 'Number',
description: 'minimum containers count',
default: 2,
minValue: 2,
});
const maxContainersParam = this.makeParam('MaxContainers', {
type: 'Number',
description: 'maximum containers count',
default: 10,
minValue: 2,
});
const targetCpuUtilizationParam = this.makeParam('AutoScalingTargetCpuUtilization', {
type: 'Number',
description: 'Auto scaling target cpu utilization',
default: 75,
minValue: 0,
});
this.addGroupParam({ 'AutoScaling Settings': [minContainersParam, maxContainersParam, targetCpuUtilizationParam] });
const javaOptsParam = this.makeParam('JavaOpts', {
type: 'String',
description: 'JAVA_OPTS environment variable',
});
this.addGroupParam({ 'Environment variable': [javaOptsParam] });
new KeyCloak(this, 'KeyCloak', {
vpc: this._keycloakSettings.vpc,
publicSubnets: this._keycloakSettings.publicSubnets,
privateSubnets: this._keycloakSettings.privateSubnets,
databaseSubnets: this._keycloakSettings.databaseSubnets,
certificateArn: this._keycloakSettings.certificateArn,
auroraServerless: props.auroraServerless,
databaseInstanceType: this._keycloakSettings.databaseInstanceType,
stickinessCookieDuration: Duration.days(7),
nodeCount: minContainersParam.valueAsNumber,
autoScaleTask: {
min: minContainersParam.valueAsNumber,
max: maxContainersParam.valueAsNumber,
targetCpuUtilization: targetCpuUtilizationParam.valueAsNumber,
},
env: {
JAVA_OPTS: javaOptsParam.valueAsString,
},
});
}
Example #21
Source File: webAppCloudFrontDistribution.ts From aws-boilerplate with MIT License | 4 votes |
export class WebAppCloudFrontDistribution extends Construct {
private distribution: CloudFrontWebDistribution;
constructor(scope: Construct, id: string, props: WebAppCloudFrontDistributionProps) {
super(scope, id);
const staticFilesBucket = this.createStaticFilesBucket();
this.distribution = this.createCloudFrontWebDistribution(staticFilesBucket, props);
this.createDnsRecord(this.distribution, props);
this.createDeployment(staticFilesBucket, this.distribution, props);
}
private createDeployment(staticFilesBucket: Bucket, distribution: CloudFrontWebDistribution, props: WebAppCloudFrontDistributionProps) {
new BucketDeployment(this, 'DeployWebsite', {
distribution,
distributionPaths: ['/index.html'],
sources: props.sources,
destinationBucket: staticFilesBucket,
cacheControl: [CacheControl.setPublic(), CacheControl.maxAge(Duration.hours(1))],
});
}
protected createStaticFilesBucket() {
return new Bucket(this, "StaticFilesBucket", {
versioned: true,
publicReadAccess: true,
websiteIndexDocument: 'index.html',
});
}
private createCloudFrontWebDistribution(staticFilesBucket: Bucket, props: WebAppCloudFrontDistributionProps) {
const indexFile = '/index.html';
const originConfigs = [this.createStaticFilesSourceConfig(staticFilesBucket, props)];
const apiSourceConfig = this.createApiProxySourceConfig(props);
if (apiSourceConfig) {
originConfigs.push(apiSourceConfig);
}
return new CloudFrontWebDistribution(this, "CloudFrontWebDistribution", {
defaultRootObject: indexFile,
errorConfigurations: [{errorCode: 404, responseCode: 200, responsePagePath: indexFile}],
originConfigs: originConfigs,
viewerCertificate: {
aliases: [props.domainName],
props: {
acmCertificateArn: props.certificateArn,
sslSupportMethod: 'sni-only',
},
},
});
}
private createStaticFilesSourceConfig(staticFilesBucket: Bucket, props: WebAppCloudFrontDistributionProps): SourceConfiguration {
const lambdaFunctionAssociations: LambdaFunctionAssociation[] = [];
const originHeaders: { [key: string]: string } = {};
if (props.basicAuth) {
const authLambdaParam = new AwsCustomResource(
this,
"GetParameter",
{
policy: AwsCustomResourcePolicy.fromSdkCalls({
resources: AwsCustomResourcePolicy.ANY_RESOURCE,
}),
onUpdate: {
action: 'getParameter',
parameters: {Name: props.authLambdaSSMParameterName},
region: 'us-east-1',
service: 'SSM',
physicalResourceId: PhysicalResourceId.of(Date.now().toString()),
}
}
)
lambdaFunctionAssociations.push({
eventType: LambdaEdgeEventType.ORIGIN_REQUEST,
lambdaFunction: lambda.Version.fromVersionArn(this, "AuthLambdaFunction",
authLambdaParam.getResponseField("Parameter.Value"))
});
originHeaders['X-Auth-String'] = new Buffer(props.basicAuth).toString('base64')
}
return {
behaviors: [{
lambdaFunctionAssociations,
isDefaultBehavior: true,
forwardedValues: {
headers: ['Authorization', 'CloudFront-Viewer-Country'],
queryString: true,
}
}],
originPath: '',
customOriginSource: {
domainName: staticFilesBucket.bucketWebsiteDomainName,
originProtocolPolicy: OriginProtocolPolicy.HTTP_ONLY,
},
originHeaders,
};
}
private createApiProxySourceConfig(props: WebAppCloudFrontDistributionProps): SourceConfiguration | null {
if (!props.apiDomainName) {
return null;
}
return {
behaviors: [{
pathPattern: '/api/*',
allowedMethods: CloudFrontAllowedMethods.ALL,
forwardedValues: {
queryString: true,
headers: ["Host"],
cookies: {forward: 'all'},
},
defaultTtl: Duration.seconds(0),
minTtl: Duration.seconds(0),
maxTtl: Duration.seconds(0),
}],
customOriginSource: {
domainName: props.apiDomainName,
originProtocolPolicy: OriginProtocolPolicy.HTTPS_ONLY,
},
};
}
private createDnsRecord(distribution: CloudFrontWebDistribution, props: WebAppCloudFrontDistributionProps) {
return new ARecord(this, `DNSRecord`, {
zone: props.domainZone,
recordName: props.domainName,
target: RecordTarget.fromAlias(new CloudFrontTarget(distribution)),
});
}
}