A Pulumi package for creating and managing Amazon Web Services (AWS) cloud resources with infrastructure-as-code.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
This guide covers authentication and authorization patterns for Pulumi AWS Provider, including credential management, IAM roles, and cross-account access.
The Pulumi AWS Provider uses the AWS SDK for authentication, supporting multiple credential sources in the following precedence order:
The most common method for local development and CI/CD pipelines:
// Set environment variables before running Pulumi
// export AWS_ACCESS_KEY_ID="your-access-key"
// export AWS_SECRET_ACCESS_KEY="your-secret-key"
// export AWS_REGION="us-west-2"
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
// Provider will automatically use environment variables
const bucket = new aws.s3.Bucket("my-bucket");Key environment variables:
AWS_ACCESS_KEY_ID: Your AWS access keyAWS_SECRET_ACCESS_KEY: Your AWS secret keyAWS_SESSION_TOKEN: Session token for temporary credentialsAWS_REGION: Default AWS regionAWS_PROFILE: Named profile from credentials fileAWS_SHARED_CREDENTIALS_FILE: Custom credentials file pathAWS_CONFIG_FILE: Custom config file pathStore credentials in ~/.aws/credentials:
[default]
aws_access_key_id = AKIAIOSFODNN7EXAMPLE
aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
[production]
aws_access_key_id = AKIAI44QH8DHBEXAMPLE
aws_secret_access_key = je7MtGbClwBF/2Zp9Utk/h3yCo8nvbEXAMPLEKEY
[development]
aws_access_key_id = AKIAI44QH8DHBEXAMPLE
aws_secret_access_key = je7MtGbClwBF/2Zp9Utk/h3yCo8nvbEXAMPLEKEYUse specific profile:
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
// Use production profile
const prodProvider = new aws.Provider("prod-provider", {
profile: "production",
region: "us-east-1",
});
const bucket = new aws.s3.Bucket("prod-bucket", {}, {
provider: prodProvider,
});Store additional settings in ~/.aws/config:
[default]
region = us-west-2
output = json
[profile production]
region = us-east-1
role_arn = arn:aws:iam::123456789012:role/ProductionRole
source_profile = default
[profile development]
region = us-west-2
role_arn = arn:aws:iam::123456789012:role/DevelopmentRole
source_profile = defaultProvide credentials directly in provider configuration:
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
// Not recommended for production - use for testing only
const provider = new aws.Provider("explicit-provider", {
accessKey: "AKIAIOSFODNN7EXAMPLE",
secretKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
region: "us-west-2",
});
const bucket = new aws.s3.Bucket("test-bucket", {}, {
provider: provider,
});Store sensitive credentials in Pulumi config (encrypted):
# Set encrypted configuration values
pulumi config set aws:accessKey AKIAIOSFODNN7EXAMPLE --secret
pulumi config set aws:secretKey wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY --secret
pulumi config set aws:region us-west-2import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const config = new pulumi.Config("aws");
const provider = new aws.Provider("config-provider", {
accessKey: config.get("accessKey"),
secretKey: config.requireSecret("secretKey"),
region: config.get("region") || "us-west-2",
});
const bucket = new aws.s3.Bucket("config-bucket", {}, {
provider: provider,
});Use multiple providers for different accounts or regions:
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
// US provider
const usProvider = new aws.Provider("us-provider", {
region: "us-east-1",
profile: "production",
});
// EU provider
const euProvider = new aws.Provider("eu-provider", {
region: "eu-west-1",
profile: "production",
});
// Create resources in different regions
const usBucket = new aws.s3.Bucket("us-bucket", {}, {
provider: usProvider,
});
const euBucket = new aws.s3.Bucket("eu-bucket", {}, {
provider: euProvider,
});Assume an IAM role for resource creation:
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const provider = new aws.Provider("assume-role-provider", {
region: "us-west-2",
assumeRole: {
roleArn: "arn:aws:iam::123456789012:role/DeploymentRole",
sessionName: "pulumi-deployment",
},
});
const bucket = new aws.s3.Bucket("assumed-bucket", {}, {
provider: provider,
});Use external ID for enhanced security:
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const provider = new aws.Provider("external-id-provider", {
region: "us-west-2",
assumeRole: {
roleArn: "arn:aws:iam::123456789012:role/ThirdPartyRole",
sessionName: "pulumi-external",
externalId: "unique-external-id-12345",
},
});
const bucket = new aws.s3.Bucket("external-bucket", {}, {
provider: provider,
});Require MFA token for assume role:
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const config = new pulumi.Config();
const mfaToken = config.require("mfaToken");
const provider = new aws.Provider("mfa-provider", {
region: "us-west-2",
assumeRole: {
roleArn: "arn:aws:iam::123456789012:role/MFARequiredRole",
sessionName: "pulumi-mfa-session",
serialNumber: "arn:aws:iam::123456789012:mfa/user",
tokenCode: mfaToken,
},
});
const bucket = new aws.s3.Bucket("mfa-bucket", {}, {
provider: provider,
});Control session duration:
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const provider = new aws.Provider("duration-provider", {
region: "us-west-2",
assumeRole: {
roleArn: "arn:aws:iam::123456789012:role/LongLivedRole",
sessionName: "pulumi-long-session",
duration: "3600s", // 1 hour
},
});
const bucket = new aws.s3.Bucket("duration-bucket", {}, {
provider: provider,
});Create an IAM role that Pulumi can assume:
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
// Trust policy for the role
const assumeRolePolicy = aws.iam.getPolicyDocument({
statements: [{
effect: "Allow",
principals: [{
type: "AWS",
identifiers: ["arn:aws:iam::987654321098:root"],
}],
actions: ["sts:AssumeRole"],
conditions: [{
test: "StringEquals",
variable: "sts:ExternalId",
values: ["unique-external-id"],
}],
}],
});
// Create the role
const pulumiRole = new aws.iam.Role("pulumi-deployment-role", {
assumeRolePolicy: assumeRolePolicy.then(policy => policy.json),
description: "Role for Pulumi deployments",
});
// Attach policies
const policyAttachment = new aws.iam.RolePolicyAttachment("pulumi-policy", {
role: pulumiRole.name,
policyArn: "arn:aws:iam::aws:policy/PowerUserAccess",
});
export const roleArn = pulumiRole.arn;Authenticate using GitHub Actions OIDC provider:
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
// Create OIDC provider for GitHub
const githubOidc = new aws.iam.OpenIdConnectProvider("github-oidc", {
url: "https://token.actions.githubusercontent.com",
clientIdLists: ["sts.amazonaws.com"],
thumbprintLists: ["6938fd4d98bab03faadb97b34396831e3780aea1"],
});
// Create role that trusts GitHub OIDC
const githubRole = new aws.iam.Role("github-actions-role", {
assumeRolePolicy: pulumi.interpolate`{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Federated": "${githubOidc.arn}"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:myorg/myrepo:*"
},
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
}
}
}]
}`,
});
// Attach permissions
const githubPolicy = new aws.iam.RolePolicyAttachment("github-policy", {
role: githubRole.name,
policyArn: "arn:aws:iam::aws:policy/PowerUserAccess",
});
export const githubRoleArn = githubRole.arn;GitHub Actions workflow usage:
# .github/workflows/deploy.yml
name: Deploy
on: [push]
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v2
with:
role-to-assume: arn:aws:iam::123456789012:role/github-actions-role
aws-region: us-west-2
- name: Deploy with Pulumi
run: pulumi up --yesAuthenticate using GitLab OIDC provider:
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
// Create OIDC provider for GitLab
const gitlabOidc = new aws.iam.OpenIdConnectProvider("gitlab-oidc", {
url: "https://gitlab.com",
clientIdLists: ["https://gitlab.com"],
thumbprintLists: ["7e04e2ddc6e1e5f3f14dd0297e99c1c23f6b1d92"],
});
// Create role that trusts GitLab OIDC
const gitlabRole = new aws.iam.Role("gitlab-ci-role", {
assumeRolePolicy: pulumi.interpolate`{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Federated": "${gitlabOidc.arn}"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringLike": {
"gitlab.com:sub": "project_path:mygroup/myproject:ref_type:branch:ref:main"
}
}
}]
}`,
});
export const gitlabRoleArn = gitlabRole.arn;Access resources in another AWS account:
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
// Account A (current account) - create role
const crossAccountRole = new aws.iam.Role("cross-account-role", {
assumeRolePolicy: JSON.stringify({
Version: "2012-10-17",
Statement: [{
Effect: "Allow",
Principal: {
AWS: "arn:aws:iam::999999999999:root", // Account B
},
Action: "sts:AssumeRole",
}],
}),
});
const rolePolicy = new aws.iam.RolePolicy("cross-account-policy", {
role: crossAccountRole.id,
policy: JSON.stringify({
Version: "2012-10-17",
Statement: [{
Effect: "Allow",
Action: ["s3:*"],
Resource: "*",
}],
}),
});
// Account B (remote account) - assume role and access resources
const remoteProvider = new aws.Provider("account-b-provider", {
region: "us-west-2",
assumeRole: {
roleArn: "arn:aws:iam::123456789012:role/cross-account-role",
sessionName: "cross-account-session",
},
});
const remoteBucket = new aws.s3.Bucket("account-b-bucket", {}, {
provider: remoteProvider,
});Manage multiple accounts in an organization:
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
interface AccountConfig {
name: string;
accountId: string;
roleArn: string;
region: string;
}
const accounts: AccountConfig[] = [
{
name: "production",
accountId: "111111111111",
roleArn: "arn:aws:iam::111111111111:role/OrganizationAccountAccessRole",
region: "us-east-1",
},
{
name: "staging",
accountId: "222222222222",
roleArn: "arn:aws:iam::222222222222:role/OrganizationAccountAccessRole",
region: "us-east-1",
},
{
name: "development",
accountId: "333333333333",
roleArn: "arn:aws:iam::333333333333:role/OrganizationAccountAccessRole",
region: "us-west-2",
},
];
// Create providers for each account
const providers = accounts.map(account => ({
name: account.name,
provider: new aws.Provider(`${account.name}-provider`, {
region: account.region,
assumeRole: {
roleArn: account.roleArn,
sessionName: `pulumi-${account.name}`,
},
}),
}));
// Deploy resources to each account
providers.forEach(({ name, provider }) => {
const bucket = new aws.s3.Bucket(`${name}-bucket`, {
tags: {
Environment: name,
},
}, {
provider: provider,
});
});// BAD - Never do this
const provider = new aws.Provider("bad", {
accessKey: "AKIAIOSFODNN7EXAMPLE",
secretKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
});
// GOOD - Use environment variables or config
const provider = new aws.Provider("good", {
profile: "production",
region: "us-west-2",
});import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
// Prefer IAM roles over access keys
const provider = new aws.Provider("prod-provider", {
region: "us-west-2",
assumeRole: {
roleArn: pulumi.getStack() === "production"
? "arn:aws:iam::123456789012:role/ProductionRole"
: "arn:aws:iam::123456789012:role/DevelopmentRole",
sessionName: `pulumi-${pulumi.getStack()}`,
},
});import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
// Create role with minimal permissions
const deployRole = new aws.iam.Role("deploy-role", {
assumeRolePolicy: JSON.stringify({
Version: "2012-10-17",
Statement: [{
Effect: "Allow",
Principal: { Service: "ec2.amazonaws.com" },
Action: "sts:AssumeRole",
}],
}),
});
// Only grant necessary permissions
const policy = new aws.iam.RolePolicy("deploy-policy", {
role: deployRole.id,
policy: JSON.stringify({
Version: "2012-10-17",
Statement: [{
Effect: "Allow",
Action: [
"s3:GetObject",
"s3:PutObject",
],
Resource: "arn:aws:s3:::specific-bucket/*",
}],
}),
});import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
const provider = new aws.Provider("tagged-provider", {
region: "us-west-2",
assumeRole: {
roleArn: "arn:aws:iam::123456789012:role/DeploymentRole",
sessionName: "pulumi-deployment",
tags: {
Project: pulumi.getProject(),
Stack: pulumi.getStack(),
DeployedBy: "pulumi",
},
},
});import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
// Use temporary credentials with short duration
const provider = new aws.Provider("short-lived-provider", {
region: "us-west-2",
assumeRole: {
roleArn: "arn:aws:iam::123456789012:role/DeploymentRole",
sessionName: "pulumi-deployment",
duration: "900s", // 15 minutes
},
});Install with Tessl CLI
npx tessl i tessl/npm-pulumi--aws@7.16.0