C# AWS Parameter Store - Configuration not loading SystemManagerConfiguration in .Net 6 - c#

Application is not able to talk to AWS Parameter Store in .NET 6.
It is always talking to appsettings.json.
I tried debugging locally, still same behavior. Not able to find the SystemManagerConfiguration under list of configuration .
var builder = WebApplication.CreateBuilder();
var connectionString = builder.Configuration.GetConnectionString("OrderTrackerDatabase");
Packages Used
Library Source Code : https://github.com/aws/aws-dotnet-extensions-configuration
image

I got the same issue and finally resolved it.
The samples code in https://github.com/aws/aws-dotnet-extensions-configuration missed one line as below after called "AddSystemsManager" method in .Net 6.
builder.Services.Configure<Settings>(builder.Configuration.GetSection($"common:settings"));
After added above line, then I'm able to get the correct values from AWS Parameter Store when using the settings.
I've also created an issue of this in GitHub as below -
https://github.com/aws/aws-dotnet-extensions-configuration/issues/114

I believe the problem might be the trailing slash after "/OrderTracking/", try "/OrderTracking" instead.

WebApplication.CreateBuilder() will create new instance and doesn't carry over the SystemManager configuration.
Instead, use IConfiguration instance through constructor DI.
var connectionString = _configuration.GetConnectionString("OrderTrackerDatabase");

In my case this extensions method was returning null at my lambda:
private static IConfiguration InitializeConfiguration() => new ConfigurationBuilder()
.AddSystemsManager($"/my-data", true, TimeSpan.FromMinutes(5))
.Build();
Because the role of lambda didn't have permission for read SSM for that resource.
User: is not authorized to perform: ssm:GetParametersByPath on resource
So, just add necessary permission (ssm:GetParametersByPath) for the role of lambda to your resource at System Manager.

In my case, I am using lambda serverless, so the IConfig is always null when it is passed to the controller.
I resolved it by changing the IOptions<Settings> settings in the Controller constructor to IConfiguration settings and then access the parameters by name like _settings.GetValue<string>("ParameterName")
A little less "Object Oriented", but it seemed much easier than this complex solution

Related

Inject AddSqlServer with another service

Is it possible to resolve an instance of ISettingsService from the ConfigureServices method in Startup(.cs) - webapplication?
I've implemented a SettingsService which is able to retrieve the database connectionstring from an external source (secure credentials store). Within the ConfigureServices I need an instance of the ISettingsService in order to get the connectionstring and pass it to the services.AddSqlServer<MyDbContext>(connectionstring) method.
While creating the instance (using var provider = services.BuildServiceProvider(); var settings = provider.GetService<ISettingsProvider>();) Visual Studio displays the next error:
Another developer posted a similar question on StackOverflow and the answer provides a solution in case of AddSingleton/ AddTransient. What is the correct way to apply it on the AddSqlServer call? Or could you provide another solution to avoid the warning/ error message?
The Intellisense comment for .AddSqlServer actually says to use .AddDbContext if you need more control, and that's certainly the correct option.
If you refer to the source code here, you can see that all .AddSqlServer is actually doing is calling .AddDbContext and configuring the options accordingly. We can therefore write our own solution like this:
services.AddDbContext<DbContext>((serviceProvider, options) => {
var settings = serviceProvider.GetService<ISettingsProvider>();
// I don't know what methods your ISettingsProvider makes available
// so adjust accordingly
string connectionString = settings.GetSetting("connectionString");
options.UseSqlServer(connectionString);
});
Of course you can make other changes to the options here, and .UseSqlServer can also take a Action<SqlServerDbContextOptionsBuilder> (options.UseSqlServer(connectionString, opts => opts.EnableRetryOnFailure()), etc.) to further configure it.

Turn `ReloadOnChange` off in config source for WebApplicationFactory

This is both a question and an answer. I've fixed my problem, but it seems a bit wrong.
My original problem is running my asp.net core integration tests in a bitbucket pipeline causes System.IO.IOException: The configured user limit (128) on the number of inotify instances has been reached. Some solutions call for changing some setting through sysctl, but that is restricted by bitbucket, so that isn't an option for me.
The second way of fixing this, as noted in these stackoverflow answers, is to turn reloadOnChange off.
My new problem is now, how do we best do this for the test WebApplicationFactory?
One solution that has worked for me, which is the least amount of code, seems like a total hack. I iterate through all the JsonConfigurationSource and set ReloadOnChange to false.
Full solution:
public class TestApplicationFactory : WebApplicationFactory<Startup>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureAppConfiguration(config =>
{
foreach (var source in config.Sources)
{
if (source is JsonConfigurationSource)
{
var jsonConfigSource = (JsonConfigurationSource) source;
jsonConfigSource.ReloadOnChange = false;
}
}
});
}
}
Another solution, that I haven't tried, may be to override CreateWebHostBuilder(). However, it seems like more code and a lot of copy and paste from the default one.
Am I missing something? Is there a better way to do this?
Just experienced this issue myself running integration tests within a Linux container and followed the previous suggestions to switch off the ReloadOnChange within the WebApplicationFactory. Unfortunately that did not resolve the problem and integration tests were still failing with the same error:
System.IO.IOException: The configured user limit (128) on the number of inotify instances has been reached.
I also tried to configure xUnit to run the integration tests sequentially rather than in parallel, but that did not work either.
The solution that did work for me was to set the appropriate environment variable within the container that runs the integration tests:
export ASPNETCORE_hostBuilder__reloadConfigOnChange=false
builder.ConfigureAppConfiguration is there to configure your (main) application.
You can use builder.ConfigureHostConfiguration (see docs) to explicitly configure files to be read for the host.
builder.ConfigureHostConfiguration((hostingContext, config) =>
{
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: false);
});
The host configuration is loaded. ASP.NET Core from 3.0 is built based on Generic host (rather than the Web Host of the former versions).
You can do this without inheriting from WebApplicationFactory by using the WithWebHostBuilder and ConfigureAppConfiguration extension methods:
var webAppFactory = new WebApplicationFactory<Startup>().WithWebHostBuilder(webHostBuilder =>
{
webHostBuilder.ConfigureAppConfiguration((hostingContext, configBuilder) =>
configBuilder.Sources.Where(s => s is FileConfigurationSource).ToList()
.ForEach(s => ((FileConfigurationSource)s).ReloadOnChange = false));
});
This accomplishes the same thing as your original idea (which helped me a lot!), but more compact and without the need for a separate class definition.
I just encountered the same issue.
Setting the env variable DOTNET_hostBuilder:reloadConfigOnChange to false fixed it.
This solution works for net6 when you use the Generic Host. For other hosts, maybe try replacing DOTNET_ prefix with ASPNETCORE_
To make it simple, I set it in my code before creating the WebApplicationFactory
Environment.SetEnvironmentVariable("DOTNET_hostBuilder:reloadConfigOnChange", "false");

ASP.NET Core access service in Startup.cs ConfigureServices method

I need to access a service inside ConfigureServices method in Startup.cs and I do this:
services.AddScoped<ICustomService, CustomService>();
var sp = services.BuildServiceProvider();
var service = sp.GetService<ICustomService>(); // this is null
However var service above is always null.
What do i do wrong?
I had this sort of problem - I had a singleton 'settings' service which I wanted to use. I solved it by Actually creating one then registering that exact instance with DI via the overload that lets you specify a 'provider', rather than just registering the class, and adding a nice big comment explaining this:
var settingsService = new SettingsService(_hostingEnvironment);
//Add a concrete settings service which is then registered as the de facto settings service for all time.
//we need to do this as we want to use the settings in this method, and there isn't a satisfactory method to
//pull it back out of the IServiceCollection here (we could build a provider, but then that's not the same provider
//as would be build later... at least this way I have the exact class I'll be using.
services.AddSingleton<ISettingsService, SettingsService>((p) => settingsService);
..
..
..
var thing = settingsService.SomeSettingIWant();
If what you want isn't a singleton but is something transient, then I guess you can just create a concrete class for it right there? I know it probably feels a bit like cheating, but it would work fine...

Getting started with Google Adword .net API

Most things that I've found about the .net AdWords API mention creating a bunch of settings for the application that will be used when you create an instance of the AdWordsUser class; however, I've had no luck with this. My user always ends up nil and no settings are being read even though I named them as indicated in the AdWordsAppConfig class.
AdWordsAppConfig appConfig = new AdWordsAppConfig();
appConfig.AdWordsApiServer = "https://adwords-sandbox.google.com";
appConfig.Email = "fname.lname#gmail.com";
appConfig.Password = "password";
appConfig.DeveloperToken = "fname.lname#gmail.com++USD";
AdWordsUser user = new AdWordsUser();
The AdWordsUser has an overloaded constructor that accepts a dictionary of strings containing configuration parameters but doesn't have on that accepts an AdWordsAppConfig object.
http://code.google.com/p/google-api-adwords-dotnet/source/browse/trunk/src/AdWords/Lib/AdWordsAppConfig.cs
According to the AdWordsAppConfig class the following is used to retreive the settings when you call the constructor; however, how do you create a section called "AdWordsApi" in the App.Config? I don't see that option in the settings editor.
ReadSettings((Hashtable) ConfigurationManager.GetSection("AdWordsApi"));
Any ideas on the proper way to set the configuration information and create the user instance that I need?
What Eric mentioned is the long-term solution, but if anyone is looking for a quick answer, this is how you do it:
AdWordsUser user = new AdWordsUser();
(user.Config as AdWordsAppConfig).Password = "XXXXX";
//TODO (Add more configuration settings here.
CampaignService campaignService = (CampaignService) user.GetService(AdWordsService.v201109.CampaignService);
//TODO (Add your code here to use the service.)
Cheers,
Anash
You may want to post this question on the official AdWords API forum, as it is monitored by the current maintainers of that library:
http://code.google.com/apis/adwords/forum.html

Custom MembershipProvider Initialize method

When overriding the MembershipProvider and calling it directly, is there a way to fill the NameValueCollection config parameter of the Initialize method without manually looking through the config file for the settings?
Obviously this Initialize is being called by asp.net and the config is being filled somewhere. I have implemented my own MembershipProvider and it works fine through the build in controls.
I would like to create a new instance of my provider and make a call to it directly, but I don't really want to parse the .config for the MembershipProvider, it's connection string name and then the connection string if it's already being done somewhere.
tvanfosson- Thanks for the help. (if I had the 15 points necessary I would vote you up)
From your link I was able to figure it out. It turns out the second parameter to the Initialize proceedure was the list of parameters from the provider and could be reached in the following way:
string configPath = "~/web.config";
Configuration config = WebConfigurationManager.OpenWebConfiguration(configPath);
MembershipSection section = (MembershipSection)config.GetSection("system.web/membership");
ProviderSettingsCollection settings = section.Providers;
NameValueCollection membershipParams = settings[section.DefaultProvider].Parameters;
Initialize(section.DefaultProvider, membershipParams);
Not sure why you want to create a new one, but if you create it yourself, you'll need to read the web config and get the values yourself to pass to Initialize() as this is done outside the class. I'm sure, though, that there is already a section handler for this section so it should be just a matter of doing:
MembershipSection section = WebConfigurationManager.GetSection("membership");
Then find your provider and accessing its properties to construct the NameValueCollection. I don't think you will have to write any code to parse the configuration section.
Here is the MembershipSection documentation at MSDN. Drill down from there.
In any case you shouldn't create instance of MembershipProvider. It is creating and initializating by standard asp.net infrastructure. You can access to it by code like this one:
var customerMembership = Membership.Provider;

Categories