Abstract
C#でAWS Secrets Managerをローカルエミュレータ(LocalStack)上で使ってみました。
1. Introduction
1.1. Preparation
-
Install the package
1
|
$ dotnet add package AWSSDK.SecretsManager
|
-
Launch LocalStack (AWS Secrets Manager)
docker-compose.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
|
services:
localstack:
image: localstack/localstack
container_name: "${LOCALSTACK_DOCKER_NAME:-localstack-main}"
ports:
- "127.0.0.1:4566:4566" # LocalStack Gateway
environment:
# LocalStack configuration: https://docs.localstack.cloud/references/configuration/
- SERVICES=secretsmanager # Write the AWS services you want to use, separated by commas
- DEBUG=${DEBUG:-0}
volumes:
- "${LOCALSTACK_VOLUME_DIR:-./volume}:/var/lib/localstack"
- "/var/run/docker.sock:/var/run/docker.sock"
|
1.2. Create a Secret
1
|
$ awslocal secretsmanager create-secret --name my-secret --description "LocalStack Secret" --secret-string file://secrets.json
|
secrets.json
1
2
3
4
|
{
"username": "admin",
"password": "password"
}
|
2. C# Source Code
Program.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
using Amazon.SecretsManager;
using Amazon.SecretsManager.Model;
using Amazon.Runtime;
/// <remarks>
/// The program initializes a connection to the LocalStack instance running on `localhost:XXXX`.
/// Make sure that LocalStack is running and that the secret is created before executing the application.
/// </remarks>
class Program
{
private static readonly string _localstackEndpoint = "http://localhost:4566"; // LocalStackのエンドポイント
static async Task Main(string[] args)
{
// 引数のチェックとシークレット名の取得
if (args.Length < 1)
{
Console.WriteLine("Error: Missing required argument. Please provide the secret name to retrieve.");
return;
}
var secretName = args[0];
// クライアント作成
var config = new AmazonSecretsManagerConfig
{
ServiceURL = _localstackEndpoint,
};
// ダミー認証情報(LocalStackは実際の認証を行いませんが、形式的には必要です)
// IAMロールでAWS Secrets Managerへのアクセス権限が付与されていれば、クレデンシャルは不要
var credentials = new BasicAWSCredentials("dummy-access-key", "dummy-secret-key");
var client = new AmazonSecretsManagerClient(credentials, config);
// Secretの取得処理
try
{
var response = await GetSecretAsync(client, secretName);
if (response is not null)
{
var secret = DecodeString(response);
if (!string.IsNullOrEmpty(secret))
{
Console.WriteLine($"The decoded secret value is: {secret}.");
}
else
{
Console.WriteLine("No secret value was returned.");
}
}
}
catch (AmazonSecretsManagerException ex)
{
Console.WriteLine($"AWS SecretsManager error: {ex.Message}");
Console.WriteLine($"Error code: {ex.ErrorCode}, HTTP status: {ex.StatusCode}");
}
catch (Exception ex)
{
Console.WriteLine($"An unexpected error occurred: {ex.Message}");
Console.WriteLine(ex.StackTrace);
}
finally
{
if (client is IDisposable disposable)
{
disposable.Dispose();
}
}
}
/// <summary>
/// Asynchronously retrieves the value of a secret from AWS Secrets Manager.
/// </summary>
/// <param name="client">The <see cref="IAmazonSecretsManager"/> client used to communicate with the Secrets Manager service.</param>
/// <param name="secretName">The name or ARN of the secret to retrieve from Secrets Manager.</param>
/// <returns>A <see cref="Task"/> that represents the asynchronous operation, containing a <see cref="GetSecretValueResponse"/> that holds the secret value.</returns>
/// <exception cref="AmazonSecretsManagerException">Thrown when the request to Secrets Manager fails.</exception>
/// <exception cref="Exception">Thrown when an unexpected error occurs during the retrieval of the secret.</exception>
static async Task<GetSecretValueResponse> GetSecretAsync(IAmazonSecretsManager client, string secretName)
{
var request = new GetSecretValueRequest
{
SecretId = secretName
};
var response = await client.GetSecretValueAsync(request);
return response;
}
/// <summary>
/// Decodes the secret returned by the call to GetSecretValueAsync and
/// returns it to the calling program.
/// </summary>
/// <param name="response">A GetSecretValueResponse object containing
/// the requested secret value returned by GetSecretValueAsync.</param>
/// <returns>A string representing the decoded secret value.</returns>
public static string DecodeString(GetSecretValueResponse response)
{
// Decrypts secret using the associated AWS Key Management Service
// Customer Master Key (CMK.) Depending on whether the secret is a
// string or binary value, one of these fields will be populated.
if (response.SecretString is not null)
{
var secret = response.SecretString;
return secret;
}
else if (response.SecretBinary is not null)
{
var memoryStream = response.SecretBinary;
StreamReader reader = new StreamReader(memoryStream);
string decodedBinarySecret = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(reader.ReadToEnd()));
return decodedBinarySecret;
}
else
{
return string.Empty;
}
}
}
|
3. Execution Result
1
2
3
4
5
|
❯ dotnet run my-secret
The decoded secret value is: {
"username": "admin",
"password": "password"
}
|
4. Reference