aws-cdk-lib#Duration TypeScript Examples
The following examples show how to use
aws-cdk-lib#Duration.
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: index.ts From cloudstructs with Apache License 2.0 | 6 votes |
constructor(scope: Construct, id: string, props: SlackTextractProps) {
super(scope, id);
const handler = new DetectFunction(this, 'handler', {
timeout: Duration.seconds(30),
logRetention: logs.RetentionDays.ONE_MONTH,
environment: {
SLACK_TOKEN: props.botToken.toString(),
},
});
handler.addToRolePolicy(new iam.PolicyStatement({
actions: ['textract:DetectDocumentText'],
resources: ['*'],
}));
new SlackEvents(this, 'SlackEvents', {
signingSecret: props.signingSecret,
});
const fileSharedRule = new events.Rule(this, 'SlackEventsRule', {
eventPattern: {
detail: {
event: {
type: ['file_shared'],
},
},
resources: [props.appId],
source: ['slack'],
},
});
fileSharedRule.addTarget(new targets.LambdaFunction(handler, {
event: events.RuleTargetInput.fromEventPath('$.detail.event'),
}));
}
Example #2
Source File: gitlab-runner-instance.ts From cdk-gitlab-runner with Apache License 2.0 | 6 votes |
/**
* Add expire time function for spotfleet runner !!! .
*
* @param duration - Block duration.
*/
public expireAfter(duration: Duration) {
const date = new Date();
date.setSeconds(date.getSeconds() + duration.toSeconds());
this.validUntil = date.toISOString();
}
Example #3
Source File: url-shortener.test.ts From cloudstructs with Apache License 2.0 | 6 votes |
test('UrlShortener', () => {
const hostedZone = new route53.HostedZone(stack, 'HostedZone', { zoneName: 'cstructs.com' });
new UrlShortener(stack, 'UrlShortener', {
hostedZone,
expiration: Duration.days(60),
});
expect(Template.fromStack(stack).toJSON()).toMatchSnapshot();
});
Example #4
Source File: toolkit-cleaner.test.ts From cloudstructs with Apache License 2.0 | 6 votes |
test('with retainAssetsNewerThan', () => {
new ToolkitCleaner(stack, 'ToolkitCleaner', {
retainAssetsNewerThan: Duration.days(90),
});
Template.fromStack(stack).hasResourceProperties('AWS::Lambda::Function', {
Environment: {
Variables: Match.objectLike({
RETAIN_MILLISECONDS: '7776000000',
}),
},
});
});
Example #5
Source File: ecs-service-roller.test.ts From cloudstructs with Apache License 2.0 | 6 votes |
test('EcsServiceRoller with schedule', () => {
new EcsServiceRoller(stack, 'EcsServiceRoller', {
cluster,
service,
trigger: RollTrigger.fromSchedule(events.Schedule.rate(Duration.hours(5))),
});
expect(Template.fromStack(stack).toJSON()).toMatchSnapshot();
});
Example #6
Source File: codecommit-mirror.test.ts From cloudstructs with Apache License 2.0 | 6 votes |
test('CodeCommitMirror with a private GitHub repo', () => {
const urlSecret = secretsmanager.Secret.fromSecretNameV2(stack, 'Secret', 'clone-url');
new CodeCommitMirror(stack, 'Mirror', {
cluster,
repository: CodeCommitMirrorSourceRepository.private('private', ecs.Secret.fromSecretsManager(urlSecret)),
schedule: events.Schedule.rate(Duration.hours(6)),
});
expect(Template.fromStack(stack).toJSON()).toMatchSnapshot();
});
Example #7
Source File: index.ts From cloudstructs with Apache License 2.0 | 6 votes |
/**
* Best practice security headers used as default
*/
public static defaultSecurityHeadersBehavior: cloudfront.ResponseSecurityHeadersBehavior = {
contentTypeOptions: {
override: true,
},
frameOptions: {
frameOption: cloudfront.HeadersFrameOption.DENY,
override: true,
},
referrerPolicy: {
referrerPolicy: cloudfront.HeadersReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN,
override: true,
},
strictTransportSecurity: {
accessControlMaxAge: Duration.seconds(31536000),
includeSubdomains: true,
preload: true,
override: true,
},
xssProtection: {
protection: true,
modeBlock: true,
override: true,
},
};
Example #8
Source File: StaticWebsiteAbstract.ts From lift with MIT License | 6 votes |
private errorResponse(): ErrorResponse {
const errorPath = this.errorPath();
// Custom error page
if (errorPath !== undefined) {
return {
httpStatus: 404,
ttl: Duration.seconds(0),
responseHttpStatus: 404,
responsePagePath: errorPath,
};
}
/**
* The default behavior is optimized for SPA: all unknown URLs are served
* by index.html so that routing can be done client-side.
*/
return {
httpStatus: 404,
ttl: Duration.seconds(0),
responseHttpStatus: 200,
responsePagePath: "/index.html",
};
}
Example #9
Source File: Storage.ts From lift with MIT License | 6 votes |
constructor(scope: CdkConstruct, id: string, configuration: Configuration, private provider: AwsProvider) {
super(scope, id);
const resolvedConfiguration = Object.assign({}, STORAGE_DEFAULTS, configuration);
const encryptionOptions = {
s3: BucketEncryption.S3_MANAGED,
kms: BucketEncryption.KMS_MANAGED,
};
this.bucket = new Bucket(this, "Bucket", {
encryption: encryptionOptions[resolvedConfiguration.encryption],
versioned: true,
blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
enforceSSL: true,
lifecycleRules: [
{
transitions: [
{
storageClass: StorageClass.INTELLIGENT_TIERING,
transitionAfter: Duration.days(0),
},
],
},
{
noncurrentVersionExpiration: Duration.days(30),
},
],
});
this.bucketNameOutput = new CfnOutput(this, "BucketName", {
value: this.bucket.bucketName,
});
}
Example #10
Source File: 001_UserPool.ts From flect-chime-sdk-demo with Apache License 2.0 | 6 votes |
createUserPool = (scope: Construct, id: string) => {
const userPool = new cognito.UserPool(scope, `${id}_UserPool`, {
userPoolName: `${id}_UserPool`,
selfSignUpEnabled: true,
autoVerify: {
email: true,
},
passwordPolicy: {
minLength: 6,
requireSymbols: false,
},
signInAliases: {
email: true,
},
});
const userPoolClient = new cognito.UserPoolClient(scope, id + "_UserPool_Client", {
userPoolClientName: `${id}_UserPoolClient`,
userPool: userPool,
accessTokenValidity: Duration.minutes(1440),
idTokenValidity: Duration.minutes(1440),
refreshTokenValidity: Duration.days(30),
});
return { userPool, userPoolClient }
}
Example #11
Source File: ServerSideWebsite.ts From lift with MIT License | 6 votes |
private createErrorResponses(): ErrorResponse[] {
let responsePagePath = undefined;
if (this.configuration.errorPage !== undefined) {
responsePagePath = `/${this.getErrorPageFileName()}`;
}
return [
{
httpStatus: 500,
// Disable caching of error responses
ttl: Duration.seconds(0),
responsePagePath,
},
{
httpStatus: 504,
// Disable caching of error responses
ttl: Duration.seconds(0),
responsePagePath,
},
];
}
Example #12
Source File: 002_lambdas.ts From flect-chime-sdk-demo with Apache License 2.0 | 6 votes |
createLambdas = (scope: Construct) => {
// (1) Base Parameters
const baseParameters = {
code: lambda.Code.fromAsset(`${__dirname}/../dist`),
runtime: lambda.Runtime.NODEJS_14_X,
timeout: Duration.seconds(5),
memorySize: 256,
}
// (2) Function
//// (2-1) Auth
const lambdaFuncRestAPIAuth = new lambda.Function(scope, "ChimeRESTAPIAuth", {
...baseParameters,
handler: "rest_auth.authorize",
});
//// (2-2) Rest
const lambdaFunctionForRestAPI: lambda.Function = new lambda.Function(scope, "funcHelloWorld", {
...baseParameters,
handler: "index.handler",
});
//// (2-3) Slack Rest
const lambdaFunctionForSlackFederationRestAPI: lambda.Function = new lambda.Function(scope, "funcSlackFederation", {
...baseParameters,
handler: "slack.handler",
});
return { lambdaFuncRestAPIAuth, lambdaFunctionForRestAPI, lambdaFunctionForSlackFederationRestAPI }
}
Example #13
Source File: receiver.ts From cloudstructs with Apache License 2.0 | 5 votes |
constructor(scope: Construct, id: string, props: EmailReceiverProps) {
super(scope, id);
const receiptRule = new ses.ReceiptRule(this, 'ReceiptRule', {
ruleSet: props.receiptRuleSet,
recipients: props.recipients,
after: props.afterRule,
});
const bucket = new s3.Bucket(this, 'Bucket', {
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
encryption: s3.BucketEncryption.S3_MANAGED,
lifecycleRules: [{ expiration: Duration.days(1) }],
});
bucket.grantRead(props.function); // Download email
const topic = new sns.Topic(this, 'Topic');
// Actions
if (props.sourceWhitelist) {
const whitelistHandler = new WhitelistFunction(this, 'whitelist', {
environment: {
SOURCE_WHITELIST: props.sourceWhitelist,
},
logRetention: logs.RetentionDays.ONE_MONTH,
});
receiptRule.addAction(new actions.Lambda({
function: whitelistHandler,
invocationType: actions.LambdaInvocationType.REQUEST_RESPONSE,
}));
}
receiptRule.addAction(new actions.S3({
bucket,
topic,
}));
const s3Handler = new S3Function(this, 's3', {
logRetention: logs.RetentionDays.ONE_MONTH,
onSuccess: new destinations.LambdaDestination(props.function, {
responseOnly: true,
}),
});
topic.addSubscription(new subscriptions.LambdaSubscription(s3Handler)); // Notify
}
Example #14
Source File: index.ts From cdk-ssm-document with Apache License 2.0 | 5 votes |
private ensureLambda(): aws_lambda.Function {
const stack = Stack.of(this);
const constructName = 'SSM-Document-Manager-Lambda';
const existing = stack.node.tryFindChild(constructName);
if (existing) {
return existing as aws_lambda.Function;
}
const policy = new aws_iam.ManagedPolicy(
stack,
'SSM-Document-Manager-Policy',
{
managedPolicyName: `${stack.stackName}-${cleanID}`,
description: `Used by Lambda ${cleanID}, which is a custom CFN resource, managing SSM documents`,
statements: [
new aws_iam.PolicyStatement({
actions: ['ssm:ListDocuments', 'ssm:ListTagsForResource'],
resources: ['*'],
}),
new aws_iam.PolicyStatement({
actions: ['ssm:AddTagsToResource', 'ssm:CreateDocument'],
resources: ['*'],
conditions: {
StringLike: {
'aws:RequestTag/CreatedByCfnCustomResource': ID,
},
},
}),
new aws_iam.PolicyStatement({
actions: [
'ssm:AddTagsToResource',
'ssm:DeleteDocument',
'ssm:DescribeDocument',
'ssm:GetDocument',
'ssm:ListDocumentVersions',
'ssm:ModifyDocumentPermission',
'ssm:RemoveTagsFromResource',
'ssm:UpdateDocument',
'ssm:UpdateDocumentDefaultVersion',
],
resources: ['*'],
conditions: {
StringLike: {
'ssm:ResourceTag/CreatedByCfnCustomResource': ID,
},
},
}),
],
}
);
const role = new aws_iam.Role(stack, 'SSM-Document-Manager-Role', {
roleName: `${stack.stackName}-${cleanID}`,
description: `Used by Lambda ${cleanID}, which is a custom CFN resource, managing SSM documents`,
assumedBy: new aws_iam.ServicePrincipal('lambda.amazonaws.com'),
managedPolicies: [
policy,
aws_iam.ManagedPolicy.fromAwsManagedPolicyName(
'service-role/AWSLambdaBasicExecutionRole'
),
],
});
const fn = new aws_lambda.Function(stack, constructName, {
functionName: `${stack.stackName}-${cleanID}`,
role: role,
description: 'Custom CFN resource: Manage SSM Documents',
runtime: aws_lambda.Runtime.NODEJS_14_X,
handler: 'index.handler',
code: aws_lambda.Code.fromAsset(
path.join(__dirname, '../lambda/code.zip')
),
timeout: Duration.minutes(lambdaTimeout),
});
return fn;
}
Example #15
Source File: 001_FrontendBucket.ts From flect-chime-sdk-demo with Apache License 2.0 | 5 votes |
createFrontendS3 = (scope: Construct, id: string, USE_CDN: boolean) => {
const frontendBucket = new s3.Bucket(scope, "StaticSiteBucket", {
bucketName: `${id}-Bucket`.toLowerCase(),
removalPolicy: RemovalPolicy.DESTROY,
publicReadAccess: true,
});
let frontendCdn: cloudfront.CloudFrontWebDistribution | null = null;
if (USE_CDN) {
const oai = new cloudfront.OriginAccessIdentity(scope, "my-oai");
const myBucketPolicy = new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ["s3:GetObject"],
principals: [new iam.CanonicalUserPrincipal(oai.cloudFrontOriginAccessIdentityS3CanonicalUserId)],
resources: [frontendBucket.bucketArn + "/*"],
});
frontendBucket.addToResourcePolicy(myBucketPolicy);
// Create CloudFront WebDistribution
frontendCdn = new cloudfront.CloudFrontWebDistribution(scope, "WebsiteDistribution", {
viewerCertificate: {
aliases: [],
props: {
cloudFrontDefaultCertificate: true,
},
},
priceClass: cloudfront.PriceClass.PRICE_CLASS_ALL,
originConfigs: [
{
s3OriginSource: {
s3BucketSource: frontendBucket,
originAccessIdentity: oai,
},
behaviors: [
{
isDefaultBehavior: true,
minTtl: Duration.seconds(0),
maxTtl: Duration.days(365),
defaultTtl: Duration.days(1),
pathPattern: "my-contents/*",
},
],
},
],
errorConfigurations: [
{
errorCode: 403,
responsePagePath: "/index.html",
responseCode: 200,
errorCachingMinTtl: 0,
},
{
errorCode: 404,
responsePagePath: "/index.html",
responseCode: 200,
errorCachingMinTtl: 0,
},
],
});
}
return { frontendBucket, frontendCdn }
}
Example #16
Source File: aws-secure-bucket.ts From amazon-ec2-image-builder-samples with MIT No Attribution | 5 votes |
constructor(scope: Construct, id: string, props?: AWSSecureBucketProps) {
super(scope, id);
this.encryptionKey = props?.encryptionKeyArn
? Key.fromKeyArn(this, `encryption-key-${id}`, props?.encryptionKeyArn)
: new Key(this, `encryption-key-${id}`, {
removalPolicy: props?.removalPolicy,
enableKeyRotation: true,
});
this.bucket = new Bucket(this, `aws-${id}`, {
...props,
encryptionKey: this.encryptionKey,
encryption: BucketEncryption.KMS,
blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
enforceSSL: true,
versioned: true,
objectOwnership: s3.ObjectOwnership.BUCKET_OWNER_PREFERRED,
autoDeleteObjects: false,
serverAccessLogsPrefix: "imageBuilder",
lifecycleRules: [
{
abortIncompleteMultipartUploadAfter: Duration.days(300),
transitions: [
{
storageClass: StorageClass.INFREQUENT_ACCESS,
transitionAfter: Duration.days(30),
},
{
storageClass: StorageClass.INTELLIGENT_TIERING,
transitionAfter: Duration.days(60),
},
{
storageClass: StorageClass.GLACIER,
transitionAfter: Duration.days(180),
},
{
storageClass: StorageClass.DEEP_ARCHIVE,
transitionAfter: Duration.days(365),
},
],
},
],
});
if (props?.objectLockMode && props?.objectLockRetentionDays) {
// Add Object Lock configuration to the bucket
const bucket = this.bucket.node.defaultChild as CfnBucket;
bucket.objectLockEnabled = true as boolean;
bucket.objectLockConfiguration = {
objectLockEnabled: "Enabled",
rule: {
defaultRetention: {
days: props.objectLockRetentionDays,
mode: props.objectLockMode,
},
},
} as s3.CfnBucket.ObjectLockConfigurationProperty;
}
this.bucket.addToResourcePolicy(
new PolicyStatement({
sid: "HttpsOnly",
resources: [`${this.bucket.bucketArn}/*`],
actions: ["*"],
principals: [new AnyPrincipal()],
effect: Effect.DENY,
conditions: {
Bool: {
"aws:SecureTransport": "false",
},
},
})
);
}
Example #17
Source File: toolkit-cleaner.integ.ts From cloudstructs with Apache License 2.0 | 5 votes |
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
new ToolkitCleaner(this, 'ToolkitCleaner', {
dryRun: true,
retainAssetsNewerThan: Duration.days(90),
});
}
Example #18
Source File: s3cloudfront.ts From cdk-examples with MIT License | 5 votes |
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
///////////////////////////////
// Part 2
const bucket = new Bucket(this, 'websiteBucket', {
removalPolicy: RemovalPolicy.DESTROY,
bucketName: website_domain,
autoDeleteObjects: true
})
new CfnOutput(this, 'websiteBucketArn', {
value: bucket.bucketArn
})
///////////////////////////////
///////////////////////////////
// Part 3
const certificate = Certificate.fromCertificateArn(this, 'websiteCert', websiteCertArn)
const cachePolicy = new CachePolicy(this, 'examplePolicy', {
defaultTtl: Duration.hours(24),
minTtl: Duration.hours(24),
maxTtl: Duration.hours(24),
enableAcceptEncodingGzip: true,
enableAcceptEncodingBrotli: true
})
const distribution = new Distribution(this, 'exampleDistribution', {
defaultBehavior: {
origin: new S3Origin(bucket),
allowedMethods: AllowedMethods.ALLOW_GET_HEAD_OPTIONS,
cachePolicy,
compress: true,
viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS
},
domainNames: [website_domain],
certificate,
minimumProtocolVersion: SecurityPolicyProtocol.TLS_V1_2_2021,
defaultRootObject: 'index.html',
enableIpv6: true,
enabled: true,
httpVersion: HttpVersion.HTTP2,
priceClass: PriceClass.PRICE_CLASS_ALL
})
///////////////////////////////
///////////////////////////////
// Part 4
const hostedZone = HostedZone.fromHostedZoneAttributes(this, 'hostedZoneWithAttrs', {
hostedZoneId,
zoneName: website_domain
})
new ARecord(this, 'aliasForCloudfront', {
target: RecordTarget.fromAlias(new CloudFrontTarget(distribution)),
zone: hostedZone,
recordName: website_domain
})
///////////////////////////////
///////////////////////////////
// Part 5
new HttpsRedirect(this, 'wwwToNonWww', {
recordNames: ['www.example.com'],
targetDomain: website_domain,
zone:hostedZone
})
const repo = new Repository(this, 'reactSourceCode', {
repositoryName: 'example',
description: `react repo for ${website_domain}`
})
new CfnOutput(this, 'reactRepoArn', {
value: repo.repositoryArn
})
///////////////////////////////
}
Example #19
Source File: 003_lamdaForWebsocket.ts From flect-chime-sdk-demo with Apache License 2.0 | 5 votes |
createLambdaForWebsocket = (scope: Construct) => {
// (1) Base Parameters
const baseParameters = {
code: lambda.Code.fromAsset(`${__dirname}/../dist`),
runtime: lambda.Runtime.NODEJS_14_X,
timeout: Duration.seconds(30),
memorySize: 256,
}
// (2) Functions
// (2-1) connect
const lambdaFuncMessageConnect = new lambda.Function(scope, "ChimeMessageAPIConnect", {
...baseParameters,
handler: "message.connect",
});
// (2-2) disconnect
const lambdaFuncMessageDisconnect = new lambda.Function(scope, "ChimeMessageAPIDisconnect", {
...baseParameters,
handler: "message.disconnect",
});
// (2-3) message
const lambdaFuncMessageMessage = new lambda.Function(scope, "ChimeMessageAPIMessage", {
...baseParameters,
handler: "message.message",
});
// (2-4) auth
const lambdaFuncMessageAuth = new lambda.Function(scope, "ChimeMessageAPIAuth", {
...baseParameters,
handler: "message.authorize",
});
return {
lambdaFuncMessageConnect,
lambdaFuncMessageDisconnect,
lambdaFuncMessageMessage,
lambdaFuncMessageAuth
}
}
Example #20
Source File: dns-validated-domain-identity.ts From aws-cdk-ses-domain-identity with MIT License | 5 votes |
public constructor(scope: Construct, id: string, props: DnsValidatedDomainIdentityProps) {
super(scope, id);
const stack = Stack.of(this);
const region = props.region ?? stack.region;
const accountId = stack.account;
this.domainName = props.domainName;
this.dkim = props.dkim ?? false;
this.identityArn = `arn:aws:ses:${region}:${accountId}:identity/${this.domainName}`;
this.normalizedZoneName = props.hostedZone.zoneName;
// Remove trailing `.` from zone name
if (this.normalizedZoneName.endsWith(".")) {
this.normalizedZoneName = this.normalizedZoneName.substring(0, this.normalizedZoneName.length - 1);
}
// Remove any `/hostedzone/` prefix from the Hosted Zone ID
this.hostedZoneId = props.hostedZone.hostedZoneId.replace(/^\/hostedzone\//, "");
const requestorFunction = new lambda.Function(this, "DomainIdentityRequestorFunction", {
code: lambda.Code.fromAsset(path.resolve(__dirname, "..", "lambda-packages", "dns-validated-domain-identity-handler", "dist")),
handler: "index.identityRequestHandler",
runtime: lambda.Runtime.NODEJS_14_X,
memorySize: 128,
timeout: Duration.minutes(15),
role: props.customResourceRole,
});
requestorFunction.addToRolePolicy(new iam.PolicyStatement({
actions: [
"ses:GetIdentityVerificationAttributes",
"ses:GetIdentityDkimAttributes",
"ses:SetIdentityDkimEnabled",
"ses:VerifyDomainIdentity",
"ses:VerifyDomainDkim",
"ses:ListIdentities",
"ses:DeleteIdentity",
],
resources: ["*"],
}));
requestorFunction.addToRolePolicy(new iam.PolicyStatement({
actions: ["route53:GetChange"],
resources: ["*"],
}));
requestorFunction.addToRolePolicy(new iam.PolicyStatement({
actions: [
"route53:changeResourceRecordSets",
"route53:ListResourceRecordSets",
],
resources: [`arn:${Stack.of(requestorFunction).partition}:route53:::hostedzone/${this.hostedZoneId}`],
}));
const identity = new CustomResource(this, "IdentityRequestorResource", {
serviceToken: requestorFunction.functionArn,
properties: {
DomainName: this.domainName,
HostedZoneId: this.hostedZoneId,
Region: region,
DKIM: props.dkim,
},
});
this.node.addValidation({
validate: (): string[] => {
const errors: string[] = [];
// Ensure the zone name is a parent zone of the certificate domain name
if (!Token.isUnresolved(this.normalizedZoneName) &&
this.domainName !== this.normalizedZoneName &&
!this.domainName.endsWith("." + this.normalizedZoneName)) {
errors.push(`DNS zone ${this.normalizedZoneName} is not authoritative for SES identity domain name ${this.domainName}`);
}
return errors;
},
});
}
Example #21
Source File: cloudfront.ts From minwiz with BSD 2-Clause "Simplified" License | 5 votes |
constructor(scope: Construct, id: string, props: CloudfrontStackProps) {
super(scope, id, props);
this.websiteBucket = new Bucket(this, "websiteBucket", {
removalPolicy: RemovalPolicy.DESTROY,
bucketName: website_domain,
autoDeleteObjects: true,
});
new CfnOutput(this, "websiteBucketArn", {
value: this.websiteBucket.bucketArn,
});
const cachePolicy = new CachePolicy(this, "MinWizPolicy", {
defaultTtl: Duration.hours(24),
minTtl: Duration.hours(24),
maxTtl: Duration.hours(24),
enableAcceptEncodingGzip: true,
enableAcceptEncodingBrotli: true,
});
const distribution = new Distribution(this, "MinWizDistribution", {
defaultBehavior: {
origin: new S3Origin(this.websiteBucket),
allowedMethods: AllowedMethods.ALLOW_GET_HEAD_OPTIONS,
cachePolicy,
compress: true,
viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
},
domainNames: [website_domain /*, `www.${website_domain}`*/],
certificate: props.websiteCert,
minimumProtocolVersion: SecurityPolicyProtocol.TLS_V1_2_2019,
defaultRootObject: "index.html",
enableIpv6: true,
enabled: true,
httpVersion: HttpVersion.HTTP2,
priceClass: PriceClass.PRICE_CLASS_ALL,
});
new ARecord(this, "aliasForCloudfront", {
target: RecordTarget.fromAlias(new CloudFrontTarget(distribution)),
zone: props.hostedZone,
recordName: website_domain,
});
new HttpsRedirect(this, "wwwToNonWww", {
recordNames: [`www.${website_domain}`],
targetDomain: website_domain,
zone: props.hostedZone,
});
}
Example #22
Source File: index.ts From cloudstructs with Apache License 2.0 | 4 votes |
constructor(scope: Construct, id: string, props: UrlShortenerProps) {
super(scope, id);
const domainName = props.recordName ? `${props.recordName}.${props.hostedZone.zoneName}` : props.hostedZone.zoneName;
// Table to save a counter
const table = new dynamodb.Table(this, 'Table', {
partitionKey: {
name: 'key',
type: dynamodb.AttributeType.STRING,
},
removalPolicy: RemovalPolicy.DESTROY,
});
// Bucket to save redirects
const bucket = new s3.Bucket(this, 'Bucket', {
lifecycleRules: [{
expiration: props.expiration ?? Duration.days(365),
}],
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
bucketName: props.bucketName ?? `cloudstructs-url-shortener-${domainName}`,
});
// Redirect function
const redirectFunction = new RedirectFunction(this, 'Redirect');
bucket.grantRead(redirectFunction);
// CloudFront distribution
const certificate = new acm.DnsValidatedCertificate(this, 'Certificate', {
domainName,
hostedZone: props.hostedZone,
region: 'us-east-1',
});
const distribution = new cloudfront.Distribution(this, 'Distribution', {
defaultBehavior: {
origin: new origins.S3Origin(bucket),
edgeLambdas: [
{
eventType: cloudfront.LambdaEdgeEventType.ORIGIN_REQUEST,
functionVersion: redirectFunction,
},
],
},
certificate,
domainNames: [domainName],
});
// Route53 records
new route53.ARecord(this, 'ARecord', {
zone: props.hostedZone,
target: route53.RecordTarget.fromAlias(new targets.CloudFrontTarget(distribution)),
recordName: props.recordName,
});
new route53.AaaaRecord(this, 'AaaaRecord', {
zone: props.hostedZone,
target: route53.RecordTarget.fromAlias(new targets.CloudFrontTarget(distribution)),
recordName: props.recordName,
});
// Lambda function to increment counter and write redirect in bucket
const shortenerFunction = new ShortenerFunction(this, 'Shortener', {
logRetention: logs.RetentionDays.ONE_MONTH,
environment: {
DOMAIN_NAME: domainName,
BUCKET_NAME: bucket.bucketName,
TABLE_NAME: table.tableName,
},
});
if (props.corsAllowOrigins) {
shortenerFunction.addEnvironment('CORS_ALLOW_ORIGINS', props.corsAllowOrigins.join(' '));
}
bucket.grantPut(shortenerFunction);
table.grant(shortenerFunction, 'dynamodb:UpdateItem');
// API
this.api = new apigateway.RestApi(this, `UrlShortener${props.hostedZone.zoneName}`, {
endpointTypes: props.apiGatewayEndpoint ? [apigateway.EndpointType.PRIVATE] : undefined,
policy: props.apiGatewayEndpoint
? new iam.PolicyDocument({
statements: [
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ['execute-api:Invoke'],
principals: [new iam.AnyPrincipal()],
resources: [Fn.join('', ['execute-api:/', '*'])],
conditions: {
StringEquals: { 'aws:SourceVpce': props.apiGatewayEndpoint.vpcEndpointId },
},
}),
],
})
: undefined,
defaultCorsPreflightOptions: props.corsAllowOrigins
? { allowOrigins: props.corsAllowOrigins }
: undefined,
});
this.api.root.addMethod('ANY', new apigateway.LambdaIntegration(shortenerFunction), {
authorizer: props.apiGatewayAuthorizer,
});
this.api.root
.addResource('{proxy+}')
.addMethod('ANY', new apigateway.LambdaIntegration(shortenerFunction), {
authorizer: props.apiGatewayAuthorizer,
});
this.apiEndpoint = this.api.url;
}
Example #23
Source File: index.ts From cdk-ec2-key-pair with Apache License 2.0 | 4 votes |
private ensureLambda(): aws_lambda.Function {
const stack = Stack.of(this);
const constructName = 'EC2-Key-Name-Manager-Lambda';
const existing = stack.node.tryFindChild(constructName);
if (existing) {
return existing as aws_lambda.Function;
}
const resources = [`arn:${stack.partition}:ec2:*:*:key-pair/*`];
const policy = new aws_iam.ManagedPolicy(
stack,
'EC2-Key-Pair-Manager-Policy',
{
managedPolicyName: `${this.prefix}-${cleanID}`,
description: `Used by Lambda ${cleanID}, which is a custom CFN resource, managing EC2 Key Pairs`,
statements: [
new aws_iam.PolicyStatement({
actions: ['ec2:DescribeKeyPairs'],
resources: ['*'],
}),
new aws_iam.PolicyStatement({
actions: [
'ec2:CreateKeyPair',
'ec2:CreateTags',
'ec2:ImportKeyPair',
],
conditions: {
StringLike: {
'aws:RequestTag/CreatedByCfnCustomResource': ID,
},
},
resources,
}),
new aws_iam.PolicyStatement({
// allow delete/update, only if createdByTag is set
actions: ['ec2:CreateTags', 'ec2:DeleteKeyPair', 'ec2:DeleteTags'],
conditions: {
StringLike: {
'ec2:ResourceTag/CreatedByCfnCustomResource': ID,
},
},
resources,
}),
new aws_iam.PolicyStatement({
// we need this to check if a secret exists before attempting to delete it
actions: ['secretsmanager:ListSecrets'],
resources: ['*'],
}),
new aws_iam.PolicyStatement({
actions: [
'secretsmanager:CreateSecret',
'secretsmanager:TagResource',
],
conditions: {
StringLike: {
'aws:RequestTag/CreatedByCfnCustomResource': ID,
},
},
resources: ['*'],
}),
new aws_iam.PolicyStatement({
// allow delete/update, only if createdByTag is set
actions: [
'secretsmanager:DeleteResourcePolicy',
'secretsmanager:DeleteSecret',
'secretsmanager:DescribeSecret',
'secretsmanager:GetResourcePolicy',
'secretsmanager:GetSecretValue',
'secretsmanager:ListSecretVersionIds',
'secretsmanager:PutResourcePolicy',
'secretsmanager:PutSecretValue',
'secretsmanager:RestoreSecret',
'secretsmanager:UntagResource',
'secretsmanager:UpdateSecret',
'secretsmanager:UpdateSecretVersionStage',
],
conditions: {
StringLike: {
'secretsmanager:ResourceTag/CreatedByCfnCustomResource': ID,
},
},
resources: ['*'],
}),
],
}
);
const role = new aws_iam.Role(stack, 'EC2-Key-Pair-Manager-Role', {
roleName: `${this.prefix}-${cleanID}`,
description: `Used by Lambda ${cleanID}, which is a custom CFN resource, managing EC2 Key Pairs`,
assumedBy: new aws_iam.ServicePrincipal('lambda.amazonaws.com'),
managedPolicies: [
policy,
aws_iam.ManagedPolicy.fromAwsManagedPolicyName(
'service-role/AWSLambdaBasicExecutionRole'
),
],
});
const fn = new aws_lambda.Function(stack, constructName, {
functionName: `${this.prefix}-${cleanID}`,
role: role,
description: 'Custom CFN resource: Manage EC2 Key Pairs',
runtime: aws_lambda.Runtime.NODEJS_14_X,
handler: 'index.handler',
code: aws_lambda.Code.fromAsset(
path.join(__dirname, '../lambda/code.zip')
),
timeout: Duration.minutes(lambdaTimeout),
});
return fn;
}
Example #24
Source File: gitlab-runner-instance.ts From cdk-gitlab-runner with Apache License 2.0 | 4 votes |
constructor(scope: Construct, id: string, props: GitlabContainerRunnerProps) {
super(scope, id);
const spotFleetId = id;
const defaultProps = {
gitlabRunnerImage: 'public.ecr.aws/gitlab/gitlab-runner:alpine',
gitlaburl: 'https://gitlab.com/',
ec2type: 't3.micro',
tags: ['gitlab', 'awscdk', 'runner'],
};
const runnerProps = { ...defaultProps, ...props };
const runnerBucket = new Bucket(this, 'runnerBucket', {
removalPolicy: RemovalPolicy.DESTROY,
autoDeleteObjects: true,
});
const shell = UserData.forLinux();
shell.addCommands(...this.createUserData(runnerProps, runnerBucket.bucketName));
this.runnerRole =
runnerProps.ec2iamrole ??
new Role(this, 'runner-role', {
assumedBy: new ServicePrincipal('ec2.amazonaws.com'),
description: 'For Gitlab EC2 Runner Role',
});
this.validUntil = runnerProps.validUntil;
const instanceProfile = new CfnInstanceProfile(this, 'InstanceProfile', {
roles: [this.runnerRole.roleName],
});
runnerBucket.grantWrite(this.runnerRole);
this.vpc =
runnerProps.selfvpc ??
new Vpc(this, 'VPC', {
cidr: '10.0.0.0/16',
maxAzs: 2,
subnetConfiguration: [
{
cidrMask: 26,
name: 'RunnerVPC',
subnetType: SubnetType.PUBLIC,
},
],
natGateways: 0,
});
this.defaultRunnerSG = new SecurityGroup(this, 'SpotFleetSg', {
vpc: this.vpc,
});
this.defaultRunnerSG.connections.allowFromAnyIpv4(Port.tcp(22));
const spotOrOnDemand = runnerProps.spotFleet ?? false;
if (spotOrOnDemand) {
//throw new Error('yes new spotfleet');
const imageId = MachineImage.latestAmazonLinux({
generation: AmazonLinuxGeneration.AMAZON_LINUX_2,
}).getImage(this).imageId;
const lt = new CfnLaunchTemplate(this, 'LaunchTemplate', {
launchTemplateData: {
imageId,
instanceType: runnerProps.ec2type,
blockDeviceMappings: [
{
deviceName: '/dev/xvda',
ebs: {
volumeSize: runnerProps.ebsSize ?? 60,
},
},
],
userData: Fn.base64(shell.render()),
keyName: runnerProps.keyName,
tagSpecifications: [
{
resourceType: 'instance',
tags: [
{
key: 'Name',
value: `${Stack.of(this).stackName
}/spotFleetGitlabRunner/${spotFleetId}`,
},
],
},
],
instanceMarketOptions: {
marketType: 'spot',
spotOptions: {
blockDurationMinutes:
runnerProps.blockDuration ?? BlockDuration.ONE_HOUR,
instanceInterruptionBehavior:
runnerProps.instanceInterruptionBehavior ??
InstanceInterruptionBehavior.TERMINATE,
},
},
securityGroupIds: this.defaultRunnerSG.connections.securityGroups.map(
(m) => m.securityGroupId,
),
iamInstanceProfile: {
arn: instanceProfile.attrArn,
},
},
});
const spotFleetRole = new Role(this, 'FleetRole', {
assumedBy: new ServicePrincipal('spotfleet.amazonaws.com'),
managedPolicies: [
ManagedPolicy.fromAwsManagedPolicyName(
'service-role/AmazonEC2SpotFleetTaggingRole',
),
],
});
const vpcSubnetSelection = runnerProps.vpcSubnet ?? {
subnetType: SubnetType.PUBLIC,
};
const subnetConfig = this.vpc
.selectSubnets(vpcSubnetSelection)
.subnets.map((s) => ({
subnetId: s.subnetId,
}));
const cfnSpotFleet = new CfnSpotFleet(this, id, {
spotFleetRequestConfigData: {
launchTemplateConfigs: [
{
launchTemplateSpecification: {
launchTemplateId: lt.ref,
version: lt.attrLatestVersionNumber,
},
overrides: subnetConfig,
},
],
iamFleetRole: spotFleetRole.roleArn,
targetCapacity: 1,
validUntil: Lazy.string({ produce: () => this.validUntil }),
terminateInstancesWithExpiration: true,
},
});
const onEvent = new lambda.Function(this, 'OnEvent', {
code: lambda.Code.fromAsset(path.join(__dirname, '../assets/functions')),
handler: 'index.on_event',
runtime: lambda.Runtime.PYTHON_3_8,
timeout: Duration.seconds(60),
});
const isComplete = new lambda.Function(this, 'IsComplete', {
code: lambda.Code.fromAsset(path.join(__dirname, '../assets/functions')),
handler: 'index.is_complete',
runtime: lambda.Runtime.PYTHON_3_8,
timeout: Duration.seconds(60),
role: onEvent.role,
});
const myProvider = new cr.Provider(this, 'MyProvider', {
onEventHandler: onEvent,
isCompleteHandler: isComplete,
logRetention: logs.RetentionDays.ONE_DAY,
});
onEvent.addToRolePolicy(
new PolicyStatement({
actions: ['ec2:DescribeSpotFleetInstances'],
resources: ['*'],
}),
);
const fleetInstances = new CustomResource(this, 'GetInstanceId', {
serviceToken: myProvider.serviceToken,
properties: {
SpotFleetRequestId: cfnSpotFleet.ref,
},
});
fleetInstances.node.addDependency(cfnSpotFleet);
this.spotFleetInstanceId = Token.asString(
fleetInstances.getAtt('InstanceId'),
);
this.spotFleetRequestId = Token.asString(
fleetInstances.getAtt('SpotInstanceRequestId'),
);
new CfnOutput(this, 'InstanceId', { value: this.spotFleetInstanceId });
new CfnOutput(this, 'SpotFleetId', { value: cfnSpotFleet.ref });
} else {
this.runnerEc2 = new Instance(this, 'GitlabRunner', {
instanceType: new InstanceType(runnerProps.ec2type),
instanceName: 'Gitlab-Runner',
vpc: this.vpc,
vpcSubnets: runnerProps.vpcSubnet ?? {
subnetType: SubnetType.PUBLIC,
},
machineImage: MachineImage.latestAmazonLinux({
generation: AmazonLinuxGeneration.AMAZON_LINUX_2,
}),
role: this.runnerRole,
userData: shell,
securityGroup: this.defaultRunnerSG,
blockDevices: [
{
deviceName: '/dev/xvda',
volume: BlockDeviceVolume.ebs(runnerProps.ebsSize ?? 60),
},
],
});
new CfnOutput(this, 'Runner-Instance-ID', {
value: this.runnerEc2.instanceId,
});
}
const unregisterRunnerOnEvent = new lambda.Function(this, 'unregisterRunnerOnEvent', {
code: lambda.Code.fromAsset(path.join(__dirname, '../assets/functions')),
handler: 'unregister_runner.on_event',
runtime: lambda.Runtime.PYTHON_3_8,
timeout: Duration.seconds(60),
});
const unregisterRunnerProvider = new cr.Provider(this, 'unregisterRunnerProvider', {
onEventHandler: unregisterRunnerOnEvent,
logRetention: logs.RetentionDays.ONE_DAY,
});
const unregisterRunnerCR = new CustomResource(this, 'unregisterRunnerCR', {
resourceType: 'Custom::unregisterRunnerProvider',
serviceToken: unregisterRunnerProvider.serviceToken,
properties: {
BucketName: runnerBucket.bucketName,
GitlabUrl: runnerProps.gitlaburl,
},
});
runnerBucket.grantReadWrite(unregisterRunnerOnEvent);
unregisterRunnerCR.node.addDependency(runnerBucket);
this.runnerRole.addManagedPolicy(
ManagedPolicy.fromAwsManagedPolicyName('AmazonSSMManagedInstanceCore'),
);
new CfnOutput(this, 'Runner-Role-Arn', {
value: this.runnerRole.roleArn,
});
}
Example #25
Source File: s3cloudfront.ts From cdk-examples with MIT License | 4 votes |
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
///////////////////////////////
// Part 2
const bucket = new Bucket(this, 'websiteBucket', {
removalPolicy: RemovalPolicy.DESTROY,
bucketName: website_domain,
autoDeleteObjects: true,
websiteIndexDocument: 'index.html',
websiteErrorDocument: '404.html'
})
bucket.addToResourcePolicy(new PolicyStatement({
effect: Effect.ALLOW,
principals: [new AnyPrincipal()],
actions: ['s3:GetObject'],
resources: ['arn:aws:s3:::example.com/*'],
conditions: {
"StringLike":{"aws:Referer":[bucketRefererSecret]}
}
}))
new CfnOutput(this, 'websiteBucketArn', {
value: bucket.bucketArn
})
new CfnOutput(this, 'websiteBucketUrl', {
value: bucket.bucketWebsiteUrl
})
///////////////////////////////
///////////////////////////////
// Part 3
const certificate = Certificate.fromCertificateArn(this, 'websiteCert', websiteCertArn)
const cachePolicy = new CachePolicy(this, 'examplePolicy', {
defaultTtl: Duration.hours(24),
minTtl: Duration.hours(24),
maxTtl: Duration.hours(24),
enableAcceptEncodingBrotli: true,
enableAcceptEncodingGzip: true
})
const distribution = new Distribution(this, 'exampleDistribution', {
defaultBehavior: {
origin: new HttpOrigin(bucketWebsiteUrl, {
protocolPolicy: OriginProtocolPolicy.HTTP_ONLY,
customHeaders: {
'Referer': bucketRefererSecret
}
}),
allowedMethods: AllowedMethods.ALLOW_GET_HEAD_OPTIONS,
cachePolicy,
compress: true,
viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS
},
domainNames: [website_domain],
certificate,
minimumProtocolVersion: SecurityPolicyProtocol.TLS_V1_2_2021,
enableIpv6: true,
enabled: true,
httpVersion: HttpVersion.HTTP2,
priceClass: PriceClass.PRICE_CLASS_ALL
})
///////////////////////////////
///////////////////////////////
// Part 4
const hostedZone = HostedZone.fromHostedZoneAttributes(this, 'hostedZoneWithAttrs', {
hostedZoneId,
zoneName: website_domain
})
new ARecord(this, 'aliasForCloudfront', {
target: RecordTarget.fromAlias(new CloudFrontTarget(distribution)),
zone: hostedZone,
recordName: website_domain
})
///////////////////////////////
///////////////////////////////
// Part 5
new HttpsRedirect(this, 'wwwToNonWww', {
recordNames: ['www.example.com'],
targetDomain: website_domain,
zone:hostedZone
})
const repo = new Repository(this, 'hugoSourceCode', {
repositoryName: 'example',
description: `hugo repo for ${website_domain}`
})
new CfnOutput(this, 'hugoRepoArn', {
value: repo.repositoryArn
})
///////////////////////////////
}
Example #26
Source File: index.ts From cloudstructs with Apache License 2.0 | 4 votes |
constructor(scope: Construct, id: string, props: ToolkitCleanerProps = {}) {
super(scope, id);
// Dummy assets to reference S3 bucket and ECR repository
const fileAsset = new Asset(this, 'FileAsset', {
path: path.join(__dirname, '..', '..', 'assets', 'toolkit-cleaner', 'docker', 'dummy.txt'),
});
const dockerImageAsset = new DockerImageAsset(this, 'DockerImageAsset', {
directory: path.join(__dirname, '..', '..', 'assets', 'toolkit-cleaner', 'docker'),
});
const getStackNamesFunction = new GetStackNamesFunction(this, 'GetStackNamesFunction');
getStackNamesFunction.addToRolePolicy(new PolicyStatement({
actions: ['cloudformation:DescribeStacks'],
resources: ['*'],
}));
const getStackNames = new tasks.LambdaInvoke(this, 'GetStackNames', {
lambdaFunction: getStackNamesFunction,
payloadResponseOnly: true,
});
const stacksMap = new sfn.Map(this, 'StacksMap', {
maxConcurrency: 1, // Avoid "Rate exceeded" error from CloudFormation
resultSelector: {
AssetHashes: sfn.JsonPath.stringAt('$'),
},
});
const extractTemplateHashesFunction = new ExtractTemplateHashesFunction(this, 'ExtractTemplateHashesFunction', {
timeout: Duration.seconds(30),
});
extractTemplateHashesFunction.addToRolePolicy(new PolicyStatement({
actions: ['cloudformation:GetTemplate'],
resources: ['*'],
}));
const extractTemplateHashes = new tasks.LambdaInvoke(this, 'ExtractTemplateHashes', {
lambdaFunction: extractTemplateHashesFunction,
payloadResponseOnly: true,
}).addRetry({
errors: ['Throttling'], // Avoid "Rate exceeded" error from CloudFormation
});
const flattenHashes = new tasks.EvaluateExpression(this, 'FlattenHashes', {
expression: '[...new Set(($.AssetHashes).flat())]',
});
const cleanObjectsFunction = new CleanObjectsFunction(this, 'CleanObjectsFunction', {
timeout: Duration.minutes(5),
});
cleanObjectsFunction.addEnvironment('BUCKET_NAME', fileAsset.bucket.bucketName);
fileAsset.bucket.grantRead(cleanObjectsFunction);
fileAsset.bucket.grantDelete(cleanObjectsFunction);
const cleanObjects = new tasks.LambdaInvoke(this, 'CleanObjects', {
lambdaFunction: cleanObjectsFunction,
payloadResponseOnly: true,
});
const cleanImagesFunction = new CleanImagesFunction(this, 'CleanImagesFunction', {
timeout: Duration.minutes(5),
});
cleanImagesFunction.addEnvironment('REPOSITORY_NAME', dockerImageAsset.repository.repositoryName);
dockerImageAsset.repository.grant(cleanImagesFunction, 'ecr:DescribeImages', 'ecr:BatchDeleteImage');
const cleanImages = new tasks.LambdaInvoke(this, 'CleanImages', {
lambdaFunction: cleanImagesFunction,
payloadResponseOnly: true,
});
if (!props.dryRun) {
cleanObjectsFunction.addEnvironment('RUN', 'true');
cleanImagesFunction.addEnvironment('RUN', 'true');
}
if (props.retainAssetsNewerThan) {
const retainMilliseconds = props.retainAssetsNewerThan.toMilliseconds().toString();
cleanObjectsFunction.addEnvironment('RETAIN_MILLISECONDS', retainMilliseconds);
cleanImagesFunction.addEnvironment('RETAIN_MILLISECONDS', retainMilliseconds);
}
const sumReclaimed = new tasks.EvaluateExpression(this, 'SumReclaimed', {
expression: '({ Deleted: $[0].Deleted + $[1].Deleted, Reclaimed: $[0].Reclaimed + $[1].Reclaimed })',
});
const stateMachine = new sfn.StateMachine(this, 'Resource', {
definition: getStackNames
.next(stacksMap.iterator(extractTemplateHashes))
.next(flattenHashes)
.next(new sfn.Parallel(this, 'Clean')
.branch(cleanObjects)
.branch(cleanImages))
.next(sumReclaimed),
});
const rule = new Rule(this, 'Rule', {
enabled: props.scheduleEnabled ?? true,
schedule: props.schedule ?? Schedule.rate(Duration.days(1)),
});
rule.addTarget(new SfnStateMachine(stateMachine));
}
Example #27
Source File: index.ts From cloudstructs with Apache License 2.0 | 4 votes |
constructor(scope: Construct, id: string, props: StateMachineCustomResourceProviderProps) {
super(scope, id);
const cfnResponseSuccessFn = this.createCfnResponseFn('Success');
const cfnResponseFailedFn = this.createCfnResponseFn('Failed');
const role = new iam.Role(this, 'Role', {
assumedBy: new iam.ServicePrincipal('states.amazonaws.com'),
});
role.addToPolicy(new iam.PolicyStatement({
actions: ['lambda:InvokeFunction'],
resources: [cfnResponseSuccessFn.functionArn, cfnResponseFailedFn.functionArn],
}));
// https://docs.aws.amazon.com/step-functions/latest/dg/stepfunctions-iam.html
// https://docs.aws.amazon.com/step-functions/latest/dg/concept-create-iam-advanced.html#concept-create-iam-advanced-execution
role.addToPolicy(new iam.PolicyStatement({
actions: ['states:StartExecution'],
resources: [props.stateMachine.stateMachineArn],
}));
role.addToPolicy(new iam.PolicyStatement({
actions: ['states:DescribeExecution', 'states:StopExecution'],
resources: [Stack.of(this).formatArn({
service: 'states',
resource: 'execution',
arnFormat: ArnFormat.COLON_RESOURCE_NAME,
resourceName: `${Stack.of(this).splitArn(props.stateMachine.stateMachineArn, ArnFormat.COLON_RESOURCE_NAME).resourceName}*`,
})],
}));
role.addToPolicy(new iam.PolicyStatement({
actions: ['events:PutTargets', 'events:PutRule', 'events:DescribeRule'],
resources: [Stack.of(this).formatArn({
service: 'events',
resource: 'rule',
resourceName: 'StepFunctionsGetEventsForStepFunctionsExecutionRule',
})],
}));
const definition = Stack.of(this).toJsonString({
StartAt: 'StartExecution',
States: {
StartExecution: {
Type: 'Task',
Resource: 'arn:aws:states:::states:startExecution.sync:2', // with sync:2 the Output is JSON parsed
Parameters: {
'Input.$': '$',
'StateMachineArn': props.stateMachine.stateMachineArn,
},
TimeoutSeconds: (props.timeout ?? Duration.minutes(30)).toSeconds(),
Next: 'CfnResponseSuccess',
Catch: [{
ErrorEquals: ['States.ALL'],
Next: 'CfnResponseFailed',
}],
},
CfnResponseSuccess: {
Type: 'Task',
Resource: cfnResponseSuccessFn.functionArn,
End: true,
},
CfnResponseFailed: {
Type: 'Task',
Resource: cfnResponseFailedFn.functionArn,
End: true,
},
},
});
const stateMachine = new CfnResource(this, 'StateMachine', {
type: 'AWS::StepFunctions::StateMachine',
properties: {
DefinitionString: definition,
RoleArn: role.roleArn,
},
});
stateMachine.node.addDependency(role);
const startExecution = new lambda.Function(this, 'StartExecution', {
code: lambda.Code.fromAsset(path.join(__dirname, 'runtime')),
handler: 'index.startExecution',
runtime: lambda.Runtime.NODEJS_14_X,
});
startExecution.addToRolePolicy(new iam.PolicyStatement({
actions: ['states:StartExecution'],
resources: [stateMachine.ref],
}));
startExecution.addEnvironment('STATE_MACHINE_ARN', stateMachine.ref);
this.serviceToken = startExecution.functionArn;
}
Example #28
Source File: ServerSideWebsite.ts From lift with MIT License | 4 votes |
constructor(
scope: Construct,
private readonly id: string,
readonly configuration: Configuration,
private readonly provider: AwsProvider
) {
super(scope, id);
if (configuration.domain !== undefined && configuration.certificate === undefined) {
throw new ServerlessError(
`Invalid configuration in 'constructs.${id}.certificate': if a domain is configured, then a certificate ARN must be configured as well.`,
"LIFT_INVALID_CONSTRUCT_CONFIGURATION"
);
}
if (configuration.errorPage !== undefined && !configuration.errorPage.endsWith(".html")) {
throw new ServerlessError(
`Invalid configuration in 'constructs.${id}.errorPage': the custom error page must be a static HTML file. '${configuration.errorPage}' does not end with '.html'.`,
"LIFT_INVALID_CONSTRUCT_CONFIGURATION"
);
}
const bucket = new Bucket(this, "Assets", {
// Assets are compiled artifacts, we can clear them on serverless remove
removalPolicy: RemovalPolicy.DESTROY,
});
/**
* We create custom "Origin Policy" and "Cache Policy" for the backend.
* "All URL query strings, HTTP headers, and cookies that you include in the cache key (using a cache policy) are automatically included in origin requests. Use the origin request policy to specify the information that you want to include in origin requests, but not include in the cache key."
* https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/controlling-origin-requests.html
*/
const backendOriginPolicy = new OriginRequestPolicy(this, "BackendOriginPolicy", {
originRequestPolicyName: `${this.provider.stackName}-${id}`,
comment: `Origin request policy for the ${id} website.`,
cookieBehavior: OriginRequestCookieBehavior.all(),
queryStringBehavior: OriginRequestQueryStringBehavior.all(),
headerBehavior: this.headersToForward(),
});
const backendCachePolicy = new CachePolicy(this, "BackendCachePolicy", {
cachePolicyName: `${this.provider.stackName}-${id}`,
comment: `Cache policy for the ${id} website.`,
// For the backend we disable all caching by default
defaultTtl: Duration.seconds(0),
// Authorization is an exception and must be whitelisted in the Cache Policy
// This is the reason why we don't use the managed `CachePolicy.CACHING_DISABLED`
headerBehavior: CacheHeaderBehavior.allowList("Authorization"),
});
const apiId =
configuration.apiGateway === "rest"
? this.provider.naming.getRestApiLogicalId()
: this.provider.naming.getHttpApiLogicalId();
const apiGatewayDomain = Fn.join(".", [Fn.ref(apiId), `execute-api.${this.provider.region}.amazonaws.com`]);
// Cast the domains to an array
this.domains = configuration.domain !== undefined ? flatten([configuration.domain]) : undefined;
const certificate =
configuration.certificate !== undefined
? acm.Certificate.fromCertificateArn(this, "Certificate", configuration.certificate)
: undefined;
this.distribution = new Distribution(this, "CDN", {
comment: `${provider.stackName} ${id} website CDN`,
defaultBehavior: {
// Origins are where CloudFront fetches content
origin: new HttpOrigin(apiGatewayDomain, {
// API Gateway only supports HTTPS
protocolPolicy: OriginProtocolPolicy.HTTPS_ONLY,
}),
// For a backend app we all all methods
allowedMethods: AllowedMethods.ALLOW_ALL,
cachePolicy: backendCachePolicy,
viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
// Forward all values (query strings, headers, and cookies) to the backend app
// See https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-managed-origin-request-policies.html#managed-origin-request-policies-list
originRequestPolicy: backendOriginPolicy,
functionAssociations: [
{
function: this.createRequestFunction(),
eventType: FunctionEventType.VIEWER_REQUEST,
},
],
},
// All the assets paths are created in there
additionalBehaviors: this.createCacheBehaviors(bucket),
errorResponses: this.createErrorResponses(),
// Enable http2 transfer for better performances
httpVersion: HttpVersion.HTTP2,
certificate: certificate,
domainNames: this.domains,
});
// CloudFormation outputs
this.bucketNameOutput = new CfnOutput(this, "AssetsBucketName", {
description: "Name of the bucket that stores the website assets.",
value: bucket.bucketName,
});
let websiteDomain = this.getMainCustomDomain();
if (websiteDomain === undefined) {
// Fallback on the CloudFront domain
websiteDomain = this.distribution.distributionDomainName;
}
this.domainOutput = new CfnOutput(this, "Domain", {
description: "Website domain name.",
value: websiteDomain,
});
this.cnameOutput = new CfnOutput(this, "CloudFrontCName", {
description: "CloudFront CNAME.",
value: this.distribution.distributionDomainName,
});
this.distributionIdOutput = new CfnOutput(this, "DistributionId", {
description: "ID of the CloudFront distribution.",
value: this.distribution.distributionId,
});
}
Example #29
Source File: Queue.ts From lift with MIT License | 4 votes |
constructor(
scope: CdkConstruct,
private readonly id: string,
private readonly configuration: Configuration,
private readonly provider: AwsProvider
) {
super(scope, id);
// This should be covered by the schema validation, but until it is enforced by default
// this is a very common error for users
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (configuration.worker === undefined) {
throw new ServerlessError(
`Invalid configuration in 'constructs.${this.id}': no 'worker' defined. Queue constructs require a 'worker' function to be defined.`,
"LIFT_INVALID_CONSTRUCT_CONFIGURATION"
);
}
// The default function timeout is 6 seconds in the Serverless Framework
const functionTimeout = configuration.worker.timeout ?? 6;
// This should be 6 times the lambda function's timeout + MaximumBatchingWindowInSeconds
// See https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html
const visibilityTimeout = functionTimeout * 6 + this.getMaximumBatchingWindow();
const maxRetries = configuration.maxRetries ?? 3;
let delay = undefined;
if (configuration.delay !== undefined) {
if (configuration.delay < 0 || configuration.delay > 900) {
throw new ServerlessError(
`Invalid configuration in 'constructs.${this.id}': 'delay' must be between 0 and 900, '${configuration.delay}' given.`,
"LIFT_INVALID_CONSTRUCT_CONFIGURATION"
);
}
delay = Duration.seconds(configuration.delay);
}
let encryption = undefined;
if (isNil(configuration.encryption) || configuration.encryption.length === 0) {
encryption = {};
} else if (configuration.encryption === "kmsManaged") {
encryption = { encryption: QueueEncryption.KMS_MANAGED };
} else if (configuration.encryption === "kms") {
if (isNil(configuration.encryptionKey) || configuration.encryptionKey.length === 0) {
throw new ServerlessError(
`Invalid configuration in 'constructs.${this.id}': 'encryptionKey' must be set if the 'encryption' is set to 'kms'`,
"LIFT_INVALID_CONSTRUCT_CONFIGURATION"
);
}
encryption = {
encryption: QueueEncryption.KMS,
encryptionMasterKey: new Key(this, configuration.encryptionKey),
};
} else {
throw new ServerlessError(
`Invalid configuration in 'constructs.${this.id}': 'encryption' must be one of 'kms', 'kmsManaged', null, '${configuration.encryption}' given.`,
"LIFT_INVALID_CONSTRUCT_CONFIGURATION"
);
}
const baseName = `${this.provider.stackName}-${id}`;
this.dlq = new CdkQueue(this, "Dlq", {
queueName: configuration.fifo === true ? `${baseName}-dlq.fifo` : `${baseName}-dlq`,
// 14 days is the maximum, we want to keep these messages for as long as possible
retentionPeriod: Duration.days(14),
fifo: configuration.fifo,
...encryption,
});
this.queue = new CdkQueue(this, "Queue", {
queueName: configuration.fifo === true ? `${baseName}.fifo` : `${baseName}`,
visibilityTimeout: Duration.seconds(visibilityTimeout),
deadLetterQueue: {
maxReceiveCount: maxRetries,
queue: this.dlq,
},
fifo: configuration.fifo,
deliveryDelay: delay,
contentBasedDeduplication: configuration.fifo,
...encryption,
});
const alarmEmail = configuration.alarm;
if (alarmEmail !== undefined) {
const alarmTopic = new Topic(this, "AlarmTopic", {
topicName: `${this.provider.stackName}-${id}-dlq-alarm-topic`,
displayName: `[Alert][${id}] There are failed jobs in the dead letter queue.`,
});
new Subscription(this, "AlarmTopicSubscription", {
topic: alarmTopic,
protocol: SubscriptionProtocol.EMAIL,
endpoint: alarmEmail,
});
const alarm = new Alarm(this, "Alarm", {
alarmName: `${this.provider.stackName}-${id}-dlq-alarm`,
alarmDescription: "Alert triggered when there are failed jobs in the dead letter queue.",
metric: new Metric({
namespace: "AWS/SQS",
metricName: "ApproximateNumberOfMessagesVisible",
dimensionsMap: {
QueueName: this.dlq.queueName,
},
statistic: "Sum",
period: Duration.minutes(1),
}),
evaluationPeriods: 1,
// Alert as soon as we have 1 message in the DLQ
threshold: 0,
comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD,
});
alarm.addAlarmAction({
bind(): AlarmActionConfig {
return { alarmActionArn: alarmTopic.topicArn };
},
});
}
// CloudFormation outputs
this.queueArnOutput = new CfnOutput(this, "QueueArn", {
description: `ARN of the "${id}" SQS queue.`,
value: this.queue.queueArn,
});
this.queueUrlOutput = new CfnOutput(this, "QueueUrl", {
description: `URL of the "${id}" SQS queue.`,
value: this.queue.queueUrl,
});
this.dlqUrlOutput = new CfnOutput(this, "DlqUrl", {
description: `URL of the "${id}" SQS dead letter queue.`,
value: this.dlq.queueUrl,
});
this.appendFunctions();
}