34

I am trying to test a .NET core console program to publish a message to SNS. As I had issues trying to get it to work in Lambda, I want to try it in a non-Lambda environment. In Lambda, security is covered by the role, but in a console program, I presume that I have to specify my access key and secret somehow.

I've read this page: http://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/net-dg-config-creds.html#net-dg-config-creds-sdk-store, but still totally confused.

I'm running on my local development computer, not an EC2 instance. No intent to go to production with this, just trying to test some code.

I'm on Visual Studio 2015, .NET Core 1.0. I've used Nuget to get the following: "AWSSDK.Extensions.NETCore.Setup": "3.3.3", "AWSSDK.SimpleNotificationService": "3.3.0.23",

Based on the answer to How to set credentials on AWS SDK on NET Core? I created the /user/.aws/credentials file (assuming credentials was the file name and not the directory name).

But that question/answer doesn't address how to actually use this file. The code I'm running is below.

    public static void Main(string[] args)
    {
        Console.WriteLine("Started");
        //var awsCredentials = new Amazon.Runtime.AWSCredentials()
        var client = new Amazon.SimpleNotificationService.AmazonSimpleNotificationServiceClient(Amazon.RegionEndpoint.EUWest2);
        //var client = new Amazon.SimpleNotificationService.AmazonSimpleNotificationServiceClient(awsCredentials, Amazon.RegionEndpoint.EUWest2);
        //Amazon.SimpleNotificationService.Model.PublishResponse publishResp = null;
        SendMessage(client).Wait();
        Console.WriteLine("Completed call to SendMessage: Press enter to end:");
        Console.ReadLine(); 
    }

The error I'm getting on the new client is:

An unhandled exception of type 'Amazon.Runtime.AmazonServiceException' occurred in AWSSDK.Core.dll

Additional information: Unable to find credentials

I see there is a way to pass an AWSCredentials object to that constructor, but I don't understand how to build it. Amazon.Runtime.AWSCredentials is an abstract class, so I can't use it in a "new" statement.

NealWalters
  • 14,090
  • 34
  • 109
  • 199
  • 7
    To downvoter: please give a way to improve the OP's question instead of downvoting and saying nothing – Dan Nov 05 '17 at 18:24
  • By default the Amazon SDKs will use the standard locations for credentials if they are setup. The easiest way to setup default credentials is to install the AWS CLI. Try this command from your command prompt: "aws sns list-topics". If this fails, then install the AWS Command Line Interface https://aws.amazon.com/cli/ and setup your credentials. – John Hanley Nov 05 '17 at 18:25
  • 1
    @Dan Pantry. I agree. We should be helping each other improve questions and answers not only for the current thread but also for the built up knowledge base. Then we could also vote on the comments that are part of the downvote. – John Hanley Nov 05 '17 at 18:30
  • I have used amazon-cli before. So If I run the .NET console program from there, then it will 'inherit' those credentials? I was just trying to run in Visual Studio. – NealWalters Nov 05 '17 at 21:30

4 Answers4

47

Based on Dan Pantry's answer, here is a simple short answer with code highlighted (note the region enum in the second line):

var awsCredentials = new Amazon.Runtime.BasicAWSCredentials("myaccesskey", "mysecretkey"); 
var client = new Amazon.SimpleNotificationService.AmazonSimpleNotificationSer‌​viceClient(
                              awsCreden‌​tials, Amazon.RegionEndpoint.EUWest2);

Use a role if possible, but above works when needed. Then the question is where to store the access key/secret key; could be environment variable, config file, prompt the user, or any of the usual suspects.

AWS-CLI and Python use credentials from here: c:\Users\username\.aws\credentials, so the C# could just read that file so as not to put the codes in the C# program itself. But then each user/developer that runs the program would need to set their credentials there.

There is also now a concept of running Lambda on your local machine, but I haven't tried it yet: https://dzone.com/articles/run-aws-lambda-functions-locally-on-windows-machin#:~:text=Step%201%3A%20Download%20SAM%20local,version%20with%20the%20command%20below.&text=Step%203%3A%20Write%20your%20lambda,yaml%20on%20the%20root%20level. So the point is that if you are going to do Lambda, but you need to test locally first, this would probably be worth trying.

NealWalters
  • 14,090
  • 34
  • 109
  • 199
  • I am having similar issue that my credentials are not picked up from my appsettings in .net core. I am using dynamo db for my access. I had created my post at:https://stackoverflow.com/questions/53176420/net-core-2-0-amazonserviceexception-unable-to-find-credentials Would you be able to check how can I set it up there. – aman Nov 07 '18 at 13:46
  • This is really not the right answer, explicitly passing the credentials to the SDK is not the recommended way and only provides an override. What you really want to do is to have the credentials loaded by the credentials provider, which means they first need to be registered. – Warren Parad May 07 '19 at 15:43
  • You are welcome to put in a better answer. Also note that I stated I was running a .NET console program in a TEST environment (my home computer), not running under Lambda... – NealWalters May 07 '19 at 17:05
  • Thanks Neal (still a useful answer) BasicAWSCredentials really should be highlighted - but is not by Amazon. Due obviously to there (overengineered?) way of saving the creds with their SDK. I am using .Net Core, have values stored in a secret file. Then the values get injected into settings which are then used by the function that is creating the client. I don't need the magic SDK credentials overhead – drewid Jan 25 '21 at 05:55
  • I was just thinking, AWS-CLI and Python use credentials from here: c:\Users\username\.aws\credentials, so the C# could just read that file so as not to put the codes in the C# program itself. But the main point of my answer was to show the syntax, not how and where to store the codes. – NealWalters Jan 25 '21 at 15:04
30

You'll want to construct one of its child classes instead of the abstract one. You can take a look at the class hierarchy here.

For posterity, the options are:

  • AnonymousAWSCredentials - Authenticates as an anonymous user.
  • BasicAWSCredentials - You provide your credentials to the class constructor directly.
  • EnvironmentAWSCredentials - Credentials are pulled from the environment variables of the running executable.
  • InstanceProfileAWSCredentials - Pulls credentials from the Instance Profile of the EC2 instance running the executable. This, obviously, only works on EC2.
  • SessionAWSCredentials - Similar to BasicAWSCredentials, except utilises an AWS Session using a temporary session token from AWS STS.
  • RefreshingSessionAWSCredentials - Similar to SessionAWSCredentials, but refreshes when the STS token expires.

Note that the default strategy in the absence of a credentials object involves checking the Environment Variables and then the instance profile.

If you want to have the program pull credentials from ~/.aws/credentials, you'll need to do some legwork. There used to be a StoredProfileAWSCredentials class, but that appears to have been removed - you can find more information by looking at this github issue. This is only useful, really, in development as you won't be using ~/.aws/credentials in production but probably instance profiles - I'd suggest instead using the default strategy and using Environment AWS credentials in test or development environments.

I take this approach at work since we use a command line tool to grab us limited time tokens from AWS STS and plunk them into the current shell for use for the next hour.

EDIT: It appears you're using AWS Lambda. These have federated access to AWS resources based on the roles assigned to them, so this should work using the default credential strategy in the aws-sdk library which uses instance profiles. So this is only really necessary for development/testing, in which case I would again recommend just using environment variables.

NealWalters
  • 14,090
  • 34
  • 109
  • 199
Dan
  • 9,064
  • 1
  • 29
  • 59
  • 1
    No, the question was not for Lambda. I stated I was using Lambda and had a issue, and wanted to perform the same function in a console .NET core program. I'm trying to isolate if the async/MoveNext error I was getting was related to Lambda or not. So I will try one of the classes you provided. – NealWalters Nov 05 '17 at 21:28
  • 1
    This worked. Got me past the credentials issues on to what I was there to try to test: var awsCredentials = new Amazon.Runtime.BasicAWSCredentials("myaccesskey", "mysecretkey"); var client = new Amazon.SimpleNotificationService.AmazonSimpleNotificationServiceClient(awsCredentials, Amazon.RegionEndpoint.EUWest2); – NealWalters Nov 05 '17 at 21:37
  • Ah okay, my mistake. Sorry, I saw the code example and ran with it! – Dan Nov 06 '17 at 13:58
  • 1
    Update: today (2020, AWSSDK.Core, Version=3.3.0.0) the environment variables credentials class is apparently `EnvironmentVariablesAWSCredentials` –  Jun 29 '20 at 14:08
23

This is a really old question, and the existing answers work, but I really don't like hard-coding my Access Key Id and Secret Key values directly into source code, even for throw-away projects I'm doing on my local machine. For one thing, I might revoke those keys in the future, so I want to leverage the credentials in my .aws\credentials file.

To do that for my .NET core apps (including console apps, etc), I first add two NuGet packages:

  • Microsoft.Extensions.Configuration.Json
  • AWSSDK.Extensions.NETCore.Setup

Then, I add an applications.json file to my project, which contains the following (note - you need to right-click the file, and set "Copy to output" as either "copy if newer" or "always"):

{
  "AWS": {
    "Profile": "default",
    "ProfilesLocation": "C:\\Users\\my-user-profile-folder\\.aws\\credentials",
    "Region": "us-west-2"
  }
}

Finally, I create an instance of the AWS SDK client using the following:

var builder = new ConfigurationBuilder().AddJsonFile("appsettings.Development.json", optional: false, reloadOnChange: true);
var options = builder.Build().GetAWSOptions();
var s3client = options.CreateServiceClient<IAmazonS3>();

This way, if I update my credentials file, I'm fine. Or if my code gets zipped up and emailed to a friend or co-worker, I don't accidentally send them my credentials also.

There is another way to do this, without needing to add the NuGet packages also, which many people might prefer. You can use the new SharedCredentialsFile class and AWSCredentialsFactory, like this (using the "default" profile here, and assumes your credential file is in the default location, same as the other method):

var sharedFile = new SharedCredentialsFile();
sharedFile.TryGetProfile("default", out var profile);
AWSCredentialsFactory.TryGetAWSCredentials(profile, sharedFile, out var credentials);

var s3Client = new AmazonS3Client(credentials);

Note - I'm not checking that the two Try* methods are succeeding here, which you probably should do. Details on using these classes are here: https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/net-dg-config-creds.html#how-to-create-an-amazons3client-using-the-sharedcredentialsfile-class

Kirkaiya
  • 885
  • 8
  • 12
  • Thanks for this. I have a question though, about the profile location what if this is being hosted in cloud. I mean does that change anything? – Silly Volley Jul 30 '19 at 11:18
  • 1
    If your code is running in AWS, the SDKs will fetch temp credentials from the IAM role on the EC2 instance, container, or Lambda function the code is running in (assuming you are using IAM roles). And you should - don't put your keys on a file in the code execution environment, that's inherently less secure. – Kirkaiya Oct 07 '19 at 21:52
  • Just a note to those like myself who end up here. From the AWS docs: "SDK Store profiles are specific to a particular user on a particular host. For this reason, you cannot use SDK Store profiles in production applications. For more information, see https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/net-dg-config-creds.html#creds-assign" – Reahreic Jun 05 '20 at 16:26
  • Sure, but the original question asked was for testing out a .NET Core console application in a non-lambda environment, which presumably means testing on the user's local dev machine. For production applications deployed in AWS or on on-premise VMs instances managed by AWS Systems Manager (which relies on an agent), you would use IAM profiles attached to the code execution environment (EC2, ECS/Fargate container, Lambda, CodeBuild container etc). – Kirkaiya Jun 08 '20 at 01:49
2

For those struggling with profile names, here is where you can find it.

Contents of your ~/.aws/credentials:

[YOUR_PROFILE_NAME]
aws_access_key_id = ***
aws_secret_access_key = ***
aws_security_token = ***
aws_session_expiration = ***
aws_session_token = ***

So then in your application you access the credentials like this:

var chain = new CredentialProfileStoreChain();
var result = chain.TryGetAWSCredentials("YOUR_PROFILE_NAME", out var credentials);

Resources:

Piotr Justyna
  • 4,310
  • 3
  • 22
  • 40
  • 1
    Once you have two clients and you use the same computer for both, this is critical. So best to use the profile from the beginning. – NealWalters May 12 '21 at 16:50