@@ -115,6 +115,8 @@ export interface SecretProps {
115
115
/**
116
116
* Configuration for how to generate a secret value.
117
117
*
118
+ * Only one of `secretString` and `generateSecretString` can be provided.
119
+ *
118
120
* @default - 32 characters with upper-case letters, lower-case letters, punctuation and numbers (at least one from each
119
121
* category), per the default values of ``SecretStringGenerator``.
120
122
*/
@@ -128,6 +130,24 @@ export interface SecretProps {
128
130
*/
129
131
readonly secretName ?: string ;
130
132
133
+ /**
134
+ * Initial value for the secret
135
+ *
136
+ * **NOTE:** *It is **highly** encouraged to leave this field undefined and allow SecretsManager to create the secret value.
137
+ * The secret string -- if provided -- will be included in the output of the cdk as part of synthesis,
138
+ * and will appear in the CloudFormation template in the console. This can be secure(-ish) if that value is merely reference to
139
+ * another resource (or one of its attributes), but if the value is a plaintext string, it will be visible to anyone with access
140
+ * to the CloudFormation template (via the AWS Console, SDKs, or CLI).
141
+ *
142
+ * Specifies text data that you want to encrypt and store in this new version of the secret.
143
+ * May be a simple string value, or a string representation of a JSON structure.
144
+ *
145
+ * Only one of `secretString` and `generateSecretString` can be provided.
146
+ *
147
+ * @default - SecretsManager generates a new secret value.
148
+ */
149
+ readonly secretStringBeta1 ?: SecretStringValueBeta1 ;
150
+
131
151
/**
132
152
* Policy to apply when the secret is removed from this stack.
133
153
*
@@ -160,6 +180,64 @@ export interface ReplicaRegion {
160
180
readonly encryptionKey ?: kms . IKey ;
161
181
}
162
182
183
+ /**
184
+ * An experimental class used to specify an initial secret value for a Secret.
185
+ * The class wraps a simple string (or JSON representation) in order to provide some safety checks and warnings
186
+ * about the dangers of using plaintext strings as initial secret seed values via CDK/CloudFormation.
187
+ */
188
+ export class SecretStringValueBeta1 {
189
+
190
+ /**
191
+ * Creates a `SecretStringValueBeta1` from a plaintext value.
192
+ * This approach is inherently unsafe, as the secret value may be visible in your source control repository
193
+ * and will also appear in plaintext in the resulting CloudFormation template, including in the AWS Console or APIs.
194
+ * Usage of this method is discouraged, especially for production workloads.
195
+ */
196
+ public static fromUnsafePlaintext ( secretValue : string ) { return new SecretStringValueBeta1 ( secretValue ) ; }
197
+
198
+ /**
199
+ * Creates a `SecretValueValueBeta1` from a string value coming from a Token.
200
+ * The intent is to enable creating secrets from references (e.g., `Ref`, `Fn::GetAtt`) from other resources.
201
+ * This might be the direct output of another Construct, or the output of a Custom Resource.
202
+ * This method throws if it determines the input is an unsafe plaintext string.
203
+ *
204
+ * For example:
205
+ * ```ts
206
+ * // Creates a new IAM user, access and secret keys, and stores the secret access key in a Secret.
207
+ * const user = new iam.User(this, 'User');
208
+ * const accessKey = new iam.CfnAccessKey(this, 'AccessKey', { userName: user.userName });
209
+ * const secretValue = secretsmanager.SecretStringValueBeta1.fromToken(accessKey.attrSecretAccessKey);
210
+ * new secretsmanager.Secret(this, 'Secret', {
211
+ * secretStringBeta1: secretValue,
212
+ * });
213
+ * ```
214
+ *
215
+ * The secret may also be embedded in a string representation of a JSON structure:
216
+ * const secretValue = secretsmanager.SecretStringValueBeta1.fromToken(JSON.stringify({
217
+ * username: user.userName,
218
+ * database: 'foo',
219
+ * password: accessKey.attrSecretAccessKey
220
+ * }));
221
+ *
222
+ * Note that the value being a Token does *not* guarantee safety. For example, a Lazy-evaluated string
223
+ * (e.g., `Lazy.string({ produce: () => 'myInsecurePassword' }))`) is a Token, but as the output is
224
+ * ultimately a plaintext string, and so insecure.
225
+ *
226
+ * @param secretValueFromToken a secret value coming from a Construct attribute or Custom Resource output
227
+ */
228
+ public static fromToken ( secretValueFromToken : string ) {
229
+ if ( ! Token . isUnresolved ( secretValueFromToken ) ) {
230
+ throw new Error ( 'SecretStringValueBeta1 appears to be plaintext (unsafe) string (or resolved Token); use fromUnsafePlaintext if this is intentional' ) ;
231
+ }
232
+ return new SecretStringValueBeta1 ( secretValueFromToken ) ;
233
+ }
234
+
235
+ private constructor ( private readonly _secretValue : string ) { }
236
+
237
+ /** Returns the secret value */
238
+ public secretValue ( ) : string { return this . _secretValue ; }
239
+ }
240
+
163
241
/**
164
242
* Attributes required to import an existing secret into the Stack.
165
243
* One ARN format (`secretArn`, `secretCompleteArn`, `secretPartialArn`) must be provided.
@@ -459,10 +537,15 @@ export class Secret extends SecretBase {
459
537
throw new Error ( '`secretStringTemplate` and `generateStringKey` must be specified together.' ) ;
460
538
}
461
539
540
+ if ( props . generateSecretString && props . secretStringBeta1 ) {
541
+ throw new Error ( 'Cannot specify both `generateSecretString` and `secretStringBeta1`.' ) ;
542
+ }
543
+
462
544
const resource = new secretsmanager . CfnSecret ( this , 'Resource' , {
463
545
description : props . description ,
464
546
kmsKeyId : props . encryptionKey && props . encryptionKey . keyArn ,
465
- generateSecretString : props . generateSecretString || { } ,
547
+ generateSecretString : props . generateSecretString ?? ( props . secretStringBeta1 ? undefined : { } ) ,
548
+ secretString : props . secretStringBeta1 ?. secretValue ( ) ,
466
549
name : this . physicalName ,
467
550
replicaRegions : Lazy . any ( { produce : ( ) => this . replicaRegions } , { omitEmptyArray : true } ) ,
468
551
} ) ;
0 commit comments