Skip to content

Connect AWS

Who this is for: admins who let workflows read users, groups, policies and permission sets in AWS, for example to check what access a leaver still has.

Before you start: you need the Manage connections permission in Liitora (Admin and Editor), and the Manage secrets permission to store the access key. In AWS you need to be allowed to create IAM roles, IAM users and access keys, and to edit a role’s trust policy.

Liitora signs in to AWS as a role in your account. Your AWS account keeps full control: you decide what the role may do, you can see every call in CloudTrail, and you can stop it at any time. Liitora signs every AWS request itself. Workflows never see the access key, the session or a signature, and they can’t set the headers that carry them.

  1. You create an IAM user for Liitora whose only permission is to assume one role. Liitora stores that user’s access key as a secret.
  2. When a run first uses the connection, Liitora uses the key to assume the role, together with an external ID. The external ID is a random value that Liitora makes for this connection. The role’s trust policy accepts the IAM user only when it brings that exact value, so nobody can trick Liitora into using your role for someone else.
  3. AWS gives the run a session that lasts 15 minutes. The session is named liitora- followed by the run’s ID, so CloudTrail shows which run made each call. Each run gets its own session, and no session is stored or shared.

The AWS connection template fills in everything that must not change. You choose the Region, the services and the role.

Setting Value
Address The STS endpoint of your Region, for example https://sts.eu-north-1.amazonaws.com. Liitora signs in only there.
Other hosts it may call Only the services you choose: iam.amazonaws.com for iam, sso.<region>.amazonaws.com for sso (IAM Identity Center), identitystore.<region>.amazonaws.com for identitystore
Sign-in The access key assumes the role with the external ID
Shared request limit 15 calls per second, under IAM Identity Center’s own limit

A workflow can call only those hosts, and only at the address /. Any other host, port or path is refused before anything is signed. Liitora signs each call for the service of its host, so a signature for one service can’t be used at another.

  1. In the IAM console, create a role for Liitora, for example liitora-access-check. For now, let your own account assume it. You replace the trust policy later.
  2. Give the role only the permissions your workflows need. This policy allows the reads the AWS access actions use, and nothing else:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ReadIamUsers",
"Effect": "Allow",
"Action": [
"iam:GetUser",
"iam:ListGroupsForUser",
"iam:ListAttachedUserPolicies",
"iam:ListUserPolicies"
],
"Resource": "*"
},
{
"Sid": "ReadIdentityCenter",
"Effect": "Allow",
"Action": [
"sso:ListInstances",
"sso:ListAccountAssignmentsForPrincipal",
"sso:DescribePermissionSet",
"sso:ListPermissionSets",
"identitystore:GetUserId",
"identitystore:ListGroupMembershipsForMember"
],
"Resource": "*"
}
]
}

You can narrow Resource to your own users, instance and identity store. The Service Authorization Reference lists the resource ARNs each action takes. AWS’s managed policy AWSSSODirectoryReadOnly does not include identitystore:GetUserId, so it is not enough on its own.

  1. Copy the role’s ARN, for example arn:aws:iam::111122223333:role/liitora-access-check.
  1. In the IAM console, create a user for Liitora, for example liitora, without console access.
  2. Give it this one permission, with your role’s ARN:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::111122223333:role/liitora-access-check"
}
]
}
  1. Create an access key for the user. AWS shows the secret access key only once.
  2. In Liitora, open Secrets and add a secret of the kind AWS access key, for example aws_key. Enter the Access key ID and the Secret access key. Both are saved together. See Add a secret.
  1. Open Connections and select Add a connection.
  2. Under Start from, pick AWS (IAM, IAM Identity Center, Identity Store).
  3. Type the Name, for example aws. Workflows use this name.
  4. Pick the Environment. Use a separate AWS account, or at least a separate role, for Test.
  5. Pick the AWS Region of your IAM Identity Center instance, for example Europe (Stockholm) (eu-north-1).
  6. Type the AWS services the workflows call, separated by commas: iam, sso, identitystore.
  7. Paste the Role ARN.
  8. Under Secrets, type aws_key for both Access key ID and Secret access key. If the secret doesn’t exist yet, select Ask for it so someone adds the value.
  9. Select Save.

Your AI assistant can add the connection with the create_connection_from_template tool and ask for the key with request_secret. It never sees the key. See the MCP tools reference.

Require the external ID in the role’s trust policy

Section titled “Require the external ID in the role’s trust policy”

After you save, the connection shows its External ID, for example liitora-3f9c…. It is not a secret, but only this connection uses it. Replace the role’s trust policy with this one, with your IAM user’s ARN and the external ID from Liitora:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::111122223333:user/liitora" },
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": { "sts:ExternalId": "<the external ID Liitora shows>" }
}
}
]
}

Then select Test connection.

Test connection assumes the role with the external ID and asks AWS who it is signed in as. It shows the AWS account and the role. Then it tries to assume the role without the external ID. That must fail. If AWS lets it through, the test fails, because then anyone with the key could use your role without Liitora’s external ID.

The test says What to do
This role can be used without the external ID Add the sts:ExternalId condition to the role’s trust policy as shown above, then test again.
Check the access key in the AWS connection AWS refused the key. Check both values in the secret, and that the key is active in IAM.
Check that the role trusts the IAM user and the external ID The key works, but the role can’t be assumed. Check the role ARN, the user’s sts:AssumeRole permission and the trust policy.
AWS refused the request time (aws_clock_skew) A machine’s clock is off. See aws_clock_skew.

Liitora reminds your admins to rotate the key 90 days after it was last set, and every 30 days after that. Rotate it the way AWS recommends, so nothing stops working:

  1. Create a second access key for the IAM user. A user can have two.
  2. In Liitora, set both new values in the secret and save.
  3. Select Test connection.
  4. In AWS, deactivate the old key. Delete it once you are sure nothing uses it.

Select New external ID on the connection if the old one should stop working, for example after you shared it by mistake. The connection can’t sign in until you put the new value in the role’s trust policy. On a production connection only a person who can approve workflows may do this. AI assistants and API tokens can’t.

  • A test run never signs in to a production AWS account. Add Test settings with a test role instead. A production AWS connection can’t be marked as having no separate test system.
  • A dry run sends only calls that read: the operations GetUser, ListGroupsForUser, ListAttachedUserPolicies, ListUserPolicies, ListInstances, ListAccountAssignmentsForPrincipal, DescribePermissionSet, ListPermissionSets, GetUserId, ListGroupMembershipsForMember and GetCallerIdentity. Liitora goes by the operation’s name, not by the HTTP method, because AWS uses POST for reads and changes alike. Everything else is held back and shown as “would have”.
  • Steps with an AWS connection can run on your own runner. The runner signs in to AWS itself with a key from your environment, so Liitora never sees it. See AWS connections on a runner.

AWS answers a throttled call with an error code instead of waiting. Liitora waits and tries again, using AWS’s suggested wait when it gives one. A call that AWS refused as invalid is not retried.

The data stays in your AWS account. IAM is global. IAM Identity Center and Identity Store keep their data in the Region you chose, and Liitora’s calls go to that Region. The calls carry what the workflow looks up, such as a user name or an email address, and the answers go into the run. The session name in CloudTrail holds only the run’s ID, never a person’s name.

These points come from AWS’s documentation but haven’t been checked against a real account yet. If something in your account behaves differently, tell us:

  • IAM signs as the Region us-east-1. IAM Identity Center signs as sso, Identity Store as identitystore.
  • The services accept the session token inside the signed headers.
  • How far a clock may be off before AWS refuses a call (5 or 15 minutes).
  • Whether ListAccountAssignmentsForPrincipal for a user includes access the user has through groups.
  • Whether GetUserId matches user names regardless of upper and lower case.
  • IAM’s own limit on calls per second.

Written from AWS’s own documentation: