1919class BootstrappedDb (Construct ):
2020 """
2121 Given an RDS database, connect to DB and create a database, user, and
22- password
22+ password, optionally a read-only user if `read_only_username` is provided.
2323 """
2424
2525 def __init__ (
@@ -32,6 +32,7 @@ def __init__(
3232 secrets_prefix : str ,
3333 handler : Union [aws_lambda .Function , aws_lambda .SingletonFunction ],
3434 db_engine : Optional [str ] = None ,
35+ read_only_username : Optional [str ] = None ,
3536 ) -> None :
3637 super ().__init__ (scope , id )
3738
@@ -44,6 +45,7 @@ def __init__(
4445 " you must provide a value for `db_engine` explicitly."
4546 )
4647
48+ # Create secret for the main user
4749 self .secret = secretsmanager .Secret (
4850 self ,
4951 id ,
@@ -66,27 +68,66 @@ def __init__(
6668 description = f"Deployed by { Stack .of (self ).stack_name } " ,
6769 )
6870
71+ # Optionally create a secret for the read-only user
72+ self .read_only_secret = None
73+ if read_only_username :
74+ self .read_only_secret = secretsmanager .Secret (
75+ self ,
76+ f"{ id } -readonly-secret" ,
77+ secret_name = os .path .join (
78+ secrets_prefix ,
79+ f"{ id } _readonly" .replace (" " , "_" ),
80+ self .node .id [- 8 :],
81+ ),
82+ generate_secret_string = secretsmanager .SecretStringGenerator (
83+ secret_string_template = json .dumps (
84+ {
85+ "dbname" : new_dbname ,
86+ "engine" : db_engine if db_engine else db .engine .engine_type , # type: ignore
87+ "port" : db .instance_endpoint .port ,
88+ "host" : db .instance_endpoint .hostname ,
89+ "username" : read_only_username ,
90+ },
91+ ),
92+ generate_string_key = "password" ,
93+ exclude_punctuation = True ,
94+ ),
95+ description = f"Read-only user secret deployed by { Stack .of (self ).stack_name } " ,
96+ )
97+
6998 self .provider = custom_resources .Provider (
7099 scope , "BootstrapProvider" , on_event_handler = handler
71100 )
72101
102+ # Prepare the properties for the custom resource
103+ resource_properties = {
104+ "conn_secret_arn" : db .secret .secret_arn ,
105+ "new_user_secret_arn" : self .secret .secret_arn ,
106+ "version" : handler .current_version .version ,
107+ }
108+
109+ # Optionally include the read-only secret ARN
110+ if self .read_only_secret :
111+ resource_properties ["read_only_user_secret_arn" ] = (
112+ self .read_only_secret .secret_arn
113+ )
114+
73115 self .resource = CustomResource (
74116 scope = scope ,
75117 id = "BootstrapHandlerResource" ,
76118 service_token = self .provider .service_token ,
77- properties = {
78- "conn_secret_arn" : db .secret .secret_arn ,
79- "new_user_secret_arn" : self .secret .secret_arn ,
80- "version" : handler .current_version .version ,
81- },
119+ properties = resource_properties ,
82120 )
83121
84- # Allow lambda to...
85- # read new user secret
122+ # Grant the Lambda handler permissions to read the main user secret
86123 self .secret .grant_read (handler )
87- # read database secret
124+
125+ # If the read-only secret exists, grant the handler permissions to read it
126+ if self .read_only_secret :
127+ self .read_only_secret .grant_read (handler )
128+
129+ # Grant the Lambda handler permission to connect to the database
88130 db .secret .grant_read (handler )
89- # connect to database
90131 db .connections .allow_from (handler , port_range = ec2 .Port .tcp (5432 ))
91132
92133 def is_required_by (self , construct : Construct ):
0 commit comments