2

我似乎一直坚持使用利用 Microsoft.Configuration.ConfigurationBuilders 的 NETFramework 4.7.1 为 Amazon 的 System Manager Parameter Store (SSM) 开发自定义键/值对提供程序。

实施:

using System;
using System.Collections.Generic;
using Amazon.SimpleSystemsManagement;
using Amazon.SimpleSystemsManagement.Model;
using Microsoft.Configuration.ConfigurationBuilders;
using System.Linq;
using System.Diagnostics;
using System.Collections.Specialized;
using Amazon.Runtime;
using Amazon.Runtime.CredentialManagement;
using System.Configuration;
using System.Threading.Tasks;

namespace AXS.Configurations
{
    public class ParameterStoreConfigBuilder : KeyValueConfigBuilder
    {
        public const string envTag = "Environment";
        public const string appNameTag = "AppName";

        private IAmazonSimpleSystemsManagement client;
        /// <summary>
        /// Gets or sets an environment (dev|qa|staging|production)
        /// </summary>
        public string Environment { get; set; }
        /// <summary>
        /// Gets or sets a AppName 
        /// </summary>
        public string AppName { get; set; }

        public ParameterStoreConfigBuilder(IAmazonSimpleSystemsManagement client,
            string appName,
            string environment)
        {
            this.client = client;
            Environment = environment.ToLower();
            AppName = appName;
        }
        public ParameterStoreConfigBuilder()
        {
            client = new AmazonSimpleSystemsManagementClient();
        }

        public override string Description => "Parameter Store";
        public override string Name => "SSM";

        protected override void LazyInitialize(string name, NameValueCollection config)
        {
            Optional = false;
            base.LazyInitialize(name, config);
            string env = UpdateConfigSettingWithAppSettings(envTag);
            if (string.IsNullOrWhiteSpace(env))
                throw new ArgumentException($"environment must be specified with the '{envTag}' attribute.");
            Environment = env;
            string appName = UpdateConfigSettingWithAppSettings(appNameTag);
            if (string.IsNullOrWhiteSpace(appName))
                throw new ArgumentException($"appName must be specified with the '{appNameTag}' attribute.");
            AppName = appName;
            client = new AmazonSimpleSystemsManagementClient("","", Amazon.RegionEndpoint.USWest2);

        }
        public override ICollection<KeyValuePair<string, string>> GetAllValues(string prefix)
        {
            Trace.TraceInformation($"return values prefix {prefix}");
            if (client == null)
                return null;
            var parameters = new List<Parameter>();
            string nextToken = null;
            do
            {
                var response = client.GetParametersByPath(new GetParametersByPathRequest { Path = prefix, Recursive = true, WithDecryption = true, NextToken = nextToken });
                nextToken = response.NextToken;
                parameters.AddRange(response.Parameters);
            } while (!string.IsNullOrEmpty(nextToken));
            return parameters.Select(p => new
            {
                Key = p.Name,
                p.Value
            }).ToDictionary(parameter => parameter.Key, parameter => parameter.Value, StringComparer.OrdinalIgnoreCase);
        }

        public override string GetValue(string key)
        {
            return Task.Run(async () => { return await GetValueAsync(key); }).Result;
        }
        private async Task<string> GetValueAsync(string key)
        {
            var name = $"/{Environment}/{AppName}/{key.Replace(':', '/')}";
            Trace.WriteLine($"get value async:{name}");
            if (client == null)
                return null;

            try
            {
                Trace.TraceInformation($"fetch key {name}");
                var request = new GetParameterRequest
                {
                    Name = name,
                    WithDecryption = true
                };
                var response = await client.GetParameterAsync(request);
                var parameter = response.Parameter;
                var value = parameter.Type == ParameterType.SecureString ? "*****" : parameter.Value;
                Trace.TraceInformation($"fetched name={name} value={value}");
                return value;
            }
            catch (Exception e) when (Optional && ((e.InnerException is System.Net.Http.HttpRequestException) || (e.InnerException is UnauthorizedAccessException))) { }

            return null;
        }
    }
}

问题似乎是 AWS SSMclient永远不会被创建。如果我更改代码并尝试在构造函数中实例化,我会由于递归而得到堆栈溢出异常。

关于如何强制创建 AmazonSimpleSystemsManagementClient 的任何想法?

该代码使用来自https://github.com/aspnet/MicrosoftConfigurationBuilders的指导

App.Config

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="configBuilders" type="System.Configuration.ConfigurationBuildersSection, 
             System.Configuration, Version=4.0.0.0, Culture=neutral, 
             PublicKeyToken=b03f5f7f11d50a3a" 
             restartOnExternalChanges="false" 
             requirePermission="true" />
  </configSections>
  <configBuilders>
    <builders>
      <add name="ParameterStore" Environment="development" AppName="myAppNameforParmStore" type="AXS.Configurations.ParameterStoreConfigBuilder, AXS.Configurations" />
      <add name="Env" prefix="appsettings_" stripPrefix="true" type="Microsoft.Configuration.ConfigurationBuilders.EnvironmentConfigBuilder, Microsoft.Configuration.ConfigurationBuilders.Environment, Version=2.0.0.0, Culture=neutral" />
    </builders>
  </configBuilders>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.1" />
  </startup>
  <appSettings configBuilders="Env,ParameterStore">
    <add key="Url" value="URL Value for from paramter Store" />
    <add key="Secret" value="Some Secret value decrypted" />
  </appSettings>
</configuration>

谢谢

4

1 回答 1

5

更新 我在我的 GitHub 上发布了 AwsSsmConfigurationBuilder 的更新版本和一个使用它的示例 ASP.NET Web 窗体项目: https ://github.com/Kirkaiya/AwsSsmConfigBuilderPoC/

免责声明: 这是针对 ASP.NET 4.7.1 或更高版本(显然在 .NET Framework 上运行)的自定义 ConfigurationBuilder 的概念验证 (POC)。它是一个 POC,因此除了允许您将 Configuration AppSettings 存储在 AWS Parameter Store(Simple Systems Manager 的一项功能)中之外,它什么也不做。所以,很明显,不要在没有产品化和测试的情况下在生产中使用它!

先决条件:

  • 您的项目必须面向 .NET Framework 4.7.1 或更高版本
  • 包括 NuGet 包Microsoft.Configuration.ConfigurationBuilders.Base
  • AWS SSM Parameter Store 中的参数与 web.config 文件中的参数具有相同的名称(不包括前缀),反之亦然。

备注 为了避免递归调用具体的构造函数或 Initialize,我使用静态构造函数来实例化 AmazonSimpleSystemsManagementClient,它保存在静态成员中。

Web.Config 添加 注意:更改您的构建器的程序集/类名以匹配您的等

<configSections>
  <section name="configBuilders" type="System.Configuration.ConfigurationBuildersSection, System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" restartOnExternalChanges="false" requirePermission="false" />
</configSections>

<configBuilders>
  <builders>
    <add name="ParameterStore" ssmPrefix="/padnugapp/ApiKeys" type="Microsoft.Configuration.ConfigurationBuilders.AwsSsmConfigBuilder, AspNetWebFormsSample" />
  </builders>
</configBuilders>

<appSettings configBuilders="ParameterStore">
  <add key="TestKey" value="TestKey Value from web.config" /> 
  <add key="TwitterKey" value="TwitterKey value from web.config" />
</appSettings>

和 AwsSsmConfigBuilder.cs 文件:

namespace Microsoft.Configuration.ConfigurationBuilders
{
    public class AwsSsmConfigBuilder : KeyValueConfigBuilder
    {
        private string BaseParameterPath = "/padnugapp/ApiKeys";
        private static IAmazonSimpleSystemsManagement _client;

        static AwsSsmConfigBuilder()
        {
            _client = new AmazonSimpleSystemsManagementClient();
        }

        public override void Initialize(string name, NameValueCollection config)
        {
            base.Initialize(name, config);

            if (config["ssmPrefix"] == null)
                return;

            BaseParameterPath = config["ssmPrefix"];
        }

        public override ICollection<KeyValuePair<string, string>> GetAllValues(string prefix)
        {
            if (_client == null)
                return null;

            var request = new GetParametersByPathRequest
            {
                Path = $"{BaseParameterPath}/{prefix}",
                WithDecryption = true,
            };

            var response = _client.GetParametersByPathAsync(request).Result;

            var result = response.Parameters.ToDictionary(param => param.Name, param => param.Value, StringComparer.OrdinalIgnoreCase);

            return result;
        }

        public override string GetValue(string key)
        {
            if (_client == null)
                return null;

            var request = new GetParameterRequest
            {
                Name = $"{BaseParameterPath}/{key}",
                WithDecryption = true, 
            };

            var response = _client.GetParameterAsync(request).Result;

            return response.Parameter.Value;
        }
    }
}

我放入 Web 表单 (.aspx) 页面的代码以 HTML 格式呈现两个 appSettings 项:

TestKey = 
    <%=(System.Configuration.ConfigurationManager.AppSettings["TestKey"]) %>
    <br />
    TwitterKey = 
    <%=(System.Configuration.ConfigurationManager.AppSettings["TwitterKey"]) %>

我不能强调这只是为了我正在做的演示,除了在我的笔记本电脑上之外,没有以任何方式、形状或形式进行测试;-)

于 2019-12-26T20:35:47.220 回答