feat(pam): update AWS IAM resource documentation and forms

- Renamed PAM Role to Resource Role in documentation and forms to clarify the role's purpose.
- Enhanced AWS IAM account and resource forms to reflect the new naming conventions and improve user guidance.
- Added new images to the documentation for better visual understanding of the setup process.
- Updated session duration handling and trust policy instructions to align with the new Resource Role terminology.
This commit is contained in:
Victor Santos
2025-12-09 14:35:20 -03:00
parent 817d5a27c3
commit afae53c639
10 changed files with 245 additions and 207 deletions

View File

@@ -14,13 +14,13 @@ Unlike database or SSH resources that require a Gateway for network connectivity
sequenceDiagram
participant User
participant Infisical
participant PAM Role as PAM Role<br/>(Your AWS Account)
participant Resource Role as Resource Role<br/>(Your AWS Account)
participant Target Role as Target Role<br/>(Your AWS Account)
participant Console as AWS Console
User->>Infisical: Request AWS Console access
Infisical->>PAM Role: AssumeRole (with ExternalId)
PAM Role-->>Infisical: Temporary credentials
Infisical->>Resource Role: AssumeRole (with ExternalId)
Resource Role-->>Infisical: Temporary credentials
Infisical->>Target Role: AssumeRole (role chaining)
Target Role-->>Infisical: Session credentials
Infisical->>Console: Generate federation URL
@@ -31,174 +31,14 @@ sequenceDiagram
### Key Concepts
1. **PAM Role**: An IAM role in your AWS account that trusts Infisical. This is the "bridge" role that Infisical assumes first.
1. **Resource Role**: An IAM role in your AWS account that trusts Infisical. This is the "bridge" role that Infisical assumes first.
2. **Target Role**: The IAM role that end users will actually use in the AWS Console. The PAM Role assumes this role on behalf of the user.
2. **Target Role**: The IAM role that end users will actually use in the AWS Console. The Resource Role assumes this role on behalf of the user.
3. **Role Chaining**: Infisical uses AWS role chaining - it first assumes the PAM Role, then uses those credentials to assume the Target Role. This provides an additional layer of security and audit capability.
3. **Role Chaining**: Infisical uses AWS role chaining - it first assumes the Resource Role, then uses those credentials to assume the Target Role. This provides an additional layer of security and audit capability.
4. **External ID**: A unique identifier (your Infisical Project ID) used in the trust policy to prevent [confused deputy attacks](https://docs.aws.amazon.com/IAM/latest/UserGuide/confused-deputy.html).
## Prerequisites
Before configuring AWS Console access in Infisical PAM, you need to set up two IAM roles in your AWS account:
1. **PAM Role** - Trusted by Infisical, can assume target roles
2. **Target Role(s)** - The actual roles users will use in the console
<Info>
**No Gateway Required**: Unlike database or SSH resources, AWS Console access does not require an Infisical Gateway. Infisical communicates directly with AWS APIs.
</Info>
## Step 1: Create the PAM Resource
The PAM Resource represents the connection between Infisical and your AWS account. It contains the PAM Role that Infisical will assume.
### 1.1 Create the PAM Role Permissions Policy
First, create an IAM policy that allows the PAM Role to assume your target roles. We recommend using a wildcard pattern so you can add new target roles without updating this policy.
```json
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::<YOUR_ACCOUNT_ID>:role/<YOUR_PREFIX>-*"
}]
}
```
<Tip>
Choose a naming convention for your target roles (e.g., `pam-*`, `infisical-*`, or `privileged-*`). This allows you to add new accounts without modifying the PAM Role's permissions.
</Tip>
### 1.2 Create the PAM Role with Trust Policy
Create an IAM role (e.g., `InfisicalPAMRole`) with:
- The permissions policy from step 1.1 attached
- The following trust policy:
```json
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::<INFISICAL_AWS_ACCOUNT_ID>:root"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "<YOUR_INFISICAL_PROJECT_ID>"
}
}
}]
}
```
<Warning>
**Security Best Practice**: Always use the External ID condition. This prevents confused deputy attacks where another Infisical customer could potentially trick Infisical into assuming your role.
</Warning>
**Infisical AWS Account IDs:**
| Region | Account ID |
|--------|------------|
| US | `381492033652` |
| EU | `345594589636` |
<Note>
**For Dedicated Instances**: Your AWS account ID differs from the ones listed above. Please contact Infisical support to obtain your dedicated AWS account ID.
</Note>
### 1.3 Create the Resource in Infisical
1. Navigate to your PAM project and go to the **Resources** tab
2. Click **Add Resource** and select **AWS IAM**
3. Enter a name for the resource (e.g., `production-aws`)
4. Enter the **PAM Role ARN** - the ARN of the role you created in step 1.2
![Create AWS IAM Resource](/images/pam/resources/aws-iam/create-resource.png)
Clicking **Create Resource** will validate that Infisical can assume the PAM Role. If the connection fails, verify:
- The trust policy has the correct Infisical AWS account ID
- The External ID matches your project ID
- The role ARN is correct
## Step 2: Create PAM Accounts
A PAM Account represents a specific Target Role that users can request access to. You can create multiple accounts per resource, each pointing to a different target role with different permission levels.
### 2.1 Create the Target Role Trust Policy
Each target role needs a trust policy that allows your PAM Role to assume it:
```json
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::<YOUR_ACCOUNT_ID>:role/InfisicalPAMRole"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "<YOUR_INFISICAL_PROJECT_ID>"
}
}
}]
}
```
<Info>
**Tip**: If your target role name matches the wildcard pattern in your PAM Role's permissions policy (e.g., `pam-readonly`), you don't need to update the PAM Role's permissions. Just ensure the target role's trust policy is correct.
</Info>
### 2.2 Create the Account in Infisical
1. Navigate to the **Accounts** tab in your PAM project
2. Click **Add Account** and select the AWS IAM resource you created
3. Fill in the account details:
<ParamField path="Name" type="string" required>
A friendly name for this account (e.g., `readonly`, `admin`, `developer`)
</ParamField>
<ParamField path="Description" type="string">
Optional description of what this account is used for
</ParamField>
<ParamField path="Target Role ARN" type="string" required>
The ARN of the IAM role users will assume (e.g., `arn:aws:iam::123456789012:role/pam-readonly`)
</ParamField>
<ParamField path="Default Session Duration" type="string" required>
Session duration using human-readable format (e.g., `15m`, `30m`, `1h`). Minimum 15 minutes, maximum 1 hour.
<Warning>
Due to AWS role chaining limitations, the maximum session duration is **1 hour**, regardless of the target role's configured maximum session duration.
</Warning>
</ParamField>
![Create AWS IAM Account](/images/pam/resources/aws-iam/create-account.png)
## Step 3: Access the AWS Console
Once your resource and accounts are configured, users can request access through Infisical:
1. Navigate to the **Accounts** tab
2. Find the AWS Console account you want to access
3. Click the **Access** button
Infisical will:
1. Assume the PAM Role using your project's External ID
2. Assume the Target Role using role chaining
3. Generate a federated sign-in URL
4. Open the AWS Console in a new browser tab
The user will be signed into the AWS Console with the permissions of the Target Role.
## Session Behavior
### Session Duration
@@ -229,16 +69,190 @@ arn:aws:sts::123456789012:assumed-role/pam-readonly/user@example.com
This allows you to correlate Infisical PAM sessions with CloudTrail logs for complete audit visibility.
## Example: Multi-Environment Setup
## Prerequisites
A typical setup might include:
Before configuring AWS Console access in Infisical PAM, you need to set up two IAM roles in your AWS account:
**Resource**: `aws-production` (PAM Role in production account)
- **Account**: `readonly` → `arn:aws:iam::111111111111:role/pam-readonly`
- **Account**: `developer` → `arn:aws:iam::111111111111:role/pam-developer`
- **Account**: `admin` → `arn:aws:iam::111111111111:role/pam-admin`
1. **Resource Role** - Trusted by Infisical, can assume target roles
2. **Target Role(s)** - The actual roles users will use in the console
**Resource**: `aws-staging` (PAM Role in staging account)
- **Account**: `full-access` → `arn:aws:iam::222222222222:role/pam-full-access`
<Info>
**No Gateway Required**: Unlike database or SSH resources, AWS Console access does not require an Infisical Gateway. Infisical communicates directly with AWS APIs.
</Info>
Users can then be granted access to specific accounts based on their role and the approval workflows configured in Infisical.
## Create the PAM Resource
The PAM Resource represents the connection between Infisical and your AWS account. It contains the Resource Role that Infisical will assume.
<Steps>
<Step title="Create the Resource Role Permissions Policy">
First, create an IAM policy that allows the Resource Role to assume your target roles. For simplicity, you can use a wildcard to allow assuming any role in your account:
```json
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::<YOUR_ACCOUNT_ID>:role/*"
}]
}
```
![Create AWS IAM Resource](/images/pam/resources/aws-iam/resource-role-policy.png)
<Note>
**For more granular control**: If you want to restrict which roles the Resource Role can assume, replace the wildcard (`/*`) with a more specific pattern. For example:
- `arn:aws:iam::<YOUR_ACCOUNT_ID>:role/pam-*` to only allow roles with the `pam-` prefix
- `arn:aws:iam::<YOUR_ACCOUNT_ID>:role/infisical-*` to only allow roles with the `infisical-` prefix
This allows you to limit the blast radius of the Resource Role's permissions.
</Note>
</Step>
<Step title="Create the Resource Role with Trust Policy">
Create an IAM role (e.g., `InfisicalResourceRole`) with:
- The permissions policy from the previous step attached
- The following trust policy:
```json
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::<INFISICAL_AWS_ACCOUNT_ID>:root"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "<YOUR_INFISICAL_PROJECT_ID>"
}
}
}]
}
```
![Create AWS IAM Resource](/images/pam/resources/aws-iam/resource-role-trust-policy.png)
![Create AWS IAM Resource](/images/pam/resources/aws-iam/resource-role-attach-policy.png)
<Warning>
**Security Best Practice**: Always use the External ID condition. This prevents confused deputy attacks where another Infisical customer could potentially trick Infisical into assuming your role.
</Warning>
**Infisical AWS Account IDs:**
| Region | Account ID |
|--------|------------|
| US | `381492033652` |
| EU | `345594589636` |
<Note>
**For Dedicated Instances**: Your AWS account ID differs from the ones listed above. Please contact Infisical support to obtain your dedicated AWS account ID.
</Note>
<Note>
**For Self-Hosted Instances**: Use the AWS account ID where your Infisical instance is deployed. This is the account that hosts your Infisical infrastructure and will be assuming the Resource Role.
</Note>
</Step>
<Step title="Create the Resource in Infisical">
1. Navigate to your PAM project and go to the **Resources** tab
2. Click **Add Resource** and select **AWS IAM**
3. Enter a name for the resource (e.g., `production-aws`)
4. Enter the **Resource Role ARN** - the ARN of the role you created in the previous step
![Create AWS IAM Resource](/images/pam/resources/aws-iam/create-resource.png)
Clicking **Create Resource** will validate that Infisical can assume the Resource Role. If the connection fails, verify:
- The trust policy has the correct Infisical AWS account ID
- The External ID matches your project ID
- The role ARN is correct
</Step>
</Steps>
## Create PAM Accounts
A PAM Account represents a specific Target Role that users can request access to. You can create multiple accounts per resource, each pointing to a different target role with different permission levels.
<Steps>
<Step title="Create the Target Role Trust Policy">
Each target role needs a trust policy that allows your Resource Role to assume it:
```json
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::<YOUR_ACCOUNT_ID>:role/InfisicalResourceRole"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "<YOUR_INFISICAL_PROJECT_ID>"
}
}
}]
}
```
![Create AWS IAM Resource](/images/pam/resources/aws-iam/target-role-trust-policy.png)
</Step>
<Step title="Create the Account in Infisical">
1. Navigate to the **Accounts** tab in your PAM project
2. Click **Add Account** and select the AWS IAM resource you created
3. Fill in the account details:
![Create AWS IAM Account](/images/pam/resources/aws-iam/create-account.png)
<ParamField path="Name" type="string" required>
A friendly name for this account (e.g., `readonly`, `admin`, `developer`)
</ParamField>
<ParamField path="Description" type="string">
Optional description of what this account is used for
</ParamField>
<ParamField path="Target Role ARN" type="string" required>
The ARN of the IAM role users will assume (e.g., `arn:aws:iam::123456789012:role/pam-readonly`)
</ParamField>
<ParamField path="Default Session Duration" type="string" required>
Session duration using human-readable format (e.g., `15m`, `30m`, `1h`). Minimum 15 minutes, maximum 1 hour.
<Warning>
Due to AWS role chaining limitations, the maximum session duration is **1 hour**, regardless of the target role's configured maximum session duration.
</Warning>
</ParamField>
</Step>
</Steps>
## Access the AWS Console
Once your resource and accounts are configured, users can request access through Infisical:
![Create AWS IAM Resource](/images/pam/resources/aws-iam/access-account.png)
<Steps>
<Step title="Navigate to Accounts">
Go to the **Accounts** tab in your PAM project.
</Step>
<Step title="Find the Account">
Find the AWS Console account you want to access.
</Step>
<Step title="Request Access">
Click the **Access** button.
Infisical will:
1. Assume the Resource Role using your project's External ID
2. Assume the Target Role using role chaining
3. Generate a federated sign-in URL
4. Open the AWS Console in a new browser tab
The user will be signed into the AWS Console with the permissions of the Target Role.
</Step>
</Steps>

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

View File

@@ -97,17 +97,17 @@ export const AwsIamAccountForm = ({ account, resourceId, resourceType, onSubmit
enabled: !!resourceIdToFetch && !!resourceTypeToFetch
});
const pamRoleArn =
const resourceRoleArn =
(resource?.resourceType === PamResourceType.AwsIam &&
(resource as TAwsIamResource).connectionDetails?.roleArn) ||
"arn:aws:iam::<YOUR_ACCOUNT_ID>:role/<YOUR_PAM_ROLE_NAME>";
"arn:aws:iam::<YOUR_ACCOUNT_ID>:role/<YOUR_RESOURCE_ROLE_NAME>";
const targetRoleTrustPolicy = `{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"AWS": "${pamRoleArn}"
"AWS": "${resourceRoleArn}"
},
"Action": "sts:AssumeRole",
"Condition": {
@@ -229,10 +229,9 @@ export const AwsIamAccountForm = ({ account, resourceId, resourceType, onSubmit
</AccordionTrigger>
<AccordionContent className="px-4 pb-2.5">
<p className="mb-3 text-sm text-mineshaft-300">
The target role must have a trust policy that allows the PAM role (created in the
&quot;Resources&quot; tab) to assume it. If your target role name follows the
wildcard pattern you defined in the PAM role&apos;s permissions policy, no
additional changes are needed.
The target role must have a trust policy that allows the Resource Role (created in
the &quot;Resources&quot; tab) to assume it. Ensure the target role&apos;s trust
policy includes the Resource Role as a trusted principal.
</p>
<p className="mb-2 text-sm font-medium text-mineshaft-200">
@@ -247,12 +246,11 @@ export const AwsIamAccountForm = ({ account, resourceId, resourceType, onSubmit
</pre>
</div>
<p className="text-xs text-mineshaft-400">
<strong>Note:</strong> The Principal role ARN shown above is from the PAM Resource
<strong>Note:</strong> The Principal role ARN shown above is from the Resource
selected for this account. The External ID{" "}
<code className="rounded bg-mineshaft-700 px-1 font-bold">{projectId}</code> is your
current project ID. If your target role name doesn&apos;t match the wildcard pattern
in your PAM Resource&apos;s role&apos;s permissions policy, you&apos;ll need to
update that policy to include this role&apos;s ARN.
current project ID. If you configured granular permissions in your Resource
Role&apos;s policy, ensure this target role&apos;s ARN is included.
</p>
</AccordionContent>
</AccordionItem>

View File

@@ -1,5 +1,10 @@
import { DocumentationLinkBadge } from "@app/components/v3";
import { PAM_RESOURCE_TYPE_MAP, PamResourceType } from "@app/hooks/api/pam";
const PAM_ACCOUNT_DOCS_MAP: Partial<Record<PamResourceType, string>> = {
[PamResourceType.AwsIam]: "aws-iam#create-pam-accounts"
};
type Props = {
resourceName: string;
resourceType: PamResourceType;
@@ -8,6 +13,7 @@ type Props = {
export const PamAccountHeader = ({ resourceName, resourceType, onBack }: Props) => {
const details = PAM_RESOURCE_TYPE_MAP[resourceType];
const docsPath = PAM_ACCOUNT_DOCS_MAP[resourceType];
return (
<div className="mb-4 flex w-full items-start gap-2 border-b border-mineshaft-500 pb-4">
@@ -17,7 +23,14 @@ export const PamAccountHeader = ({ resourceName, resourceType, onBack }: Props)
className="h-12 w-12 rounded-md bg-bunker-500 p-2"
/>
<div>
<div className="flex items-center text-mineshaft-300">{resourceName}</div>
<div className="flex items-center gap-x-2 text-mineshaft-300">
{resourceName}
{docsPath && (
<DocumentationLinkBadge
href={`https://infisical.com/docs/documentation/platform/pam/resources/${docsPath}`}
/>
)}
</div>
<p className="text-sm leading-4 text-mineshaft-400">{details.name} resource</p>
</div>
{onBack && (

View File

@@ -30,7 +30,7 @@ const AwsIamConnectionDetailsSchema = z.object({
roleArn: z
.string()
.trim()
.min(1, "PAM Role ARN is required")
.min(1, "Resource Role ARN is required")
.refine((val) => arnRoleRegex.test(val), {
message: "ARN must be in the format 'arn:aws:iam::123456789012:role/RoleName'"
})
@@ -57,7 +57,7 @@ export const AwsIamResourceForm = ({ resource, onSubmit }: Props) => {
"Statement": [{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::<YOUR_ACCOUNT_ID>:role/<YOUR_PREFIX>-*"
"Resource": "arn:aws:iam::<YOUR_ACCOUNT_ID>:role/*"
}]
}`;
@@ -116,12 +116,15 @@ export const AwsIamResourceForm = ({ resource, onSubmit }: Props) => {
control={control}
render={({ field, fieldState: { error } }) => (
<FormControl
helperText="The ARN of the Infisical PAM role that can assume target roles"
helperText="The ARN of the Infisical Resource Role that can assume target roles"
errorText={error?.message}
isError={Boolean(error?.message)}
label="PAM Role ARN"
label="Resource Role ARN"
>
<Input placeholder="arn:aws:iam::123456789012:role/InfisicalPAMRole" {...field} />
<Input
placeholder="arn:aws:iam::123456789012:role/InfisicalResourceRole"
{...field}
/>
</FormControl>
)}
/>
@@ -148,12 +151,12 @@ export const AwsIamResourceForm = ({ resource, onSubmit }: Props) => {
Step 1: Create a permissions policy for assuming target roles
</p>
<p className="mb-3 text-sm text-mineshaft-300">
This policy allows the PAM role to assume target roles. We recommend using a
wildcard pattern (e.g.,{" "}
<code className="rounded bg-mineshaft-700 px-1 text-xs">pam-*</code> or{" "}
<code className="rounded bg-mineshaft-700 px-1 text-xs">privileged-*</code>) so you
can add new accounts without updating this policy. Choose a prefix that fits your
naming conventions.
This policy allows the Resource Role to assume target roles. For simplicity, use a
wildcard to allow assuming any role in your account. For more granular control,
replace <code className="rounded bg-mineshaft-700 px-1 text-xs">/*</code> with a
specific pattern like{" "}
<code className="rounded bg-mineshaft-700 px-1 text-xs">/pam-*</code> or{" "}
<code className="rounded bg-mineshaft-700 px-1 text-xs">/infisical-*</code>.
</p>
<div className="relative mb-4">
<div className="absolute top-1 right-1">
@@ -165,12 +168,12 @@ export const AwsIamResourceForm = ({ resource, onSubmit }: Props) => {
</div>
<p className="mb-2 text-sm font-medium text-mineshaft-200">
Step 2: Create the PAM role with a trust policy
Step 2: Create the Resource Role with a trust policy
</p>
<p className="mb-3 text-sm text-mineshaft-300">
Create an IAM role (e.g.,{" "}
<code className="rounded bg-mineshaft-700 px-1 text-xs">InfisicalPAMRole</code>)
with the permissions policy above and the following trust policy:
<code className="rounded bg-mineshaft-700 px-1 text-xs">InfisicalResourceRole</code>
) with the permissions policy above and the following trust policy:
</p>
<div className="relative mb-4">
<div className="absolute top-1 right-3">
@@ -189,11 +192,8 @@ export const AwsIamResourceForm = ({ resource, onSubmit }: Props) => {
<code className="rounded bg-mineshaft-700 px-1 font-bold">
{INFISICAL_AWS_ACCOUNT_EU}
</code>{" "}
for EU region. Replace{" "}
<code className="rounded bg-mineshaft-700 px-1 font-bold">
&lt;INFISICAL_AWS_ACCOUNT_ID&gt;
</code>{" "}
with the appropriate Infisical AWS account ID for your region. The External ID{" "}
for EU region. For dedicated instances, contact Infisical support. For self-hosted
instances, use your Infisical deployment&apos;s AWS account ID. The External ID{" "}
<code className="rounded bg-mineshaft-700 px-1 font-bold">{projectId}</code> is your
current project ID.
</p>

View File

@@ -1,5 +1,10 @@
import { DocumentationLinkBadge } from "@app/components/v3";
import { PAM_RESOURCE_TYPE_MAP, PamResourceType } from "@app/hooks/api/pam";
const PAM_RESOURCE_DOCS_MAP: Partial<Record<PamResourceType, string>> = {
[PamResourceType.AwsIam]: "aws-iam#create-the-pam-resource"
};
type Props = {
resourceType: PamResourceType;
onBack?: () => void;
@@ -7,6 +12,7 @@ type Props = {
export const PamResourceHeader = ({ resourceType, onBack }: Props) => {
const details = PAM_RESOURCE_TYPE_MAP[resourceType];
const docsPath = PAM_RESOURCE_DOCS_MAP[resourceType];
return (
<div className="mb-4 flex w-full items-start gap-2 border-b border-mineshaft-500 pb-4">
@@ -16,7 +22,14 @@ export const PamResourceHeader = ({ resourceType, onBack }: Props) => {
className="h-12 w-12 rounded-md bg-bunker-500 p-2"
/>
<div>
<div className="flex items-center text-mineshaft-300">{details.name}</div>
<div className="flex items-center gap-x-2 text-mineshaft-300">
{details.name}
{docsPath && (
<DocumentationLinkBadge
href={`https://infisical.com/docs/documentation/platform/pam/resources/${docsPath}`}
/>
)}
</div>
<p className="text-sm leading-4 text-mineshaft-400">Resource</p>
</div>
{onBack && (