diff --git a/usecases/mwaa-public-webserver-custom-domain/package.json b/usecases/mwaa-public-webserver-custom-domain/package.json index 5623766..aac2369 100644 --- a/usecases/mwaa-public-webserver-custom-domain/package.json +++ b/usecases/mwaa-public-webserver-custom-domain/package.json @@ -20,7 +20,7 @@ "@types/aws-lambda": "^8.10.77", "@types/cookie": "^0.4.0", "@types/fs-extra": "^9.0.11", - "@types/node": "^15.12.5", + "@types/node": "^20.11.17", "aws-sdk": "^2.935.0", "html-loader": "^2.1.2", "prettier": "^2.3.2", @@ -32,7 +32,11 @@ "webpack-cli": "^4.7.2" }, "dependencies": { + "@aws-sdk/client-cloudformation": "^3.529.1", + "@aws-sdk/client-cognito-identity-provider": "^3.530.0", + "@aws-sdk/client-lambda": "^3.529.1", "@aws-sdk/client-mwaa": "^3.49.0", + "@aws-sdk/client-s3": "^3.529.1", "adm-zip": "^0.5.5", "aws-jwt-verify": "^1.0.2", "cookie": "^0.4.1" diff --git a/usecases/mwaa-public-webserver-custom-domain/src/cfn-custom-resources/client-secret-retrieval/index.ts b/usecases/mwaa-public-webserver-custom-domain/src/cfn-custom-resources/client-secret-retrieval/index.ts index a6b83e2..2a34803 100644 --- a/usecases/mwaa-public-webserver-custom-domain/src/cfn-custom-resources/client-secret-retrieval/index.ts +++ b/usecases/mwaa-public-webserver-custom-domain/src/cfn-custom-resources/client-secret-retrieval/index.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: MIT-0 import { CloudFormationCustomResourceHandler } from "aws-lambda"; -import CognitoIdentityServiceProvider from "aws-sdk/clients/cognitoidentityserviceprovider"; +import { CognitoIdentityProvider, DescribeUserPoolClientCommandInput } from "@aws-sdk/client-cognito-identity-provider"; import { sendCfnResponse, Status } from "./cfn-response"; async function retrieveClientSecret( @@ -17,17 +17,16 @@ async function retrieveClientSecret( } const userPoolId = userPoolArn.split("/")[1]; const userPoolRegion = userPoolArn.split(":")[3]; - const cognitoClient = new CognitoIdentityServiceProvider({ + const cognitoClient = new CognitoIdentityProvider({ region: userPoolRegion, }); - const input: CognitoIdentityServiceProvider.Types.DescribeUserPoolClientRequest = + const input: DescribeUserPoolClientCommandInput = { UserPoolId: userPoolId, ClientId: clientId, }; const { UserPoolClient } = await cognitoClient - .describeUserPoolClient(input) - .promise(); + .describeUserPoolClient(input); if (!UserPoolClient?.ClientSecret) { throw new Error( `User Pool client ${clientId} is not set up with a client secret` diff --git a/usecases/mwaa-public-webserver-custom-domain/src/cfn-custom-resources/lambda-code-update/index.ts b/usecases/mwaa-public-webserver-custom-domain/src/cfn-custom-resources/lambda-code-update/index.ts index d40689e..b9a2084 100644 --- a/usecases/mwaa-public-webserver-custom-domain/src/cfn-custom-resources/lambda-code-update/index.ts +++ b/usecases/mwaa-public-webserver-custom-domain/src/cfn-custom-resources/lambda-code-update/index.ts @@ -6,7 +6,7 @@ import { CloudFormationCustomResourceDeleteEvent, CloudFormationCustomResourceUpdateEvent, } from "aws-lambda"; -import Lambda from "aws-sdk/clients/lambda"; +import { Lambda } from "@aws-sdk/client-lambda"; import Zip from "adm-zip"; import { writeFileSync, mkdtempSync } from "fs"; import { resolve } from "path"; @@ -27,15 +27,16 @@ async function updateLambdaCode( `Adding configuration to Lambda function ${lambdaFunction}:\n${stringifiedConfig}` ); const region = lambdaFunction.split(":")[3]; - const lambdaClient = new Lambda({ region }); + const lambdaClient = new Lambda({ + region, + }); // Parse the JSON to ensure it's validity (and avoid ugly errors at runtime) const config = JSON.parse(stringifiedConfig); // Fetch and extract Lambda zip contents to temporary folder, add configuration.json, and rezip const { Code } = await lambdaClient .getFunction({ FunctionName: lambdaFunction, - }) - .promise(); + }); const data = await fetch(Code!.Location!); const lambdaZip = new Zip(data); console.log( @@ -61,8 +62,7 @@ async function updateLambdaCode( FunctionName: lambdaFunction, ZipFile: newLambdaZip.toBuffer(), Publish: true, - }) - .promise(); + }); console.log({ CodeSha256, Version, FunctionArn }); return { physicalResourceId: lambdaFunction, diff --git a/usecases/mwaa-public-webserver-custom-domain/src/cfn-custom-resources/us-east-1-lambda-stack/index.ts b/usecases/mwaa-public-webserver-custom-domain/src/cfn-custom-resources/us-east-1-lambda-stack/index.ts index e258609..100fc48 100644 --- a/usecases/mwaa-public-webserver-custom-domain/src/cfn-custom-resources/us-east-1-lambda-stack/index.ts +++ b/usecases/mwaa-public-webserver-custom-domain/src/cfn-custom-resources/us-east-1-lambda-stack/index.ts @@ -13,16 +13,26 @@ import { CloudFormationCustomResourceDeleteEvent, CloudFormationCustomResourceUpdateEvent, } from "aws-lambda"; -import CloudFormation from "aws-sdk/clients/cloudformation"; -import S3 from "aws-sdk/clients/s3"; -import Lambda from "aws-sdk/clients/lambda"; +import { + CloudFormation, + Stack, + waitUntilChangeSetCreateComplete, + waitUntilStackCreateComplete, + waitUntilStackUpdateComplete, +} from "@aws-sdk/client-cloudformation"; +import { Lambda } from "@aws-sdk/client-lambda"; +import { S3 } from "@aws-sdk/client-s3"; import { sendCfnResponse, Status } from "./cfn-response"; import { fetch } from "./https"; const CFN_CLIENT = new CloudFormation(); -const CFN_CLIENT_US_EAST_1 = new CloudFormation({ region: "us-east-1" }); +const CFN_CLIENT_US_EAST_1 = new CloudFormation({ + region: "us-east-1", +}); const LAMBDA_CLIENT = new Lambda(); -const S3_CLIENT_US_EAST_1 = new S3({ region: "us-east-1" }); +const S3_CLIENT_US_EAST_1 = new S3({ + region: "us-east-1", +}); interface CfnTemplateBase { Resources: { @@ -122,13 +132,12 @@ async function ensureUsEast1LambdaStack(props: { const { Stacks: stacks } = await CFN_CLIENT_US_EAST_1.describeStacks({ StackName: props.stackName, }) - .promise() .catch(() => ({ Stacks: undefined })); if (stacks?.length) { console.log("Deleting us-east-1 stack ..."); await CFN_CLIENT_US_EAST_1.deleteStack({ StackName: props.stackName, - }).promise(); + }); console.log("us-east-1 stack deleted"); } else { console.log("us-east-1 stack already deleted"); @@ -146,7 +155,7 @@ async function ensureUsEast1LambdaStack(props: { const { TemplateBody: originalTemplate } = await CFN_CLIENT.getTemplate({ StackName: props.stackId, TemplateStage: "Processed", - }).promise(); + }); if (!originalTemplate) throw new Error( `Failed to get template for stack ${props.stackName} (${props.stackId})` @@ -227,7 +236,7 @@ async function ensureLambdaUsEast1Stack(props: { TemplateBody: props.newTemplate, ChangeSetType: "UPDATE", ResourceTypes: ["AWS::Lambda::Function"], - }).promise(); + }); if (!changeSetArn) throw new Error( "Failed to create change set for lambda handlers deployment" @@ -235,10 +244,12 @@ async function ensureLambdaUsEast1Stack(props: { console.log( "Waiting for completion of change set for adding lambda functions to us-east-1 stack ..." ); - await CFN_CLIENT_US_EAST_1.waitFor("changeSetCreateComplete", { + await waitUntilChangeSetCreateComplete({ + client: CFN_CLIENT_US_EAST_1, + maxWaitTime: 200, + }, { ChangeSetName: changeSetArn, }) - .promise() .catch((err) => console.log( `Caught exception while waiting for change set create completion: ${err}` @@ -247,7 +258,7 @@ async function ensureLambdaUsEast1Stack(props: { const { Status: status, StatusReason: reason } = await CFN_CLIENT_US_EAST_1.describeChangeSet({ ChangeSetName: changeSetArn, - }).promise(); + }); if (status === "FAILED") { // The only reason we'll allow a FAILED change set is if there were no changes if (!reason?.includes("didn't contain changes")) { @@ -256,13 +267,13 @@ async function ensureLambdaUsEast1Stack(props: { // No changes to make to the Lambda@Edge functions, clean up the change set then await CFN_CLIENT_US_EAST_1.deleteChangeSet({ ChangeSetName: changeSetArn, - }).promise(); + }); // Need to get the outputs (Lambda ARNs) from the existing stack then const { Stacks: existingStacks } = await CFN_CLIENT_US_EAST_1.describeStacks({ StackName: props.stackName, - }).promise(); + }); const existingOutputs = extractOutputsFromStackResponse(existingStacks); console.log( `us-east-1 stack unchanged. Stack outputs: ${JSON.stringify( @@ -281,16 +292,20 @@ async function ensureLambdaUsEast1Stack(props: { ); await CFN_CLIENT_US_EAST_1.executeChangeSet({ ChangeSetName: changeSetArn, - }).promise(); + }); console.log( "Waiting for completion of execute change set for adding lambda functions to us-east-1 stack ..." ); - const { Stacks: updatedStacks } = await CFN_CLIENT_US_EAST_1.waitFor( - "stackUpdateComplete", - { + await waitUntilStackUpdateComplete({ + client: CFN_CLIENT_US_EAST_1, + maxWaitTime: 200, + }, { + StackName: props.stackName, + }); + const { Stacks: updatedStacks } = + await CFN_CLIENT_US_EAST_1.describeStacks({ StackName: props.stackName, - } - ).promise(); + }); const outputs = extractOutputsFromStackResponse(updatedStacks); console.log( `us-east-1 stack succesfully updated. Stack outputs: ${JSON.stringify( @@ -302,7 +317,7 @@ async function ensureLambdaUsEast1Stack(props: { return outputs as { [key: string]: string }; } -function extractOutputsFromStackResponse(stacks?: CloudFormation.Stack[]) { +function extractOutputsFromStackResponse(stacks?: Stack[]) { // find the ARNs for all Lambda functions, which will be output from this custom resource const outputs = LAMBDA_NAMES.reduce((acc, lambdaName) => { @@ -329,7 +344,6 @@ async function ensureDeploymentUsEast1Stack(props: { const { Stacks: usEast1Stacks } = await CFN_CLIENT_US_EAST_1.describeStacks({ StackName: props.stackName, }) - .promise() .catch(() => ({ Stacks: undefined })); if (usEast1Stacks?.length) { const deploymentBucket = usEast1Stacks[0].Outputs?.find( @@ -347,7 +361,7 @@ async function ensureDeploymentUsEast1Stack(props: { console.log("Getting CFN stack tags ..."); const { Stacks: mainRegionStacks } = await CFN_CLIENT.describeStacks({ StackName: props.stackId, - }).promise(); + }); if (!mainRegionStacks?.length) { throw new Error( `Failed to describe stack ${props.stackName} (${props.stackId})` @@ -363,24 +377,31 @@ async function ensureDeploymentUsEast1Stack(props: { ChangeSetType: "CREATE", ResourceTypes: ["AWS::S3::Bucket"], Tags: mainRegionStacks[0].Tags, - }).promise(); + }); if (!changeSetArn) throw new Error("Failed to create change set for bucket deployment"); console.log("Waiting for change set create complete for us-east-1 stack ..."); - await CFN_CLIENT_US_EAST_1.waitFor("changeSetCreateComplete", { + await waitUntilChangeSetCreateComplete({ + client: CFN_CLIENT_US_EAST_1, + maxWaitTime: 200, + }, { ChangeSetName: changeSetArn, - }).promise(); + }); console.log("Executing change set for us-east-1 stack ..."); await CFN_CLIENT_US_EAST_1.executeChangeSet({ ChangeSetName: changeSetArn, - }).promise(); + }); console.log("Waiting for creation of us-east-1 stack ..."); - const { Stacks: createdStacks } = await CFN_CLIENT_US_EAST_1.waitFor( - "stackCreateComplete", - { + await waitUntilStackCreateComplete({ + client: CFN_CLIENT_US_EAST_1, + maxWaitTime: 200, + }, { + StackName: props.stackName, + }); + const { Stacks: createdStacks } = + await CFN_CLIENT_US_EAST_1.describeStacks({ StackName: props.stackName, - } - ).promise(); + }); const deploymentBucket = createdStacks?.[0].Outputs?.find( (output) => output.OutputKey === "DeploymentBucket" )?.OutputValue; @@ -397,7 +418,7 @@ async function copyLambdaCodeToUsEast1(props: { console.log(`Copying Lambda code: ${JSON.stringify(props, null, 2)}`); const { Code } = await LAMBDA_CLIENT.getFunction({ FunctionName: props.lambdaArn, - }).promise(); + }); console.log( `Downloading lambda code for ${props.lambdaArn} from ${Code!.Location!}` ); @@ -406,7 +427,7 @@ async function copyLambdaCodeToUsEast1(props: { Bucket: props.toBucket, Key: props.key, Body: data, - }).promise(); + }); return props; } diff --git a/usecases/mwaa-public-webserver-custom-domain/src/cfn-custom-resources/user-pool-client/index.ts b/usecases/mwaa-public-webserver-custom-domain/src/cfn-custom-resources/user-pool-client/index.ts index 10bfba1..fd536d7 100644 --- a/usecases/mwaa-public-webserver-custom-domain/src/cfn-custom-resources/user-pool-client/index.ts +++ b/usecases/mwaa-public-webserver-custom-domain/src/cfn-custom-resources/user-pool-client/index.ts @@ -13,7 +13,11 @@ import { CloudFormationCustomResourceHandler, CloudFormationCustomResourceUpdateEvent, } from "aws-lambda"; -import CognitoIdentityServiceProvider from "aws-sdk/clients/cognitoidentityserviceprovider"; +import { + CognitoIdentityProvider, + UpdateUserPoolClientCommandInput, + UserPoolClientType, +} from "@aws-sdk/client-cognito-identity-provider"; import { sendCfnResponse, Status } from "./cfn-response"; const CUSTOM_RESOURCE_CURRENT_VERSION_NAME = "UpdatedUserPoolClientV2"; @@ -22,7 +26,7 @@ const SENTINEL_DOMAIN = "example.com"; async function getUserPoolClient(props: Props) { const userPoolId = props.UserPoolArn.split("/")[1]; const userPoolRegion = props.UserPoolArn.split(":")[3]; - const cognitoClient = new CognitoIdentityServiceProvider({ + const cognitoClient = new CognitoIdentityProvider({ region: userPoolRegion, }); const input = { @@ -31,8 +35,7 @@ async function getUserPoolClient(props: Props) { }; console.debug("Describing User Pool Client", JSON.stringify(input, null, 4)); const { UserPoolClient } = await cognitoClient - .describeUserPoolClient(input) - .promise(); + .describeUserPoolClient(input); if (!UserPoolClient) { throw new Error("User Pool Client not found!"); } @@ -43,11 +46,11 @@ async function updateUserPoolClient( props: Props, redirectUrisSignIn: string[], redirectUrisSignOut: string[], - existingUserPoolClient: CognitoIdentityServiceProvider.UserPoolClientType + existingUserPoolClient: UserPoolClientType ) { const userPoolId = props.UserPoolArn.split("/")[1]; const userPoolRegion = props.UserPoolArn.split(":")[3]; - const cognitoClient = new CognitoIdentityServiceProvider({ + const cognitoClient = new CognitoIdentityProvider({ region: userPoolRegion, }); @@ -60,7 +63,7 @@ async function updateUserPoolClient( // To be able to set the redirect URL's, we must enable OAuth––required by Cognito // Vice versa, when removing redirect URL's, we must disable OAuth if there's no more redirect URL's left - let AllowedOAuthFlows: string[]; + let AllowedOAuthFlows: ("code"|"client_credentials"|"implicit")[]; let AllowedOAuthFlowsUserPoolClient: boolean; let AllowedOAuthScopes: string[]; if (CallbackURLs.length) { @@ -81,7 +84,7 @@ async function updateUserPoolClient( delete existingFields.LastModifiedDate; delete existingFields.ClientSecret; - const input: CognitoIdentityServiceProvider.Types.UpdateUserPoolClientRequest = + const input: UpdateUserPoolClientCommandInput = { ...existingFields, AllowedOAuthFlows, @@ -93,7 +96,7 @@ async function updateUserPoolClient( LogoutURLs, }; console.debug("Updating User Pool Client", JSON.stringify(input, null, 4)); - await cognitoClient.updateUserPoolClient(input).promise(); + await cognitoClient.updateUserPoolClient(input); } async function undoPriorUpdate( diff --git a/usecases/mwaa-public-webserver-custom-domain/src/cfn-custom-resources/user-pool-domain/index.ts b/usecases/mwaa-public-webserver-custom-domain/src/cfn-custom-resources/user-pool-domain/index.ts index 2e732c5..2a9f930 100644 --- a/usecases/mwaa-public-webserver-custom-domain/src/cfn-custom-resources/user-pool-domain/index.ts +++ b/usecases/mwaa-public-webserver-custom-domain/src/cfn-custom-resources/user-pool-domain/index.ts @@ -14,7 +14,7 @@ import { CloudFormationCustomResourceDeleteEvent, CloudFormationCustomResourceUpdateEvent, } from "aws-lambda"; -import CognitoIdentityServiceProvider from "aws-sdk/clients/cognitoidentityserviceprovider"; +import { CognitoIdentityProvider } from "@aws-sdk/client-cognito-identity-provider"; import { sendCfnResponse, Status } from "./cfn-response"; async function ensureCognitoUserPoolDomain( @@ -27,12 +27,11 @@ async function ensureCognitoUserPoolDomain( } const newUserPoolId = newUserPoolArn.split("/")[1]; const newUserPoolRegion = newUserPoolArn.split(":")[3]; - const cognitoClient = new CognitoIdentityServiceProvider({ + const cognitoClient = new CognitoIdentityProvider({ region: newUserPoolRegion, }); const { UserPool } = await cognitoClient - .describeUserPool({ UserPoolId: newUserPoolId }) - .promise(); + .describeUserPool({ UserPoolId: newUserPoolId }); if (!UserPool) { throw new Error(`User Pool ${newUserPoolArn} does not exist`); } diff --git a/usecases/mwaa-public-webserver-custom-domain/template.yaml b/usecases/mwaa-public-webserver-custom-domain/template.yaml index c6504dc..417c24d 100644 --- a/usecases/mwaa-public-webserver-custom-domain/template.yaml +++ b/usecases/mwaa-public-webserver-custom-domain/template.yaml @@ -234,7 +234,7 @@ Globals: - ApplyPermissionsBoundary - Ref: PermissionsBoundaryPolicyArn - Ref: AWS::NoValue - Runtime: nodejs14.x + Runtime: nodejs18.x Resources: CheckAuthHandler: Type: AWS::Serverless::Function