首次部署时自动化填充AWS Parameter Store的Serverless技术问题
解决Serverless部署时SSM参数缺失&自动创建RDS的问题
我明白你的痛点:首次部署时SSM参数还没创建,Serverless提前去读参数导致报错,之前用的before:package:cleanup钩子时机确实太晚了——那时候Serverless已经在解析CloudFormation模板、尝试拉取SSM参数了。给你几个可行的方案,从省心到灵活:
方案一:用CloudFormation自定义资源(最省心)
不用写额外脚本或插件,直接在serverless.yml里定义CloudFormation资源,让CloudFormation自动先创建SSM参数,再部署RDS——完全集成在部署流程里,不会有参数找不到的问题。
示例配置:
resources: Resources: # 先创建SSM用户名参数 RDSAdminUserParam: Type: AWS::SSM::Parameter Properties: Name: /my-app/rds/admin-user Type: String Value: db-admin Description: RDS管理员账号 # 自动生成随机密码并存到SSM RDSAdminPassParam: Type: AWS::SSM::Parameter Properties: Name: /my-app/rds/admin-pass Type: SecureString # 用CloudFormation内置函数从栈ID生成随机字符串,也可以换Lambda生成更复杂的密码 Value: !Join ['', [!Select [0, !Split ['-', !Select [4, !Split ['/', !Ref AWS::StackId]]]], !Select [1, !Split ['-', !Select [4, !Split ['/', !Ref AWS::StackId]]]]]] Description: 自动生成的RDS管理员密码 # 引用SSM参数创建RDS实例 MyRDSInstance: Type: AWS::RDS::DBInstance Properties: DBInstanceClass: db.t3.micro Engine: MySQL MasterUsername: !Ref RDSAdminUserParam MasterUserPassword: !Ref RDSAdminPassParam PubliclyAccessible: false # 生产环境建议关闭公网访问 AllocatedStorage: 20 # 其他RDS配置按需添加...
部署时CloudFormation会严格按顺序执行:先创建两个SSM参数,再用这些参数创建RDS。后续调试数据库时,用AWS CLI就能取到凭据:
# 获取用户名 aws ssm get-parameter --name /my-app/rds/admin-user # 获取密码(需要解密) aws ssm get-parameter --name /my-app/rds/admin-pass --with-decryption
方案二:用更早的Serverless钩子执行自定义脚本
如果需要自定义参数生成逻辑(比如从外部系统拉取密码),可以把脚本绑定到Serverless开始解析模板之前的钩子,确保参数在模板读取前就准备好。推荐这两个钩子:
before:package:package:在打包部署资源前执行,此时还未处理模板中的SSM引用before:deploy:deploy:如果部署流程包含自动打包,这个钩子会在打包启动前触发
修改serverless.yml的插件配置:
plugins: - serverless-scriptable-plugin custom: scriptable: hooks: before:package:package: ${file(scripts/create-ssm-params.js)}
然后编写scripts/create-ssm-params.js脚本,逻辑是检查参数是否存在,不存在就自动创建:
const { SSMClient, GetParameterCommand, PutParameterCommand } = require('@aws-sdk/client-ssm'); const crypto = require('crypto'); module.exports = async () => { const ssmClient = new SSMClient({ region: process.env.AWS_REGION || 'us-east-1' }); // 定义要创建的参数:用户名固定,密码自动生成16位随机字符串 const paramsToSet = [ { name: '/my-app/rds/admin-user', value: 'db-admin', type: 'String' }, { name: '/my-app/rds/admin-pass', value: crypto.randomBytes(16).toString('hex'), type: 'SecureString' } ]; for (const param of paramsToSet) { try { // 先检查参数是否存在 await ssmClient.send(new GetParameterCommand({ Name: param.name })); console.log(`✅ 参数 ${param.name} 已存在,跳过`); } catch (err) { if (err.name === 'ParameterNotFound') { // 不存在则创建参数 await ssmClient.send(new PutParameterCommand({ Name: param.name, Value: param.value, Type: param.type, Overwrite: false // 避免覆盖已有参数 })); console.log(`✅ 创建缺失参数 ${param.name}`); } else { // 其他错误直接抛出,中断部署 throw err; } } } };
⚠️ 注意:要确保部署用的AWS账号拥有ssm:GetParameter和ssm:PutParameter的权限,否则脚本会执行失败。
总结
- 追求简单稳定:选方案一,完全依赖CloudFormation原生能力,不用额外维护脚本;
- 需要自定义参数逻辑:选方案二,用更早的钩子提前创建参数,适配复杂场景。
内容的提问来源于stack exchange,提问作者Lukasz_K_K
相关产品推荐
相关产品推荐

