如何将配置传递到 AWS-CDK 堆栈中使用的 Web 应用程序

pfr*_*ied 2 amazon-web-services aws-cloudformation aws-cdk

假设您有一个 AWS CDK 堆栈创建了一些资源

  1. Cognito 用户池
  2. 应用同步端点

您想要部署到 S3 的 Web 应用程序会使用它们。

如何以编程方式将配置(例如 appsync 端点的端点 url)传递到要部署的应用程序?

AWS Amplify 创建aws-exports.js应用程序使用的文件。该文件由一些 amplify 命令创建,并放入应用程序目录中,以便从应用程序访问。

有没有关于如何在不使用放大的情况下解决此问题的工具或建议?如果有人有一些例子或想法那就太好了。

我正在使用使用生成器创建的标准 React 应用程序。

小智 6

我知道几个月前就有人问过这个问题,但我是在试图自己解决这个问题时来到这里的。

挑战是配置值在 CDK 部署之后才可用,因此您不能仅添加脚本来写入堆栈输出。我的解决方案是添加我自己的部署脚本,该脚本首先部署后端堆栈,然后构建配置,然后部署 Web 堆栈。

使用以下命令调用脚本npm run deploy

cdk/package.json

"deploy": "node deploy"
Run Code Online (Sandbox Code Playgroud)

cdk/deploy.js

const execSync = require('child_process').execSync

const exec = command => {
  execSync(command, {
    stdio: [0, 1, 2]
  })
}

console.log('- Deploying backend.')
exec(`cdk deploy --all --require-approval never -c deploy=backend`)

console.log('- Generating frontend config.')
exec(`npm run build-config --prefix ../web`) // "node scripts/buildConfig.js"

console.log('- Building frontend.')
exec(`npm run build --prefix ../web`) // "react-scripts build"

console.log('- Deploying frontend.')
exec(`cdk deploy --all --require-approval never -c deploy=frontend`)
Run Code Online (Sandbox Code Playgroud)

条件堆栈部署:

cdk/cdk.ts

const deploy = app.node.tryGetContext('deploy')

if (deploy === 'backend') {
  new AuthStack(...)
  new ApiStack(...)
}

if (deploy === 'frontend') {
  new WebStack(...)
}
Run Code Online (Sandbox Code Playgroud)

输出配置所需的值:

cdk/lib/auth-stack.ts

...
const userPool = new cognito.UserPool(...)

this.exportValue(userPool.userPoolId, {
  name: 'UserPoolId',
})
...
Run Code Online (Sandbox Code Playgroud)

读取输出并构建配置:

web/scripts/buildConfig.js

const fs = require('fs')
const path = require('path')
const { CloudFormation } = require('aws-sdk')

const region = 'us-west-2'
const cloudformation = new CloudFormation({ region })

const outputs = {}

// get stack info
const apiStack = await cloudformation.describeStacks({ StackName: 'ApiStack' }).promise()
const authStack = await cloudformation.describeStacks({ StackName: 'AuthStack' }).promise()

// build the outputs into a simple object
apiStack.Stacks[0].Outputs.forEach(({ ExportName, OutputValue }) => { outputs[ExportName] = OutputValue })
authStack.Stacks[0].Outputs.forEach(({ ExportName, OutputValue }) => { outputs[ExportName] = OutputValue })

// read existing config (this is our version of aws-exports)
const configFilePath = path.join(__dirname, '..', 'src', 'config.json')
const config = JSON.parse(fs.readFileSync(configFilePath))

// build config:
config.Auth = {
  region,
  userPoolId: outputs.UserPoolId,
  userPoolWebClientId: outputs.WebUserPoolClientId,
}
config.API = {
  aws_appsync_graphqlEndpoint: outputs.AppSyncURL,
  aws_appsync_region: region,
  aws_appsync_authenticationType: 'AMAZON_COGNITO_USER_POOLS',
  aws_project_region: region,
  aws_cognito_region: region,
  aws_user_pools_id: outputs.UserPoolId,
  aws_user_pools_web_client_id: outputs.WebUserPoolClientId,
}

// update the file
fs.writeFileSync(configFilePath, JSON.stringify(config, null, 2))
Run Code Online (Sandbox Code Playgroud)

在反应中:

web/src/App.tsx

import * as config from './config.json'

Amplify.configure({
  Auth: config.Auth,
  API: config.API,
})
Run Code Online (Sandbox Code Playgroud)