How to create AWS IAM role attaching managed policy only using Boto3 - amazon-web-services

I am trying to use Boto3 to create a new instance role that will attach a managed policy only.
I have the following:
Policy Name: my_instance_policy
Policy ARN: arn:aws:iam::123456789012:policy/my_test_policy
I want to create the role called 'my_instance_role' attaching attaching the above policy only.
Boto3 client has the create_role() function like below:
import boto3
client = boto3.client('iam')
response = client.create_role(
Path='string',
RoleName='string',
AssumeRolePolicyDocument='string',
Description='string'
)
Here, I do not see an option to use the policy ARN or name. My understanding is that AssumeRolePolicyDocument variable needs the JSON formatted policy document converted in to text.
Is it possible the way I am looking for?

You would have to create the role (as you are doing above) and then separately attach the managed policy to the role like this:
response = client.attach_role_policy(
RoleName='MyRole', PolicyArn='<arn of managed policy>')

I had a similar question in regard to how to supplying the AssumeRolePolicyDocument when creating an IAM role with boto3.
I used the following code...
assume_role_policy_document = json.dumps({
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "greengrass.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
})
create_role_response = self._iam.create_role(
RoleName = "my-role-name",
AssumeRolePolicyDocument = assume_role_policy_document
)
Note that the AssumeRolePolicyDocument is about defining the trust relationship and not the actual permissions of the role you are creating.

Related

Terraform change the principal of the policy to assumed role

The application is deployed in Fargate, and need to consume message from TRUCKDIM.fifo. To allow this, I granted all permissions to ecs-task-role.
The code looks like this in terraform, I am allowing the ecs-task-role to access the fifo queue TRUCKDIM.fifo with all the permissions.
resource "aws_sqs_queue" "queue_fifo-01" {
name = var.name
fifo_queue = var.fifo_queue
fifo_throughput_limit = var.fifo_throughput_limit
deduplication_scope = var.deduplication_scope
content_based_deduplication = var.content_based_deduplication
delay_seconds = var.delay_seconds
max_message_size = var.max_message_size
message_retention_seconds = var.message_retention_seconds
receive_wait_time_seconds = var.receive_wait_time_seconds
visibility_timeout_seconds = var.visibility_timeout_seconds
kms_master_key_id = var.kms_master_key_id
kms_data_key_reuse_period_seconds = var.kms_data_key_reuse_period_seconds
redrive_policy = jsonencode({
deadLetterTargetArn = aws_sqs_queue.queue_fifo-02.arn
maxReceiveCount = 10
})
policy = <<POLICY
{
"Version": "2012-10-17",
"Id": "Policy1676302010732",
"Statement": [
{
"Sid": "Stmt1676302006390",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::995556942157:role/service-role/ecs-task-role"
},
"Action": "sqs:*",
"Resource": "arn:aws:sqs:us-west-2:995556942157:TRUCKDIM.fifo"
}
]
}
POLICY
}
N.B: The role ecs-task-role is created via terraform.
When I run the terraform plan, the principal is correctly set
~ Principal = {
~ AWS = "AROA78POOLYTRE11KEEZA" -> "arn:aws:iam::995556942157:role/service-role/ecs-task-role"
}
After running terraform apply, and then check in the AWS console of the queue TRUCKDIM.fifo, the principal has changed to a string (assumed_role_id) "AROA6OPZZLYFIE6IYBEF4"
{
"Version": "2012-10-17",
"Id": "Policy1676302010732",
"Statement": [
{
"Sid": "Stmt1676302006390",
"Effect": "Allow",
"Principal": {
"AWS": "AROA6OPZZLYFIE6IYBEF4"
},
"Action": "sqs:*",
"Resource": "arn:aws:sqs:us-west-2:995556942157:TRUCKDIM.fifo"
}
]
}
Does someone know why, terraform is replacing the arn of the ecs-task-role to the assumed_rome_id ?
I am getting access denied to the TRUCKDIM.fifo in the log.
If I copy-paste the role directly in the AWS console, everything works.
It seems like this API is interpreting the ARN you provided and then replacing it with an equivalent Unique Identifier.
The difference between the two is:
An ARN uses the "friendly name" of an IAM object -- ecs-task-role in your case -- which is user-friendly but could potentially change meaning if you were to later destroy this role and then create a new role with the same name.
A unique ID is generated automatically by the IAM API and guaranteed to be unique across all objects that will ever exist. Each role you create will have a unique ID and if you delete that role and recreate one with the same name the new role will then have a distinct unique ID.
You can read more about these different identifier types in IAM identifiers.
My sense of what happened here is that the SQS API parsed your policy after Terraform submitted it and it noticed your ARN arn:aws:iam::995556942157:role/service-role/ecs-task-role and so made a query to the IAM API to find out whether there is a role named ecs-task-role. After looking it up, SQS now also knows the unique ID of this object and it seems to have stored that unique ID instead of the ARN you originally submitted, presumably so that it can "lock in" this particular role and not be tricked into using a different role if you were to delete ecs-task-role and make a new role of the same name later.
Unfortunately Terraform's AWS provider is not aware of this transformation and so from the provider's perspective this seems like the object was edited outside of Terraform and no longer matches the configuration. Although Terraform providers will typically notice when two values are equivalent, in this case that's harder because it would require the AWS provider to query the IAM API to determine whether role AROA6OPZZLYFIE6IYBEF4 has the name ecs-task-role.
Therefore I think the only way to make this configuration converge (that is: not continually propose to change the unique ID back into an ARN) would be to write the unique ID into the IAM policy instead.
One way to achieve that without hard-coding the unique ID would be to use the aws_iam_role data source to ask the IAM API to return the unique ID and then pass that value into your policy, like this:
data "aws_iam_role" "ecs_task" {
name = "ecs-task-role"
}
resource "aws_sqs_queue" "queue_fifo-01" {
# ...
policy = jsonencode({
# ...
Principal = {
AWS = data.aws_iam_role.ecs_task.unique_id
}
# ...
})
}
This unique_id attribute is defined by the aws_iam_role data source in the AWS provider to return the unique ID for the requested object. That should then cause the generated policy to include AROA6OPZZLYFIE6IYBEF4 instead of arn:aws:iam::995556942157:role/service-role/ecs-task-role and it will therefore match the way that the SQS API has stored this policy and therefore allow your configuration to converge.
(Note: I showed using jsonencode to generate the JSON here, instead of a <<POLICY "heredoc" template, because the policy content is now dynamic based and so using Terraform's JSON encoder is a robust way to ensure that the result will always be valid JSON without the need to do any special escaping. The details of that are beyond the scope of this question but if you'd like to learn more about that function please refer to its documentation.)

What's wrong with the trust policy on my aws role that it can't be assumed cross-accounts?

My lambda, named 'configure-idp' runs as role 'configure-saml-providerA' in account AAAAAAAAAAAA. I need to assume role 'configure-saml-providerB' in account BBBBBBBBBBBB which has permissions to configure the ipd for account BBBBBBBBBBBB. The trust on 'configure-saml-providerB' looks like:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:sts::AAAAAAAAAAAA:assumed-role/configure-saml-providerA/configure-idp"
},
"Action": "sts:AssumeRole"
}
]
}
And yet, when my lambda attempts to run the assume_role command (see below), I get:
An error occurred (AccessDenied) when calling the AssumeRole operation: User: arn:aws:sts::AAAAAAAAAAAA:assumed-role/configure-saml-providerA/configure-idp is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::BBBBBBBBBBBB:role/configure-saml-provider
The assume_role code mentioned above is:
client = boto3.client('sts')
assumed_role_object = client.assume_role(
RoleArn = f"arn:aws:iam::BBBBBBBBBBBB:role/configure-saml-providerB",
RoleSessionName = "LambdaSetIDPSession"
)
I've also tried changing the principal ARN in the trust to look like this:
arn:aws:sts::AAAAAAAAAAAA:role/configure-saml-providerA
But get the same error.
Why am I not able to assume role arn:aws:iam::BBBBBBBBBBBB:role/configure-saml-providerB when the trust on that role is set to allow me to do so?
Consider the following
https://aws.amazon.com/es/premiumsupport/knowledge-center/iam-assume-role-error/
Maybe you're part of an aws organization and there's an SCP that's restraining you to assume role?
The problem is that I used the account alias instead of the account id in the ARN when trying to assume role. You wouldn't see that in the post bc I obfuscated the sensitive information.

How can an IAM role assume another IAM role to access an resource

How to assume a role from another role in the same account.
Below is my first IAM role(roleA) to access sagemaker. one statement to allow access to sagemaker and another to allow assumerole.
statement {
actions = [
"sagemaker:*",
]
resources = [
"arn:aws:sagemaker:eu-west-1:1111111111:endpoint/ep",
]
}
statement {
actions = ["sts:AssumeRole"]
principals {
type = "AWS"
identifiers = ["*"]
}
}
Now I have another IAM in the same AWS account(roleB).
{
"Sid": "",
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::1111111111:role/roleA"
}
Now I assigned roleB to a Microservice. My understanding is Microservice should have access to sagemaker endpoint ep. But I am getting error that I don't have permission. where I am going wrong?
To make this work, you have to do something like the below in your microservice. As #jarmod already explained, you need to use the credentials and create clients.
source_credentials = sts_client.assume_role(
RoleArn=roleB_ARN,
RoleSessionName=`session_name`,
)
dest_sts_client = boto3.client(
'sts',
aws_access_key_id=source_credentials.get('Credentials').get('AccessKeyId'),
aws_secret_access_key=source_credentials.get('Credentials').get('SecretAccessKey'),
aws_session_token=source_credentials.get('Credentials').get('SessionToken')
)
dest_credentials = sts_client.assume_role(
RoleArn=roleA_ARN,
RoleSessionName=`session_name`,
)
sagemaker_client = boto3.client(
'sagemaker',
aws_access_key_id=dest_credentials.get('Credentials').get('AccessKeyId'),
aws_secret_access_key=dest_credentials.get('Credentials').get('SecretAccessKey'),
aws_session_token=dest_credentials.get('Credentials').get('SessionToken')
)
AWS STS Role Chaining
Roles terms and concepts

AWS lambda to assume role in the same aws account to access S3

I have created a role to get objects from s3 bucket as below:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "S3GetObjects",
"Effect": "Allow",
"Action": [
"s3:Get*"
],
"Resource": [
"arn:aws:s3:::cat-pics",
"arn:aws:s3:::cat-pics/"
]
}
]
}
Next, created a lambda function to assume this role. For that added the following statement to the basic lambda execution role which is attached to lambda:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::same-account-id:role/AssumeS3RoleDemo"
}
]
}
However, the following code
import json
import boto3
def lambda_handler(event, context):
print("this test should be printed")
# create an STS client object that represents a live connection to the
# STS service
sts_client = boto3.client('sts')
# Call the assume_role method of the STSConnection object and pass the role
# ARN and a role session name.
assumed_role_object=sts_client.assume_role(
RoleArn="arn:aws:iam::same-account-id:role/AssumeS3RoleDemo",
RoleSessionName="AssumeRoleSession"
)
# From the response that contains the assumed role, get the temporary
# credentials that can be used to make subsequent API calls
credentials=assumed_role_object['Credentials']
print("credentials are")
print(credentials)
does not work. I keep getting the following error:
An error occurred (AccessDenied) when calling the AssumeRole operation: User: arn:aws:sts::same-account-id:assumed-role/lambda_basic_execution_new/AssumeRoleDemo is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::same-account-id:role/AssumeS3RoleDemo: ClientError
Here AssumeRoleDemo is name of the lambda function and AssumeS3RoleDemo is the role name which has access to S3.
Is it possible to assume role in the same account ? Is so, what step am I missing here ? Please let me know.
thanks
You need amend the role with trust policy
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
In addition to that make sure your S3 bucket doesnt have a bucket policy. Because resource based policy and IAM based policies both should be allowing.
You don't need to use STS and AssumeRole in your lambda code to access S3 if both are in the same account, if role attached to lambda has policy allowing access on S3 it will work just fine.
But if you really want to do it, you need to make sure your role AssumeS3RoleDemo trust policy allow lambda execution role to assume it.
Below is a link to one exemplo using two different accounts, but the mechanism is the same using just one account:
https://aws.amazon.com/premiumsupport/knowledge-center/lambda-function-assume-iam-role/#:~:text=1.,the%20role%20in%20account%20B%3A&text=Update%20your%20Lambda%20function%20code,to%20create%20a%20service%20client.

Cross Account access Role creation for AWS Account using boto3

I want to create a AWS Cross account role using boto3. Created the role from Console. But not able to create using boto3. I have two different AWS Accounts. I want to access one account from other using Assume Role. For that i need to create a permission in Account 1 so that Account 2 can access the same. But, I need to perform all the functionality using boto3 only.I used this code -
iam = boto3.client('iam',aws_access_key_id='XXXXX',aws_secret_access_key='YYYY')
role = iam.create_role(RoleName=<Role Name>,AssumeRolePolicyDocument='{"Version" : "2012-10-17","Statement": [{"Effect": "Allow","Principal":{"AWS":"arn:aws:iam::<Account ID to which permission is granted>:root"} ,"Action":["sts:AssumeRole" ]}]}')
policy = iam.create_policy(PolicyName=<Policy Name>, PolicyDocument='{"Version": "2012-10-17","Statement": [{"Effect": "Allow","Action": "*","Resource": "*"}]}')
policy_arn = policy['Policy']['Arn']
iam.attach_role_policy(PolicyArn=<Policy arn>,RoleName=<Name of the role to which policy need to be attached>)
Can this code be more optimized, may be lesser calls
no optimizing required. code working fine.
In order to assume a role of account A from account B, here is the procedure.
In account A create a role with the following trust relationship.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"AWS": "Root arn of the account B"
},
"Action": "sts:AssumeRole"
}
]
}
Save the above json in one of the file. Then write a function to create a role using this above trust relationship.
import boto3
role_name = "Name of the role"
client = boto3.client('iam')
client.create_role(RoleName=role_name,
AssumeRolePolicyDocument=trust_relationship())
def trust_relationship(self, role_name):
with open('json_file_name', 'r') as data_file:
trust_relationship = json.load(data_file)
return json.dumps(trust_relationship)
Now in account B, create a role with the following json document, to assume the role of account A.
Policy Document:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Action": [
"sts:AssumeRole",
"Any other required actions"
],
"Resource": "*"
}
]
}
Now use this policy document to attach to the role in account B which then assumes the role of account A.