Skip to content

Commit 2642772

Browse files
committed
feat: configureable use of RDS Proxy
1 parent d4016e9 commit 2642772

7 files changed

Lines changed: 168 additions & 50 deletions

File tree

VirtualFinland.UsersAPI.Deployment/Common/Models/VpcSetup.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ namespace VirtualFinland.UsersAPI.Deployment.Common.Models;
55

66
public record VpcSetup
77
{
8-
public Input<string>? VpcId = default!;
8+
public Input<string> VpcId = default!;
99
public Output<IEnumerable<string>> PrivateSubnetIds = default!;
10+
public Input<string> SecurityGroupId = default!;
1011
}

VirtualFinland.UsersAPI.Deployment/Features/LambdaFunctionUrl.cs

Lines changed: 3 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -49,48 +49,21 @@ public LambdaFunctionUrl(Config config, StackSetup stackSetup, SecretsManager se
4949
Tags = stackSetup.Tags
5050
});
5151

52-
var rolePolicyAttachment = new RolePolicyAttachment($"{stackSetup.ProjectName}-LambdaRoleAttachment-{stackSetup.Environment}", new RolePolicyAttachmentArgs
52+
new RolePolicyAttachment($"{stackSetup.ProjectName}-LambdaRoleAttachment-{stackSetup.Environment}", new RolePolicyAttachmentArgs
5353
{
5454
Role = Output.Format($"{execRole.Name}"),
5555
PolicyArn = ManagedPolicy.AWSLambdaVPCAccessExecutionRole.ToString()
5656
});
5757

58-
var secretsManagerReadPolicy = new Policy($"{stackSetup.ProjectName}-LambdaSecretManagerPolicy-{stackSetup.Environment}", new()
59-
{
60-
Description = "Users-API Secret Get Policy",
61-
PolicyDocument = Output.Format($@"{{
62-
""Version"": ""2012-10-17"",
63-
""Statement"": [
64-
{{
65-
""Effect"": ""Allow"",
66-
""Action"": [
67-
""secretsmanager:GetSecretValue"",
68-
""secretsmanager:DescribeSecret""
69-
],
70-
""Resource"": [
71-
""{secretsManager.Arn}""
72-
]
73-
}}
74-
]
75-
}}"),
76-
Tags = stackSetup.Tags,
77-
});
78-
7958
new RolePolicyAttachment($"{stackSetup.ProjectName}-LambdaRoleAttachment-SecretManager-{stackSetup.Environment}", new RolePolicyAttachmentArgs
8059
{
8160
Role = execRole.Name,
82-
PolicyArn = secretsManagerReadPolicy.Arn
83-
});
84-
85-
var defaultSecurityGroup = Pulumi.Aws.Ec2.GetSecurityGroup.Invoke(new GetSecurityGroupInvokeArgs()
86-
{
87-
VpcId = stackSetup.VpcSetup.VpcId,
88-
Name = "default"
61+
PolicyArn = secretsManager.ReadPolicy.Arn
8962
});
9063

9164
var functionVpcArgs = new FunctionVpcConfigArgs()
9265
{
93-
SecurityGroupIds = defaultSecurityGroup.Apply(o => $"{o.Id}"),
66+
SecurityGroupIds = new[] { stackSetup.VpcSetup.SecurityGroupId },
9467
SubnetIds = stackSetup.VpcSetup.PrivateSubnetIds
9568
};
9669

VirtualFinland.UsersAPI.Deployment/Features/PostgresDatabase.cs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ public PostgresDatabase(Config config, StackSetup stackSetup)
2323
{
2424
SetupDevelopmentPostgresDatabase(config, stackSetup);
2525
}
26+
27+
if (config.GetBoolean("useRdsProxy") == true)
28+
{
29+
var rdsProxy = new RDSProxy(config, stackSetup, this);
30+
DatabaseConnectionString = rdsProxy.DatabaseConnectionString; // Override the connection string with one from the proxy
31+
}
2632
}
2733

2834
/// <summary>
@@ -75,7 +81,7 @@ public void SetupProductionPostgresDatabase(Config config, StackSetup stackSetup
7581
Tags = stackSetup.Tags,
7682
});
7783

78-
var auroraInstance = new ClusterInstance(stackSetup.CreateResourceName("database-instance"), new()
84+
new ClusterInstance(stackSetup.CreateResourceName("database-instance"), new()
7985
{
8086
ClusterIdentifier = auroraCluster.ClusterIdentifier,
8187
InstanceClass = "db.serverless",
@@ -87,9 +93,10 @@ public void SetupProductionPostgresDatabase(Config config, StackSetup stackSetup
8793
var DbName = config.Require("dbName");
8894
var DbUsername = config.Require("dbAdmin");
8995
var DbHostName = auroraCluster.Endpoint;
90-
DBIdentifier = auroraInstance.Identifier;
9196
var DbPassword = password.Result;
92-
DbConnectionString = Output.Format($"Host={DbHostName};Database={DbName};Username={DbUsername};Password={DbPassword}");
97+
98+
DatabaseConnectionString = Output.Format($"Host={DbHostName};Database={DbName};Username={DbUsername};Password={DbPassword}");
99+
DBIdentifier = auroraCluster.ClusterIdentifier;
93100
}
94101

95102
/// <summary>
@@ -127,11 +134,12 @@ public void SetupDevelopmentPostgresDatabase(Config config, StackSetup stackSetu
127134
var DbName = config.Require("dbName");
128135
var DbUsername = config.Require("dbAdmin");
129136
var DbHostName = rdsPostGreInstance.Endpoint;
130-
DBIdentifier = rdsPostGreInstance.Identifier;
131137
var DbPassword = password.Result;
132-
DbConnectionString = Output.Format($"Host={DbHostName};Database={DbName};Username={DbUsername};Password={DbPassword}");
138+
DatabaseConnectionString = Output.Format($"Host={DbHostName};Database={DbName};Username={DbUsername};Password={DbPassword}");
139+
DBIdentifier = rdsPostGreInstance.Identifier;
133140
}
134141

135-
public Output<string> DbConnectionString = default!;
142+
public SecretsManager SecretsManager = default!;
136143
public Output<string> DBIdentifier = default!;
144+
public Output<string> DatabaseConnectionString = default!;
137145
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
using Pulumi;
2+
using VirtualFinland.UsersAPI.Deployment.Common.Models;
3+
using Pulumi.Aws.Iam;
4+
using System.Text.Json;
5+
using System.Collections.Generic;
6+
using Pulumi.Aws.Rds;
7+
using Pulumi.Aws.Rds.Inputs;
8+
using Pulumi.Random;
9+
10+
namespace VirtualFinland.UsersAPI.Deployment.Features;
11+
12+
public class RDSProxy
13+
{
14+
public RDSProxy(Config config, StackSetup stackSetup, PostgresDatabase database)
15+
{
16+
// RDS proxy access secret
17+
var username = new RandomPassword(stackSetup.CreateResourceName("rdsproxy-username"), new()
18+
{
19+
Length = 16,
20+
Special = false,
21+
OverrideSpecial = "_%@",
22+
});
23+
var password = new RandomPassword(stackSetup.CreateResourceName("rdsproxy-password"), new()
24+
{
25+
Length = 16,
26+
Special = false,
27+
OverrideSpecial = "_%@",
28+
});
29+
var rdsProxySecretString = Output.Format($"{{\"username\":\"{username.Result}\",\"password\":\"{password.Result}\"}}");
30+
var rdsProxySecret = new SecretsManager(config, stackSetup, "rdsProxySecret", rdsProxySecretString);
31+
32+
// Create role for rds proxy
33+
var rdsProxyRole = new Role(stackSetup.CreateResourceName("database-proxy-role"), new RoleArgs()
34+
{
35+
AssumeRolePolicy = JsonSerializer.Serialize(new Dictionary<string, object?>
36+
{
37+
{ "Version", "2012-10-17" },
38+
{
39+
"Statement", new[]
40+
{
41+
new Dictionary<string, object?>
42+
{
43+
{ "Action", "sts:AssumeRole" },
44+
{ "Effect", "Allow" },
45+
{ "Sid", "" },
46+
{
47+
"Principal", new Dictionary<string, object?>
48+
{
49+
{ "Service", "rds.amazonaws.com" }
50+
}
51+
}
52+
}
53+
}
54+
}
55+
}),
56+
Tags = stackSetup.Tags
57+
});
58+
59+
new RolePolicyAttachment($"{stackSetup.ProjectName}-RdsProxy-SecretManager-{stackSetup.Environment}", new RolePolicyAttachmentArgs
60+
{
61+
Role = rdsProxyRole.Name,
62+
PolicyArn = rdsProxySecret.Arn
63+
});
64+
65+
// AWS RDS Proxy
66+
var rdsProxy = new Proxy(stackSetup.CreateResourceName("database-proxy"), new()
67+
{
68+
DebugLogging = false,
69+
EngineFamily = "POSTGRESQL",
70+
RequireTls = true,
71+
RoleArn = rdsProxyRole.Arn,
72+
VpcSubnetIds = stackSetup.VpcSetup.PrivateSubnetIds,
73+
VpcSecurityGroupIds = new[] { stackSetup.VpcSetup.SecurityGroupId },
74+
Auths = new[] {
75+
new ProxyAuthArgs
76+
{
77+
AuthScheme = "SECRETS",
78+
Description = "Secrets authentication",
79+
SecretArn = rdsProxySecret.Arn,
80+
IamAuth = "DISABLED"
81+
}
82+
},
83+
Tags = stackSetup.Tags,
84+
});
85+
86+
// RDS Proxy Target
87+
new ProxyTarget(stackSetup.CreateResourceName("database-proxy-target"), new ProxyTargetArgs()
88+
{
89+
DbProxyName = rdsProxy.Name,
90+
DbInstanceIdentifier = database.DBIdentifier,
91+
});
92+
93+
// Set outputs
94+
ProxyEndpoint = rdsProxy.Endpoint;
95+
ProxyIdentifier = rdsProxy.Id;
96+
97+
var DbName = config.Require("dbName");
98+
DatabaseConnectionString = Output.Format($"Host={rdsProxy.Endpoint};Database={DbName};Username={username.Result};Password={password.Result}");
99+
}
100+
101+
[Output]
102+
public Output<string> ProxyEndpoint { get; set; }
103+
public Output<string> ProxyIdentifier { get; set; }
104+
public Output<string> DatabaseConnectionString { get; set; }
105+
}
Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,47 @@
11
using Pulumi;
22
using Pulumi.Aws.SecretsManager;
33
using VirtualFinland.UsersAPI.Deployment.Common.Models;
4+
using Pulumi.Aws.Iam;
45

56
namespace VirtualFinland.UsersAPI.Deployment.Features;
67

7-
/// <summary>
8-
/// Creates the users-api database credentials in AWS Secrets Manager
9-
/// </summary>
108
public class SecretsManager
119
{
12-
public SecretsManager(Config config, StackSetup stackSetup, PostgresDatabase dbConfigs)
10+
public SecretsManager(Config config, StackSetup stackSetup, string secretName, Output<string> secretValue)
1311
{
14-
var secret = new Secret($"{stackSetup.ProjectName}-dbConnectionStringSecret-{stackSetup.Environment}");
15-
new SecretVersion($"{stackSetup.ProjectName}-dbConnectionStringSecretVersion-{stackSetup.Environment}", new()
12+
var secret = new Secret($"{stackSetup.ProjectName}-{secretName}-{stackSetup.Environment}");
13+
new SecretVersion($"{stackSetup.ProjectName}-{secretName}Version-{stackSetup.Environment}", new()
1614
{
1715
SecretId = secret.Id,
18-
SecretString = dbConfigs.DbConnectionString,
16+
SecretString = secretValue,
1917
});
18+
19+
ReadPolicy = new Policy($"{stackSetup.ProjectName}-${secretName}-SecretManagerPolicy-{stackSetup.Environment}", new()
20+
{
21+
Description = "Users-API Secret Get Policy",
22+
PolicyDocument = Output.Format($@"{{
23+
""Version"": ""2012-10-17"",
24+
""Statement"": [
25+
{{
26+
""Effect"": ""Allow"",
27+
""Action"": [
28+
""secretsmanager:GetSecretValue"",
29+
""secretsmanager:DescribeSecret""
30+
],
31+
""Resource"": [
32+
""{secret.Arn}""
33+
]
34+
}}
35+
]
36+
}}"),
37+
Tags = stackSetup.Tags,
38+
});
39+
2040
Name = secret.Name;
2141
Arn = secret.Arn;
2242
}
43+
2344
public Output<string> Name = default!;
2445
public Output<string> Arn = default!;
46+
public Policy ReadPolicy = default!;
2547
}

VirtualFinland.UsersAPI.Deployment/Pulumi.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ config:
99
users-api:infraStackReferenceName: infrastructure
1010
users-api:appArtifactPath: ../VirtualFinland.UserAPI/src/VirtualFinland.UsersAPI/bin/Release/net6.0/VirtualFinland.UsersAPI.zip
1111
users-api:dbMigratorArtifactPath: ../VirtualFinland.UsersAPI.DatabaseMigrationRunner/bin/Release/net6.0/VirtualFinland.UsersAPI.DatabaseMigrationRunner.zip
12+
users-api:useRdsProxy: false

VirtualFinland.UsersAPI.Deployment/UsersAPIStack.cs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Immutable;
33
using System.Linq;
44
using Pulumi;
5+
using Pulumi.Aws.Ec2;
56
using VirtualFinland.UsersAPI.Deployment.Common;
67
using VirtualFinland.UsersAPI.Deployment.Common.Models;
78
using VirtualFinland.UsersAPI.Deployment.Features;
@@ -32,6 +33,12 @@ public UsersApiStack()
3233
}
3334
};
3435

36+
var defaultSecurityGroup = GetSecurityGroup.Invoke(new GetSecurityGroupInvokeArgs()
37+
{
38+
VpcId = Output.Format($"{infraStackReferenceVpcId}"),
39+
Name = "default"
40+
});
41+
3542
var stackSetup = new StackSetup()
3643
{
3744
ProjectName = projectName,
@@ -41,19 +48,20 @@ public UsersApiStack()
4148
VpcSetup = new VpcSetup()
4249
{
4350
VpcId = Output.Format($"{infraStackReferenceVpcId}"),
44-
PrivateSubnetIds = infraStackReferencePrivateSubnetIdsAsList as Output<IEnumerable<string>>
51+
PrivateSubnetIds = infraStackReferencePrivateSubnetIdsAsList as Output<IEnumerable<string>>,
52+
SecurityGroupId = defaultSecurityGroup.Apply(o => o.Id)
4553
}
4654
};
4755

48-
var dbConfigs = new PostgresDatabase(config, stackSetup);
49-
var secretManagerSecret = new SecretsManager(config, stackSetup, dbConfigs);
56+
var database = new PostgresDatabase(config, stackSetup);
57+
var secretsManager = new SecretsManager(config, stackSetup, "dbConnectionStringSecret", database.DatabaseConnectionString);
5058

51-
var lambdaFunctionConfigs = new LambdaFunctionUrl(config, stackSetup, secretManagerSecret);
59+
var lambdaFunctionConfigs = new LambdaFunctionUrl(config, stackSetup, secretsManager);
5260
ApplicationUrl = lambdaFunctionConfigs.ApplicationUrl;
5361
LambdaId = lambdaFunctionConfigs.LambdaFunctionId;
54-
DBIdentifier = dbConfigs.DBIdentifier;
62+
DBIdentifier = database.DBIdentifier;
5563

56-
var databaseMigratorLambda = new DatabaseMigratorLambda(config, stackSetup, secretManagerSecret);
64+
var databaseMigratorLambda = new DatabaseMigratorLambda(config, stackSetup, secretsManager);
5765
DatabaseMigratorLambdaArn = databaseMigratorLambda.LambdaFunctionArn;
5866
}
5967

0 commit comments

Comments
 (0)