@@ -9,10 +9,11 @@ import * as cxapi from '@aws-cdk/cx-api';
99import { Construct } from 'constructs' ;
1010import { IClusterEngine } from './cluster-engine' ;
1111import { DatabaseClusterAttributes , IDatabaseCluster } from './cluster-ref' ;
12+ import { DatabaseSecret } from './database-secret' ;
1213import { Endpoint } from './endpoint' ;
1314import { IParameterGroup , ParameterGroup } from './parameter-group' ;
1415import { applyDefaultRotationOptions , defaultDeletionProtection , renderCredentials , setupS3ImportExport , helperRemovalPolicy , renderUnless } from './private/util' ;
15- import { BackupProps , Credentials , InstanceProps , PerformanceInsightRetention , RotationSingleUserOptions , RotationMultiUserOptions } from './props' ;
16+ import { BackupProps , Credentials , InstanceProps , PerformanceInsightRetention , RotationSingleUserOptions , RotationMultiUserOptions , SnapshotCredentials } from './props' ;
1617import { DatabaseProxy , DatabaseProxyOptions , ProxyTarget } from './proxy' ;
1718import { CfnDBCluster , CfnDBClusterProps , CfnDBInstance } from './rds.generated' ;
1819import { ISubnetGroup , SubnetGroup } from './subnet-group' ;
@@ -661,9 +662,27 @@ export interface DatabaseClusterFromSnapshotProps extends DatabaseClusterBasePro
661662 /**
662663 * Credentials for the administrative user
663664 *
665+ * Note - using this prop only works with `Credentials.fromPassword()` with the
666+ * username of the snapshot, `Credentials.fromUsername()` with the username and
667+ * password of the snapshot or `Credentials.fromSecret()` with a secret containing
668+ * the username and password of the snapshot.
669+ *
664670 * @default - A username of 'admin' (or 'postgres' for PostgreSQL) and SecretsManager-generated password
671+ * that **will not be applied** to the cluster, use `snapshotCredentials` for the correct behavior.
672+ *
673+ * @deprecated use `snapshotCredentials` which allows to generate a new password
665674 */
666675 readonly credentials ?: Credentials ;
676+
677+ /**
678+ * Master user credentials.
679+ *
680+ * Note - It is not possible to change the master username for a snapshot;
681+ * however, it is possible to provide (or generate) a new password.
682+ *
683+ * @default - The existing username and password from the snapshot will be used.
684+ */
685+ readonly snapshotCredentials ?: SnapshotCredentials ;
667686}
668687
669688/**
@@ -687,12 +706,34 @@ export class DatabaseClusterFromSnapshot extends DatabaseClusterNew {
687706 constructor ( scope : Construct , id : string , props : DatabaseClusterFromSnapshotProps ) {
688707 super ( scope , id , props ) ;
689708
690- const credentials = renderCredentials ( this , props . engine , props . credentials ) ;
691- const secret = credentials . secret ;
709+ if ( props . credentials && ! props . credentials . password && ! props . credentials . secret ) {
710+ Annotations . of ( this ) . addWarning ( 'Use `snapshotCredentials` to modify password of a cluster created from a snapshot.' ) ;
711+ }
712+ if ( ! props . credentials && ! props . snapshotCredentials ) {
713+ Annotations . of ( this ) . addWarning ( 'Generated credentials will not be applied to cluster. Use `snapshotCredentials` instead. `addRotationSingleUser()` and `addRotationMultiUser()` cannot be used on tbis cluster.' ) ;
714+ }
715+ const deprecatedCredentials = renderCredentials ( this , props . engine , props . credentials ) ;
716+
717+ let credentials = props . snapshotCredentials ;
718+ let secret = credentials ?. secret ;
719+ if ( ! secret && credentials ?. generatePassword ) {
720+ if ( ! credentials . username ) {
721+ throw new Error ( '`snapshotCredentials` `username` must be specified when `generatePassword` is set to true' ) ;
722+ }
723+
724+ secret = new DatabaseSecret ( this , 'SnapshotSecret' , {
725+ username : credentials . username ,
726+ encryptionKey : credentials . encryptionKey ,
727+ excludeCharacters : credentials . excludeCharacters ,
728+ replaceOnPasswordCriteriaChanges : credentials . replaceOnPasswordCriteriaChanges ,
729+ replicaRegions : credentials . replicaRegions ,
730+ } ) ;
731+ }
692732
693733 const cluster = new CfnDBCluster ( this , 'Resource' , {
694734 ...this . newCfnProps ,
695735 snapshotIdentifier : props . snapshotIdentifier ,
736+ masterUserPassword : secret ?. secretValueFromJson ( 'password' ) ?. unsafeUnwrap ( ) ?? credentials ?. password ?. unsafeUnwrap ( ) , // Safe usage
696737 } ) ;
697738
698739 this . clusterIdentifier = cluster . ref ;
@@ -701,6 +742,13 @@ export class DatabaseClusterFromSnapshot extends DatabaseClusterNew {
701742 this . secret = secret . attach ( this ) ;
702743 }
703744
745+ if ( deprecatedCredentials . secret ) {
746+ const deprecatedSecret = deprecatedCredentials . secret . attach ( this ) ;
747+ if ( ! this . secret ) {
748+ this . secret = deprecatedSecret ;
749+ }
750+ }
751+
704752 // create a number token that represents the port of the cluster
705753 const portAttribute = Token . asNumber ( cluster . attrEndpointPort ) ;
706754 this . clusterEndpoint = new Endpoint ( cluster . attrEndpointAddress , portAttribute ) ;
0 commit comments