首页 > 解决方案 > 如何将配置传递给 AWS-CDK 堆栈中使用的 Web 应用程序

问题描述

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

  1. Cognito 用户池
  2. AppSync 端点

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

您将如何以编程方式将 appsync 端点的端点 url 等配置传递给您要部署的应用程序?

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

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

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

标签: amazon-web-servicesamazon-cloudformationaws-cdk

解决方案


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

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

调用脚本npm run deploy

cdk/package.json

"deploy": "node deploy"

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`)

条件堆栈部署:

cdk/cdk.ts

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

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

if (deploy === 'frontend') {
  new WebStack(...)
}

输出配置所需的值:

cdk/lib/auth-stack.ts

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

this.exportValue(userPool.userPoolId, {
  name: 'UserPoolId',
})
...

读取输出并构建配置:

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))

在 React 中:

web/src/App.tsx

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

Amplify.configure({
  Auth: config.Auth,
  API: config.API,
})

推荐阅读