Update SSM param from lambda running in another AWS account? - amazon-web-services

I have a lambda in AWS Account 1.
I have an SSM parameter in AWS Account 2.
I want to write code in the lambda like this:
const ssm = new AWS.SSM({apiVersion: "2014-11-06"});
function updateSsmParamInOtherAwsAccount(newSsmValue) {
const params = {
Name: "ssm-parameter-name",
Value: newSsmValue,
Overwrite: true,
Type: "String",
};
ssm.putParameter(params, function (err) {
if (err) {
console.error(
err,
err.stack
);
} else {
console.log(
`Successfully set SSM param`
);
}
});
}
I understand how to use IAM to grant permissions for this.
What I don't understand is how to configure the aws javascript sdk to point to the other AWS account.
As written in the example above, it will update the SSM param in the same AWS account the lambda is running in. There don't seem to be any environment variables or configuration options that allow this.
Anyone know how to connect the dots here?

Since you understand the use of a cross-account IAM role, as you wrote, I guess that all the roles and permissions are correctly setup.
So what you have to do is same for all SDKs, not only JavaScript. Namely, in your lambda function you have to explicitly assume the IAM role from AWS Account 2. In JS, you have to call assumeRole and provide it with the IAM role from the second account.
The assumeRole call will return temporary AWS credentials which you can use to access the second Account. In this SO answer is a good example how you can do this in JS. Other example, specific to lambda (but for python) is here.

Related

Executing AWS Lambda with assumed role with web identity

Is it possible for Lambda to be executed via API Gateway with an assumed role on behalf of a Cognito authenticated user?
Right now I'm doing the role assumption manually, from within the Lambda code:
const assumedRole = await sts.send(new AssumeRoleWithWebIdentityCommand({
RoleArn: 'some role ARN',
RoleSessionName: event.requestContext.authorizer.jwt.claims['cognito:username'],
WebIdentityToken: event.headers.authorization
}));
But I would like to avoid that, and have the Lambda be executed with this assumed role already.
What I am having difficulty with is figuring out what IAM Role and policy is required to achieve that.
Thanks!
You can pass cognito token from header of your request to the lambda and then make a get get credentials for identity call. That would be the best approach for assuming role of your cognito user dynamically. If you have configured cognito to grant role based on token you don't even need to explicitely provide a role arn in the call.

An error occurred (AuthFailure) AWS was not able to validate the provided access credentials: ClientError from lambda function

I am trying to run the below command from lambda function but I keep getting the error AWS was not able to validate the provided access credentials. I am very sure that the credentials are correct because the access credentials are the set of credentials I use in my local AWS CLI. Does anyone have any idea?
ec2 = boto3.client('ec2', region_name=str(REGION_NAME), aws_access_key_id=str(ACCESS_KEY), aws_secret_access_key=str(SECRET_KEY))
When you are using lambda function, its permissions should be provided using AWS Lambda execution role:
An AWS Lambda function's execution role grants it permission to access AWS services and resources. You provide this role when you create a function, and Lambda assumes the role when your function is invoked.
I know it does not answer directly to your question, but this is good practice and you should not hard code any IAM credentials in your lambda function or its environmental variables.

Temporary Security Credentials - How to get access, given a role name & AWS account id?

AWS documentation provides multiple approach, here.
In our environment, we have multiple AWS accounts.
Usage scenario is to switch between AWS accounts and run AWS cli commands from laptop, as part of automation.
Before running AWS cli command on a specific AWS account, we need to get temporary credentials for that account, given account id.
Getting access to(by switching over) multiple AWS accounts, helps us in automation.
Basically we would like to run some tool like ./some_aws_sdk_tool.py role_name aws_account_name, assuming a role_name to get credentials.
I want to test this with my single AWS account(personal).
1) What are the steps to configure my AWS account to create such role_name?
2)
What is the approach to get temporary credentials to a specific aws_account_name with some_aws_sdk_tool.py? to be able run AWS CLI commands for n minutes..
Do you want to do this from within your code or using the AWS CLI?
If you're using the CLI, the easiest way is to create profiles in your AWS credentials file, as described here. Each profile identifies a role ARN and the source login information that is allowed to assume that role.
Alternatively, you can run the sts assume-role command, parse the results, and set environment variables:
aws sts assume-role --role-arn 'arn:aws:iam::123456789012:role/Example' --role-session-name 'some_unique_but_relevant_string'
{
"Credentials": {
"AccessKeyId": "ASIAXXXXXXXXXXXXXXXX",
"SecretAccessKey": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"SessionToken": "XXXXXXXXXXXXXXX//////////XXX...XXXXX",
"Expiration": "2020-03-05T20:57:45Z"
},
"AssumedRoleUser": {
"AssumedRoleId": "AROAXXXXXXXXXXXXXXXXX:some_unique_but_relevant_string",
"Arn": "arn:aws:sts::123456789012:assumed-role/Example/some_unique_but_relevant_string"
}
}
If you want to do it from within your program, you can use code like this:
sts_client = boto3.client('sts')
role_arn = "..."
session_name = "some_unique_but_relevant_value"
response = sts_client.assume_role(
RoleArn=role_arn,
RoleSessionName=session_name
)
creds = response['Credentials']
actual_client = boto3.client('SERVICE',
aws_access_key_id=creds['AccessKeyId']
aws_secret_access_key=creds['SecretAccessKey']
aws_session_token=creds['SessionToken'])
That is not possible. You can only see a secret key on the first time it gets created, AWS will never show it to you again. See more here.
Some Identity Managers like Okta allow you to assume roles on CLI so you don't have to deal with credentials directly. You just assign a role to an user and he will be able to assume the role directly after the first login.

Alexa Account Linking with Cognito

You would think two of Amazon's products would integrate nicely together. You'd also think that Amazon would have proper documentation on their services. Both are horribly wrong.
I'm using Account Linking with Alexa and using AWS Cognito as my oauth provider. I am able to successfully link my account just fine, and with every subsequent alexa invocation I get an access token returned back to me. Now what?
I need to be able to access other AWS services such as dynamo, lambda, and iot from my alexa lambda function. Thought it would be as easy as something like this:
var accessToken = event.session.user.accessToken;
var params = {
IdentityPoolId: "{identity_pool_id}",
Logins : {
'cognito-idp.us-east-1.amazonaws.com/{user_pool_id}' : accessToken
}
};
AWS.config.region = 'us-east-1';
AWS.config.credentials = new AWS.CognitoIdentityCredentials(params);
AWS.config.credentials.get(function(err) {
if (err) {
console.log(err);
} else {
console.log('success');
console.log("Cognito Identity Id: " + AWS.config.credentials.identityId);
}
});
But of course it's not that simple. Can anyone help me out?
You need to assign appropriate "Policies" for the created "Role" under IAM. All of the AWS services works on policy based access permissions and these must be explicitly attached with the IAM role for that role to be able to access/run the underlying service on the behalf of that user role.
In summary you need to assign policies from IAM related to "IOT", "DynamoDB", "Lambda", etc.

Is it possible to query the AWS SDK to determine the IAM role for the current credentials?

I would like to be able to query the AWS SDK to check what the IAM role of the current credentials is. I want to check if I am running using a particular role, and if not, then try to assume that role.
Is it possible to do this? I am using the AWS SDK for JavaScript for node.js. In the AWS.config.credentials, I have access to my keys, but not to which role they belong.
I think the method you want is GetCallerIdentity on the STS (Security Token Service) API:
API reference
JS example
This returns either nice user info (if no role in effect):
{
Account: "123456789012",
Arn: "arn:aws:iam::123456789012:user/Alice",
UserId: "AKIAI44QH8DHBEXAMPLE"
}
or info on the temporary session/user and role (if a role is in effect):
{
Account: "123456789012",
Arn: "arn:aws:sts::123456789012:assumed-role/my-role-name/my-role-session-name",
UserId: "AKIAI44QH8DHBEXAMPLE:my-role-session-name"
}
As noted, IAM().GetUser() (to return info on the current user) only works in the first instance, if no role is assumed. It fails if there is a role, so code defensively, but it's worth considering as when no role is in effect it returns a nicely formatted objects (though you could just regex-parse the arn:aws:iam::(.*):user/(.*) ARN from the GetCallerIdentity):
{
User: {
Arn: "arn:aws:iam::123456789012:user/Bob",
CreateDate: <Date Representation>,
Path: "/",
UserId: "AKIAIOSFODNN7EXAMPLE",
UserName: "Bob"
}
}
I suspect GetUser fails when a role is used because from what I can tell you are allocated a temporary user, I could see it being problematic to revert that role-assumption on the server, which would be necessary in order to make GetUser work in that case. And in some cases (instance profiles?) I think there isn't any real user account to revert back to.
To get the username of a user based on a set of keys you can use:
var iam = new AWS.IAM();
iam.getUser().User.UserName;
The API docs give the full details.
To get the role arn of an instance you'd probably have to use the instance metadata API endpoint as there is no method available in the SDK.
This answer and this one give details on different ways to query instance metadata.
Using just the AWS CLI:
aws sts get-caller-identity
The AWS Security Token Service (STS) returns something like:
{
"UserId": "ABBBCCC123123DDDDEEEE",
"Account": "123456789012",
"Arn": "arn:aws:iam::123456789012:user/bob"
}
(the username is within the Arn value, after the last :)
I will provide example how to do it in AWS CLI and you can convert it into NodeJS code:
aws sts get-caller-identity
get ARN from the response and get the user name from there (everything that comes after user/)
aws iam list-groups-for-user --user-name UserNameFromPreviousResponse
Then for each group you can dive in to the policies that are builtin or attached to that group:
aws iam list-group-policies --group-name GroupName
aws iam list-attached-group-policies --group-name GroupName
from here you can dive in into the policies to get their ARN if needed but I believe at this level it already should satisfy your needs.