> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fjall.io/llms.txt
> Use this file to discover all available pages before exploring further.

# IAM Role

> Configure AWS IAM roles for service access control and least-privilege permissions in Fjall infrastructure.

## Overview

The IAM Role resource provides identity-based access control for AWS services and resources. It is a thin wrapper around the CDK IAM Role with the same properties and methods, used throughout Fjall for service permissions.

## Resource Class

```typescript theme={null}
import { Role } from "@fjall/components-infrastructure/lib/resources/aws/iam/role";
```

## Basic Usage

```typescript theme={null}
const role = new Role(this, "MyRole", {
  assumedBy: new ServicePrincipal("lambda.amazonaws.com"),
  description: "Lambda execution role",
});
```

## Configuration Options

All standard CDK Role properties are supported:

| Property             | Type                               | Description                         |
| -------------------- | ---------------------------------- | ----------------------------------- |
| `assumedBy`          | `IPrincipal`                       | Principal that can assume this role |
| `description`        | `string`                           | Role description                    |
| `roleName`           | `string`                           | Custom role name                    |
| `managedPolicies`    | `IManagedPolicy[]`                 | AWS managed policies                |
| `inlinePolicies`     | `{[name: string]: PolicyDocument}` | Inline policies                     |
| `maxSessionDuration` | `Duration`                         | Maximum session duration            |
| `path`               | `string`                           | Path for the role                   |

## Common Patterns

### Lambda Execution Role

```typescript theme={null}
const lambdaRole = new Role(this, "LambdaRole", {
  assumedBy: new ServicePrincipal("lambda.amazonaws.com"),
  description: "Lambda function execution role",
  managedPolicies: [
    ManagedPolicy.fromAwsManagedPolicyName(
      "service-role/AWSLambdaBasicExecutionRole",
    ),
  ],
  inlinePolicies: {
    S3Access: new PolicyDocument({
      statements: [
        new PolicyStatement({
          actions: ["s3:GetObject", "s3:PutObject"],
          resources: ["arn:aws:s3:::my-bucket/*"],
        }),
      ],
    }),
  },
});
```

### ECS Task Role

```typescript theme={null}
const taskRole = new Role(this, "TaskRole", {
  assumedBy: new ServicePrincipal("ecs-tasks.amazonaws.com"),
  description: "ECS task role",
  inlinePolicies: {
    SecretsAccess: new PolicyDocument({
      statements: [
        new PolicyStatement({
          actions: ["secretsmanager:GetSecretValue", "kms:Decrypt"],
          resources: ["*"],
        }),
      ],
    }),
  },
});
```

### EC2 Instance Role

```typescript theme={null}
const instanceRole = new Role(this, "InstanceRole", {
  assumedBy: new ServicePrincipal("ec2.amazonaws.com"),
  description: "EC2 instance role",
  managedPolicies: [
    ManagedPolicy.fromAwsManagedPolicyName("AmazonSSMManagedInstanceCore"),
    ManagedPolicy.fromAwsManagedPolicyName("CloudWatchAgentServerPolicy"),
  ],
});
```

## Service Principals

### AWS Service Principals

```typescript theme={null}
// Lambda
new ServicePrincipal("lambda.amazonaws.com");

// ECS Tasks
new ServicePrincipal("ecs-tasks.amazonaws.com");

// EC2
new ServicePrincipal("ec2.amazonaws.com");

// CodeBuild
new ServicePrincipal("codebuild.amazonaws.com");

// Step Functions
new ServicePrincipal("states.amazonaws.com");

// Glue
new ServicePrincipal("glue.amazonaws.com");
```

### Federated Principals

```typescript theme={null}
// GitHub Actions OIDC
const githubRole = new Role(this, "GitHubActionsRole", {
  assumedBy: new OpenIdConnectPrincipal(oidcProvider, {
    StringEquals: {
      "token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
      "token.actions.githubusercontent.com:sub":
        "repo:myorg/myrepo:ref:refs/heads/main",
    },
  }),
  description: "GitHub Actions deployment role",
});
```

### Account Principals

```typescript theme={null}
// Cross-account access
const crossAccountRole = new Role(this, "CrossAccountRole", {
  assumedBy: new AccountPrincipal("123456789012"),
  description: "Cross-account access role",
});
```

## Inline Policies

### Single Statement

```typescript theme={null}
const role = new Role(this, "SinglePolicyRole", {
  assumedBy: new ServicePrincipal("lambda.amazonaws.com"),
  inlinePolicies: {
    DynamoAccess: new PolicyDocument({
      statements: [
        new PolicyStatement({
          actions: ["dynamodb:GetItem", "dynamodb:PutItem"],
          resources: [table.tableArn],
        }),
      ],
    }),
  },
});
```

### Multiple Policies

```typescript theme={null}
const role = new Role(this, "MultiPolicyRole", {
  assumedBy: new ServicePrincipal("ecs-tasks.amazonaws.com"),
  inlinePolicies: {
    S3Access: new PolicyDocument({
      statements: [
        new PolicyStatement({
          actions: ["s3:GetObject"],
          resources: ["arn:aws:s3:::config-bucket/*"],
        }),
      ],
    }),
    SQSAccess: new PolicyDocument({
      statements: [
        new PolicyStatement({
          actions: ["sqs:SendMessage", "sqs:ReceiveMessage"],
          resources: [queue.queueArn],
        }),
      ],
    }),
    SecretsAccess: new PolicyDocument({
      statements: [
        new PolicyStatement({
          actions: ["secretsmanager:GetSecretValue"],
          resources: [secret.secretArn],
        }),
      ],
    }),
  },
});
```

## Managed Policies

### AWS Managed Policies

```typescript theme={null}
const role = new Role(this, "ManagedPolicyRole", {
  assumedBy: new ServicePrincipal("lambda.amazonaws.com"),
  managedPolicies: [
    ManagedPolicy.fromAwsManagedPolicyName(
      "service-role/AWSLambdaBasicExecutionRole",
    ),
    ManagedPolicy.fromAwsManagedPolicyName("AWSXRayDaemonWriteAccess"),
  ],
});
```

### Customer Managed Policies

```typescript theme={null}
const customPolicy = new ManagedPolicy(this, "CustomPolicy", {
  statements: [
    new PolicyStatement({
      actions: ["s3:ListBucket"],
      resources: ["arn:aws:s3:::my-bucket"],
    }),
  ],
});

const role = new Role(this, "CustomManagedRole", {
  assumedBy: new ServicePrincipal("lambda.amazonaws.com"),
  managedPolicies: [customPolicy],
});
```

## Adding Permissions

### Add to Policy

```typescript theme={null}
const role = new Role(this, "ExpandableRole", {
  assumedBy: new ServicePrincipal("lambda.amazonaws.com"),
});

// Add permissions later
role.addToPolicy(
  new PolicyStatement({
    actions: ["s3:GetObject"],
    resources: ["arn:aws:s3:::my-bucket/*"],
  }),
);

// Add managed policy
role.addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName("ReadOnlyAccess"));
```

### Grant Methods

```typescript theme={null}
const role = new Role(this, "GrantRole", {
  assumedBy: new ServicePrincipal("lambda.amazonaws.com"),
});

// Use grant methods from resources
bucket.grantRead(role);
table.grantReadWriteData(role);
secret.grantRead(role);
topic.grantPublish(role);
```

## Trust Relationships

### Basic Trust Policy

```typescript theme={null}
const role = new Role(this, "TrustRole", {
  assumedBy: new CompositePrincipal(
    new ServicePrincipal("lambda.amazonaws.com"),
    new ServicePrincipal("events.amazonaws.com"),
  ),
});
```

### Conditional Trust

```typescript theme={null}
const role = new Role(this, "ConditionalRole", {
  assumedBy: new ServicePrincipal("lambda.amazonaws.com").withConditions({
    StringEquals: {
      "sts:ExternalId": "unique-external-id",
    },
  }),
});
```

### Session Tags

```typescript theme={null}
const role = new Role(this, "SessionTagRole", {
  assumedBy: new ServicePrincipal("lambda.amazonaws.com").withSessionTags(),
  description: "Role with session tag support",
});
```

## Role Boundaries

### Permission Boundaries

```typescript theme={null}
const boundary = ManagedPolicy.fromManagedPolicyArn(
  this,
  "Boundary",
  "arn:aws:iam::123456789012:policy/BoundaryPolicy",
);

const role = new Role(this, "BoundedRole", {
  assumedBy: new ServicePrincipal("lambda.amazonaws.com"),
  permissionsBoundary: boundary,
});
```

## Integration Examples

### With Lambda Function

```typescript theme={null}
const executionRole = new Role(this, "LambdaExecutionRole", {
  assumedBy: new ServicePrincipal("lambda.amazonaws.com"),
  managedPolicies: [
    ManagedPolicy.fromAwsManagedPolicyName(
      "service-role/AWSLambdaBasicExecutionRole",
    ),
  ],
});

const fn = new Function(this, "MyFunction", {
  runtime: Runtime.NODEJS_18_X,
  handler: "index.handler",
  code: Code.fromAsset("lambda"),
  role: executionRole,
});
```

### With ECS Task

```typescript theme={null}
const taskRole = new Role(this, "TaskRole", {
  assumedBy: new ServicePrincipal("ecs-tasks.amazonaws.com"),
});

const executionRole = new Role(this, "ExecutionRole", {
  assumedBy: new ServicePrincipal("ecs-tasks.amazonaws.com"),
  managedPolicies: [
    ManagedPolicy.fromAwsManagedPolicyName(
      "service-role/AmazonECSTaskExecutionRolePolicy",
    ),
  ],
});

const taskDefinition = new FargateTaskDefinition(this, "TaskDef", {
  taskRole: taskRole,
  executionRole: executionRole,
});
```

### With CodeBuild

```typescript theme={null}
const buildRole = new Role(this, "CodeBuildRole", {
  assumedBy: new ServicePrincipal("codebuild.amazonaws.com"),
  inlinePolicies: {
    BuildPolicy: new PolicyDocument({
      statements: [
        new PolicyStatement({
          actions: [
            "logs:CreateLogGroup",
            "logs:CreateLogStream",
            "logs:PutLogEvents",
          ],
          resources: ["*"],
        }),
        new PolicyStatement({
          actions: ["ecr:*"],
          resources: [repository.repositoryArn],
        }),
      ],
    }),
  },
});
```

## Complete Example

```typescript theme={null}
import { Role } from "@fjall/components-infrastructure/lib/resources/aws/iam/role";
import {
  ServicePrincipal,
  ManagedPolicy,
  PolicyDocument,
  PolicyStatement,
  CompositePrincipal,
} from "aws-cdk-lib/aws-iam";

// Application service role with full permissions
const appServiceRole = new Role(this, "ApplicationServiceRole", {
  roleName: `${props.appName}-service-role-${props.environment}`,
  description: "Main application service role",

  // Multiple services can assume this role
  assumedBy: new CompositePrincipal(
    new ServicePrincipal("lambda.amazonaws.com"),
    new ServicePrincipal("ecs-tasks.amazonaws.com"),
  ),

  // AWS managed policies
  managedPolicies: [
    ManagedPolicy.fromAwsManagedPolicyName(
      "service-role/AWSLambdaBasicExecutionRole",
    ),
    ManagedPolicy.fromAwsManagedPolicyName("AWSXRayDaemonWriteAccess"),
  ],

  // Inline policies for specific access
  inlinePolicies: {
    DatabaseAccess: new PolicyDocument({
      statements: [
        new PolicyStatement({
          sid: "RDSDataAccess",
          actions: [
            "rds-data:ExecuteStatement",
            "rds-data:BatchExecuteStatement",
          ],
          resources: ["arn:aws:rds:us-east-1:123456789012:cluster:api-db"],
        }),
      ],
    }),

    StorageAccess: new PolicyDocument({
      statements: [
        new PolicyStatement({
          sid: "S3BucketAccess",
          actions: ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
          resources: [`${bucket.bucketArn}/*`],
        }),
        new PolicyStatement({
          sid: "S3ListAccess",
          actions: ["s3:ListBucket"],
          resources: [bucket.bucketArn],
          conditions: {
            StringLike: {
              "s3:prefix": ["uploads/*", "processed/*"],
            },
          },
        }),
      ],
    }),

    SecretsAccess: new PolicyDocument({
      statements: [
        new PolicyStatement({
          sid: "GetSecrets",
          actions: [
            "secretsmanager:GetSecretValue",
            "secretsmanager:DescribeSecret",
          ],
          resources: [appSecret.secretArn, dbSecret.secretArn],
        }),
        new PolicyStatement({
          sid: "KMSDecrypt",
          actions: ["kms:Decrypt", "kms:DescribeKey"],
          resources: ["*"],
          conditions: {
            StringEquals: {
              "kms:ViaService": `secretsmanager.${this.region}.amazonaws.com`,
            },
          },
        }),
      ],
    }),
  },

  // Session duration
  maxSessionDuration: Duration.hours(1),
});

// Grant additional permissions from resources
dynamoTable.grantReadWriteData(appServiceRole);
queue.grantConsumeMessages(appServiceRole);
topic.grantPublish(appServiceRole);

// Add tags for compliance
Tags.of(appServiceRole).add("Environment", props.environment);
Tags.of(appServiceRole).add("Application", props.appName);
Tags.of(appServiceRole).add("ManagedBy", "Fjall");

// Output role ARN
new CfnOutput(this, "ServiceRoleArn", {
  value: appServiceRole.roleArn,
  description: "Application service role ARN",
});
```

## Best Practices

1. **Use least privilege** - Only grant required permissions
2. **Prefer managed policies** for common patterns
3. **Use conditions** to restrict access further
4. **Enable MFA** for sensitive roles
5. **Set appropriate session duration**
6. **Use permission boundaries** in multi-tenant environments
7. **Tag roles** for cost allocation and compliance

## Security Considerations

### Avoid Wildcard Permissions

```typescript theme={null}
// Bad - Too permissive
new PolicyStatement({
  actions: ["*"],
  resources: ["*"],
});

// Good - Specific permissions
new PolicyStatement({
  actions: ["s3:GetObject", "s3:PutObject"],
  resources: ["arn:aws:s3:::my-bucket/uploads/*"],
});
```

### Use Conditions

```typescript theme={null}
// IP restrictions
new PolicyStatement({
  actions: ["s3:*"],
  resources: ["*"],
  conditions: {
    IpAddress: {
      "aws:SourceIp": ["203.0.113.0/24"],
    },
  },
});

// Time-based access
new PolicyStatement({
  actions: ["ec2:TerminateInstances"],
  resources: ["*"],
  conditions: {
    DateGreaterThan: {
      "aws:CurrentTime": "2026-01-01T00:00:00Z",
    },
    DateLessThan: {
      "aws:CurrentTime": "2026-12-31T23:59:59Z",
    },
  },
});
```

## Troubleshooting

### Common Issues

1. **Access denied**: Check trust policy and permissions
2. **Invalid principal**: Verify service principal format
3. **Policy size limit**: Use managed policies for large policies
4. **Circular dependencies**: Use `addToPolicy` after creation

### Debug Commands

```bash theme={null}
# Get role details
aws iam get-role --role-name MyRole

# List role policies
aws iam list-role-policies --role-name MyRole
aws iam list-attached-role-policies --role-name MyRole

# Simulate policy
aws iam simulate-principal-policy \
  --policy-source-arn arn:aws:iam::123456789012:role/MyRole \
  --action-names s3:GetObject \
  --resource-arns arn:aws:s3:::my-bucket/*
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Secrets Manager" icon="key" href="/resources/security/secrets-manager">
    Store and grant access to credentials and API keys.
  </Card>

  <Card title="KMS Key" icon="lock" href="/resources/security/kms-key">
    Encrypt data and scope decryption to specific roles.
  </Card>

  <Card title="Lambda Function" icon="bolt" href="/resources/compute/lambda-function">
    Attach an execution role to a serverless function.
  </Card>

  <Card title="ECS Cluster" icon="server" href="/resources/compute/ecs-cluster">
    Assign task and execution roles to containers.
  </Card>
</CardGroup>
