AWS assign external role to instance - amazon-web-services

I have a program that goes through many accounts and does inventory. Right now it is using boto3 and AWS keys.
I would like to move away from aws keys and instead assign role to an instance and use that role to switch accounts.
I know how to assign external role to a user, but I have no idea how to do that with an instance role.
The idea here is to do something like this:
Get list of all accounts from main organization account
For each account assume role with ec2 (and other service) describe role
Get inventory
Switch role to another account.
Any ideas how to accomplish this?

Your method is sound. You would do it as follows:
Create a Main Role in the main account.
Assign this role to the Amazon EC2 instance being used to gather the inventory.
Create a Target IAM Role in each target account.
Assign sufficient permissions to each Target Role to allow the inventory (eg Describe* calls)
Edit the Trust Policy on each Target Role to permit the Main Role to use the Target Role
Your application can then use the Main Role (assigned to the EC2 instance) to call AssumeRole() on each of the Target Roles in the child accounts (as per your step 2).
Thus, your flow is really:
Get list of all accounts from main organization account
For each account:
Assume Role on the target role
Get inventory

You can call org API to list accounts first and the same way you can call for each account with cross-account role
Make sure your Ec2 role has sufficient permissions to assume role for child account
def get_accounts():
sts_role = 'arn:aws:iam::XXX:role/AWSAudit_CrossOrgAccess' # Org Billing API
assumedRoleObject = sts.assume_role(RoleArn=str(sts_role), RoleSessionName="accounts_watcher_lambda" )
credentials = assumedRoleObject['Credentials']
conn = boto3.client( 'organizations', aws_access_key_id=credentials['AccessKeyId'], aws_secret_access_key=credentials['SecretAccessKey'], aws_session_token=credentials['SessionToken'],)
list_of_accounts = conn.list_accounts()
return list_of_accounts```
## Your Ec2 role looks like
`
YourServerEc2Profile:
Type: AWS::IAM::InstanceProfile
Properties:
Path: '/'
Roles:
- Ref: YourServerEc2Role
YourServerEc2Role:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- sts:AssumeRole
Path: "/"
Policies:
-
PolicyName: AuditInstall
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Action:
- "ssm:*",
- "ec2:DescribeImages",
- "cloudwatch:PutMetricData",
- "ec2:DescribeInstances",
- "lambda:InvokeFunction",
- "ec2:DescribeTags",
- "ec2:DescribeVpcs",
- "cloudwatch:GetMetricStatistics",
- "ec2:DescribeSubnets",
- "ec2:DescribeKeyPairs",
- "cloudwatch:ListMetrics",
- "ec2:DescribeSecurityGroups"
Resource: "*"
Effect: "Allow"
ManagedPolicyArns:
- !Ref YourServerCrossAccount
- arn:aws:iam::aws:policy/ReadOnlyAccess
YourServerCrossAccount:
Type: AWS::IAM::ManagedPolicy
Properties:
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- sts:AssumeRole
Resource:
- arn:aws:iam::XXX:role/AWS_CrossAccount
`

Related

aws lambda permission to IAM usergroup using cloudformation

I need create a way for execute especific lambdas for specific IAM users, then i am doing:
I am creating a Usergroup in cloudformation:
Resources:
XXXGroup:
Type: AWS::IAM::Group
And after I am creating a policy and add the policy to my UserGroup
UsersXPolicies:
Type: AWS::IAM::Policy
Properties:
Groups:
- !Ref XXXGroup
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Action:
- "lambda:*"
Resource:
- !Sub arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:MyFunction
- !Sub arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:MyFunction2
PolicyName: xxx-access-policy
I try first only with "lambda:InvokeFunction" but it not works, then I try with lambda:* but it not works
After I am creating a user:
XXUser:
Type: AWS::IAM::User
Properties:
UserName: xxx.user
LoginProfile:
Password: l98GaTc9xzT9
PasswordResetRequired: true
Path: /
And finally i am adding the user to my usergroup:
USerAdditionX:
Type: AWS::IAM::UserToGroupAddition
Properties:
GroupName: !Ref XXXGroup
Users:
- !Ref XXUser
But after login with my new user i am getting the following error when i go to lambda service:
User: arn:aws:iam::xxxxxxxxx:user/xx.user is not authorized to perform: lambda:GetAccountSettings on resource: * because no identity-based policy allows the lambda:GetAccountSettings action
Access to specific lambdas from my new user
The problem is that lambda get its permission from a role.Policies are attached to role.
You need to attach your policies to a role and then attach a role to lambda.
Please read about lambda execution role over here
You can't limit the visibility for the list of all the Lambda Functions (there is also the same "problem" on EC2 Instances and S3 Buckets permissions policy), so your user cannot interact with the Lambda because the policy that you provided have the condition on the specific resource, but he need the full read-only capability even to see the function.
You should add at least an Allow statement on lambda:ListFunctions and lambda:GetAccountSettings for Resource "*" (so on every Lambda of your account), as stated here.
You could also, as documented here, add the standard AWSLambda_ReadOnlyAccess policy to your group.

Possible to create a policy and share the policy to role across multiple accounts

I am trying to create a policy in one AWS account and need to share that policy to a role in multiple accounts (Prod, Dev, Sandbox).
And I can add the AWS accounts number manually and assign AWS Managed Policy to roles and needs to create multiple roles as well.
How can we achieve this?
Here is the code I wrote
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Create a role that authorizes access to users in another account'
Metadata:
Version: 0.7
Parameters:
RoleName:
Type: String
Default: R_EC2-Describe-Instance
MainAccountId:
Type: String
Description: >-
Include the Managed Services Account ID(the account ID where the Main VPC is registered)
Default: 111111111111
MaxLength: 12
MinLength: 12
Resources:
AssumeRole:
Type: AWS::IAM::Policy
Properties:
RoleName: !Ref RoleName
Policies:
-
PolicyName: "CoreSVC-Describe-EC2"
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Action:
- 'sts:AssumeRole'
Resource: !Join [ "", [ "arn:aws:iam::", !Ref MainAccountId, ":role/R_EC2-Describe-Instance" ] ]
AssumeRolePolicyDocument:
Version: '2012-10-17'
ManagedPolicyName:
- "arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAcess"
Statement:
- Effect: Allow
Principal:
"AWS": !Join [ "", [ "arn:aws:iam::", !Ref MainAccountId, ":root" ] ]
Action:
- sts:AssumeRole
Condition: {}
You can't share an IAM Policy with other accounts, as it doesn't have a resource policy to allow it.
The code sample you've shared is sharing the IAM Role with multiple accounts, which is possible via the role's resource/trust policy.
If you want to share the same policy across multiple accounts, then you should probably use CloudFormation StackSets (as mentioned by #ervin-szilagyi), or some other infrastructure-as-code approach.
If you want to share the role with other accounts, then you've already done it. All that's missing is role in those accounts with access to the sts:AssumeRole action so that it can assume the role you've shared in your code.

Use External Id in cross-account role of AWS using Serverless Framework

Here is the problem: I need to use the Lambda function in AWS Account A (In root AWS Account A) to write some data to the DynamoDB tables in AWS Account B (in root AWS Account B). All the project in written with Serverless Framework in Node.js.
I know I need to use Cross-Account Role in the Lambda function to do that. Since Serverless use the template that shares a lot of things similar to the cloudformation template. Then I did some research on how to use the cross account role, here is the AWS Documentation https://docs.aws.amazon.com/zh_cn/IAM/latest/UserGuide/id_roles_create_for-user_externalid.html
{
"Version": "2012-10-17",
"Statement": {
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Principal": {"AWS": "Example Corp's AWS Account ID"},
"Condition": {"StringEquals": {"sts:ExternalId": "12345"}}
}
}
this is the sample iam specification for the task like this. The yaml template in the serverless looks like this in my project:
- Effect: Allow
Principal:
AWS: 'AWS Account B External ID'
Action:
- sts:AssumeRole
Resource:
- '*'
But When I tried to deploy the serverless template:
I got the error like this:
An error occurred: IamRoleLambdaExecution - Policy document should not specify a principal. (Service: AmazonIdentityManagement; Status Code: 400; Error Code: MalformedPolicyDocument; Request ID: XXXX-XXXX-XXXX-XXXX-XXXX).
I am wondering what is the correct way to specify the template according to the demand.
Appreciate to the help of any kinds
You can allow for assumed roles using an external ID by creating an AWS::IAM:Role resource. The example below gives access to an S3 bucket, but the approach is the same.
ExternalS3AccessRole:
Type: AWS::IAM::Role
Properties:
RoleName: SomeRoleName
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
AWS: "Example Corp's AWS Account ID"
Condition:
StringEquals:
'sts:ExternalId': "12345"
Policies:
- PolicyName: ExternalS3AccessPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- s3:Get*
- s3:List*
Resource:
- "arn:aws:s3:::<BUCKET_NAME>/*"
- Effect: Allow
Action:
- s3:Get*
- s3:List*
Resource:
- "arn:aws:s3:::<BUCKET_NAME>"

AWS Cloudformation Role is not authorized to perform AssumeRole on Role

I am trying to execute a cloudformation stack which contains the following resources:
Codebuild project
Codepipeline pipeline
Roles needed
While trying to execute the stack, it fails with the following error:
arn:aws:iam::ACCOUNT_ID:role/CodePipelineRole is not authorized to perform AssumeRole on role arn:aws:iam::ACCOUNT_ID:role/CodePipelineRole (Service: AWSCodePipeline; Status Code: 400; Error Code: InvalidStructureException; Request ID: 7de2b1c6-a432-47e6-8208-2c0072ebaf4b)
I created the role using a managed policy, but I have already tried with a normal policy and it does not work neither.
This is the Role Policy:
CodePipelinePolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
Description: 'This policy grants permissions to a service role to enable Codepipeline to use multiple AWS Resources on the users behalf'
Path: "/"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Resource: "*"
Effect: "Allow"
Condition: {}
Action:
- autoscaling:*
- cloudwatch:*
- cloudtrail:*
- cloudformation:*
- codebuild:*
- codecommit:*
- codedeploy:*
- codepipeline:*
- ec2:*
- ecs:*
- ecr:*
- elasticbeanstalk:*
- elasticloadbalancing:*
- iam:*
- lambda:*
- logs:*
- rds:*
- s3:*
- sns:*
- ssm:*
- sqs:*
- kms:*
This is the Role
CodePipelineRole:
Type: "AWS::IAM::Role"
Properties:
RoleName: !Sub ${EnvironmentName}-CodePipelineRole
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Action:
- 'sts:AssumeRole'
Effect: Allow
Principal:
Service:
- codepipeline.amazonaws.com
Path: /
ManagedPolicyArns:
- !Ref CodePipelinePolicy
What intrigues me the most is that it seems like CodePipelineRole is trying to AssumeRole to itself. I'm not understanding what can be happening here.
And when I set the policy's action to *, it works! I don't know what permissions could be missing.
Thanks
It is to do with the trust relationship for the role you have created i.e. CodePipelineRole
Go to the Role in IAM
Select the Trust Relationships tab ...
Then Edit Trust Relationship to include codepipeline
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"codepipeline.amazonaws.com"
]
},
"Action": "sts:AssumeRole"
}
]
}```
It seems like, behind the scenes, AWS services keep some kind of role cache. If you try to make a role, attach a policy and create a new CodeBuild project sequentially, CodeBuild will give an unauthorized error because it can't find the role. It's similar to getting a forbidden access error on a non-existing bucket (instead of a 404). If you separate the stack in two other stacks: first you create the roles and then you create the CodeBuild, it works. I don't understand why the CLI command works instantly though.
try adding sts:AssumeRole to the list of Actions.
https://docs.aws.amazon.com/IAM/latest/UserGuide/troubleshoot_roles.html
Cheers
I had a similar issue with EKS for some reason code build role could not assume role. I solved it by creating a user with sufficient access and by setting:
AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
env vars as default env vars from environment section in cloud build:
https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html
I bet you specified RoleArn on your Source action of the CodePipeline. Try to remove it.
CodePipelinePipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
...
Stages:
- Name: "Source"
Actions:
- Name: "Source"
#RoleArn: !GetAtt CodePipelineRole.Arn
The last line was the reason for the very same error in my case.

Where to find External Id for SmsConfiguration in Cognito user pool

I use create_user_pool for creating new Cognito user pools. I see there's a SmsConfiguration option which takes an ExternalId.
If you set up MFA for your user pool using the Cognito portal, this External Id (which looks like an UUID) will be used in the automatically generated IAM SMS-Role.
Where do I find/generate the value for ExternalId if I want to manually (using boto3 or AWS CLI) create the user pool and the IAM SMS role?
My MFA setup looks like this:
You're right, it's a UUID that you define in the IAM Role. Here is an example CloudFormation Template with an External ID -
CognitoSMSRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Service:
- "cognito-idp.amazonaws.com"
Action:
- "sts:AssumeRole"
Condition:
StringEquals:
"sts:ExternalId": 'this-is-my-external-id'
Path: "/"
CognitoSMSPolicy:
Type: "AWS::IAM::Policy"
Properties:
PolicyName: "CognitoSMSPolicy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action:
- "sns:publish"
Resource:
- "*"
Roles:
- Ref: CognitoSMSRole
You can also find the External ID in the console.
IAM -> Roles -> Select your Role -> Trusted Relationships