# Pulumi Component Reference

Source: https://wraps.dev/docs/pulumi-reference

# @wraps.dev/pulumi

Pulumi component for deploying Wraps email infrastructure to your AWS account. Zero credentials stored, full AWS ownership, SST-style API with transform functions.

## Installation

Copy for AI

npmpnpmyarnbun

npm install @wraps.dev/pulumi

**Requirements:** Node.js 20+, Pulumi 3.x, @pulumi/aws 6.x or 7.x

## Quick Start

Copy for AI

index.ts

```
import { WrapsEmail } from "@wraps.dev/pulumi";// Minimal setup with Vercel OIDCconst email = new WrapsEmail("email", {  vercel: {    teamSlug: "my-team",    projectName: "my-app",  },});// Export the role ARN for your Vercel environmentexport const roleArn = email.roleArn;export const configSetName = email.configSetName;export const envVars = email.envVars;
```

## With Domain and Event Tracking

Copy for AI

with-domain.ts

```
const email = new WrapsEmail("email", {  vercel: {    teamSlug: "my-team",    projectName: "my-app",  },  domain: "example.com",  events: {    types: ["SEND", "DELIVERY", "BOUNCE", "COMPLAINT", "OPEN", "CLICK"],    storeHistory: true,    retention: "90days",  },});// Export DKIM tokens for DNS configurationexport const dkimTokens = email.dkimTokens;
```

## Full Configuration

Copy for AI

full-config.ts

```
const email = new WrapsEmail("email", {  // Authentication (choose one)  vercel: {    teamSlug: "my-team",    projectName: "my-app",  },  // Or custom OIDC:  // oidc: {  //   providerUrl: "https://token.actions.githubusercontent.com",  //   audience: "sts.amazonaws.com",  //   subjectPattern: "repo:my-org/my-repo:*",  // },  // Domain configuration  domain: "example.com",  mailFromSubdomain: "mail", // Creates mail.example.com  // DNS provider (auto-creates DNS records)  dns: {    provider: "route53",    hostedZoneId: "Z1234567890",  },  // Or Cloudflare:  // dns: { provider: "cloudflare", zoneId: "abc123", apiToken: pulumi.secret("token") },  // Event tracking  events: {    types: ["SEND", "DELIVERY", "BOUNCE", "COMPLAINT", "OPEN", "CLICK"],    storeHistory: true,    retention: "90days", // "7days" | "30days" | "90days" | "6months" | "1year" | "unlimited"  },  // Email settings  reputationMetrics: true,  tlsRequired: false,  sendingEnabled: true,  // Suppression list  suppressionList: {    enabled: true,    reasons: ["BOUNCE", "COMPLAINT"],  },  // SMTP credentials (for legacy systems)  smtp: {    enabled: false,  },  // Resource tags  tags: {    Environment: "production",  },  // Transform underlying resources  transform: {    table: (args) => ({      ...args,      billingMode: "PROVISIONED",    }),  },});
```

## Constructor Args

Copy for AI

| Property | Type | Description |
| --- | --- | --- |
| `vercel` | VercelOIDCConfig | Vercel OIDC configuration (teamSlug, projectName) |
| `oidc` | OIDCConfig | Custom OIDC provider (GitHub Actions, GitLab, etc.) |
| `domain` | string | Primary sending domain with DKIM |
| `dns` | DNSConfig | DNS provider for automatic record creation |
| `mailFromSubdomain` | string | MAIL FROM subdomain (default: "mail") |
| `events` | EventsConfig | Event tracking and history storage |
| `smtp` | SMTPConfig | SMTP credentials for legacy systems |
| `reputationMetrics` | boolean | Enable SES reputation metrics (default: true) |
| `tags` | Record<string, string> | Tags to apply to all resources |
| `transform` | TransformFunctions | Transform functions for resource customization |

## Outputs

Copy for AI

| Property | Type | Description |
| --- | --- | --- |
| `roleArn` | Output<string> | IAM role ARN for SDK authentication |
| `region` | Output<string> | AWS region |
| `configSetName` | Output<string> | SES configuration set name |
| `dkimTokens` | Output<string\[\]> | DKIM tokens for DNS configuration |
| `tableName` | Output<string> | DynamoDB table name (if history enabled) |
| `queueUrl` | Output<string> | SQS queue URL (if events enabled) |
| `smtpUsername` | Output<string> | SMTP username (if SMTP enabled) |
| `smtpPassword` | Output<string> | SMTP password (if SMTP enabled) |
| `envVars` | Output<object> | Environment variables for your app |

## Transform Functions

Copy for AI

Transform functions let you customize underlying Pulumi resources before creation. Each function receives the default resource args and returns modified args.

transform.ts

```
const email = new WrapsEmail("email", {  vercel: { teamSlug: "my-team", projectName: "my-app" },  domain: "example.com",  events: { types: ["SEND"], storeHistory: true },  // Transform underlying Pulumi resources before creation  transform: {    // Customize IAM role    role: (args) => ({      ...args,      maxSessionDuration: 7200, // 2 hours    }),    // Customize DynamoDB table    table: (args) => ({      ...args,      billingMode: "PROVISIONED",      readCapacity: 5,      writeCapacity: 5,    }),    // Customize SQS queue    queue: (args) => ({      ...args,      visibilityTimeoutSeconds: 120,    }),    // Customize Lambda function    lambda: (args) => ({      ...args,      memorySize: 1024,      timeout: 60,    }),    // Customize CloudFront distribution    distribution: (args) => ({      ...args,      priceClass: "PriceClass_100", // US/Europe only    }),  },});
```

#### Available Transform Functions

| Function | Resource Type | When Available |
| --- | --- | --- |
| `role` | aws.iam.RoleArgs | Always |
| `oidcProvider` | aws.iam.OpenIdConnectProviderArgs | If OIDC configured |
| `configSet` | aws.ses.ConfigurationSetArgs | Always |
| `table` | aws.dynamodb.TableArgs | If events.storeHistory |
| `queue` | aws.sqs.QueueArgs | If events configured |
| `lambda` | aws.lambda.FunctionArgs | If events.storeHistory |
| `distribution` | aws.cloudfront.DistributionArgs | If HTTPS tracking |

## Accessing Underlying Resources

Copy for AI

Access the underlying Pulumi resources via the `.nodes` property for advanced customization.

nodes.ts

```
const email = new WrapsEmail("email", { /* ... */ });// Access raw Pulumi resources via .nodesemail.nodes.role;           // aws.iam.Roleemail.nodes.configSet;      // aws.ses.ConfigurationSetemail.nodes.domainIdentity; // aws.ses.DomainIdentity (if domain)email.nodes.domainDkim;     // aws.ses.DomainDkim (if domain)email.nodes.table;          // aws.dynamodb.Table (if events.storeHistory)email.nodes.queue;          // aws.sqs.Queue (if events)email.nodes.dlq;            // aws.sqs.Queue (if events)email.nodes.lambda;         // aws.lambda.Function (if events.storeHistory)email.nodes.eventRule;      // aws.cloudwatch.EventRule (if events)email.nodes.oidcProvider;   // aws.iam.OpenIdConnectProvider (if OIDC)email.nodes.distribution;   // aws.cloudfront.Distribution (if HTTPS tracking)// Example: Add custom policy to roleconst customPolicy = new aws.iam.RolePolicy("custom", {  role: email.nodes.role.name,  policy: JSON.stringify({    Version: "2012-10-17",    Statement: [{      Action: ["ses:ListIdentities"],      Effect: "Allow",      Resource: "*",    }],  }),});
```

#### Available Nodes

| Property | Type | When Available |
| --- | --- | --- |
| `role` | aws.iam.Role | Always |
| `oidcProvider` | aws.iam.OpenIdConnectProvider | If OIDC configured |
| `configSet` | aws.ses.ConfigurationSet | Always |
| `domainIdentity` | aws.ses.DomainIdentity | If domain configured |
| `table` | aws.dynamodb.Table | If events.storeHistory |
| `queue` | aws.sqs.Queue | If events configured |
| `lambda` | aws.lambda.Function | If events.storeHistory |
| `distribution` | aws.cloudfront.Distribution | If HTTPS tracking |

## DNS Providers

Copy for AI

Automatically create DNS records with your preferred provider.

dns-providers.ts

```
// Route53 (automatic DNS records)const emailRoute53 = new WrapsEmail("email", {  domain: "example.com",  dns: {    provider: "route53",    hostedZoneId: "Z1234567890",  },});// Cloudflareconst emailCloudflare = new WrapsEmail("email", {  domain: "example.com",  dns: {    provider: "cloudflare",    zoneId: "abc123def456",    apiToken: pulumi.secret(process.env.CLOUDFLARE_API_TOKEN!),  },});// Vercel DNSconst emailVercel = new WrapsEmail("email", {  domain: "example.com",  dns: {    provider: "vercel",    apiToken: pulumi.secret(process.env.VERCEL_TOKEN!),    teamId: "team_xxx",  },});
```

## GitHub Actions OIDC

Copy for AI

Use custom OIDC configuration for GitHub Actions or other providers.

github-actions.ts

```
const email = new WrapsEmail("email", {  oidc: {    providerUrl: "https://token.actions.githubusercontent.com",    audience: "sts.amazonaws.com",    subjectPattern: "repo:my-org/my-repo:*",  },  domain: "example.com",});
```

## Environment Variables Output

Copy for AI

Convenience output for environment variables to configure your application.

env-vars.ts

```
const email = new WrapsEmail("email", { /* ... */ });// Convenience output for environment variablesexport const envVars = email.envVars;// Returns:// {//   WRAPS_AWS_ROLE_ARN: "arn:aws:iam::123456789:role/wraps-email-role",//   WRAPS_AWS_REGION: "us-east-1",//   WRAPS_CONFIG_SET: "wraps-email-tracking",// }
```

## Using with @wraps.dev/email SDK

Copy for AI

After deploying, use the @wraps.dev/email SDK to send emails:

send-email.ts

```
import { WrapsEmail } from "@wraps.dev/email";const email = new WrapsEmail();await email.send({  from: "hello@example.com",  to: "user@example.com",  subject: "Hello from Wraps!",  html: "<h1>Welcome!</h1>",});
```

## Differences from the CLI

Copy for AI

This library and `npx @wraps.dev/cli email init` both deploy the same email stack, but they are two independent implementations, not one codebase with two entry points. They are not identical. This section states the differences plainly, not as a case for either one.

### IAM permissions on the deployed role (`wraps-email-role`)

| Grant | CLI (email init) | This library |
| --- | --- | --- |
| Suppression list management (`ListSuppressedDestinations`, `GetSuppressedDestination`, `PutSuppressedDestination`, `DeleteSuppressedDestination`) | Yes | No |
| `ses:GetSendQuota` | No | Yes |
| `cloudwatch:ListMetrics` | No | Yes |

Sending, DynamoDB history access, EventBridge, SQS, and Mail Manager archive access are granted identically by both.

### No replay archive, no delivery-failure alarm

The CLI path creates a 30-day EventBridge archive (`wraps-email-events-archive`) of every raw SES event, so a broken pipeline can be repaired and replayed instead of losing events, plus a CloudWatch alarm on EventBridge delivery failures. This library creates neither. The [BYOC page](https://wraps.dev/byoc) describes the event pipeline's replay path. That description is accurate for the CLI, not for this library.

### No inbound email

The CLI supports receiving email (`wraps email inbound init`): an S3 bucket, a receiving Lambda, and an EventBridge rule for inbound mail. This library has no equivalent resources at all.

### What CI checks, and what it does not

Nothing in this monorepo depends on this package, so no build or test run here exercises it against the CLI's actual behavior. A small set of guards keeps the two from drifting on the values that are supposed to be identical: physical resource names, the default SES event-type set, and the EventBridge rule's event pattern. Those guards do not cover IAM statement shape or feature coverage. The differences on this page are not something a test enforces.

## Next Steps

View on npm

Check out the package on npm for the latest version and changelog.

[View Package](https://www.npmjs.com/package/@wraps.dev/pulumi)

Email SDK

Learn how to send emails with the @wraps.dev/email SDK.

[SDK Reference](https://wraps.dev/docs/sdk-reference)

Pulumi Docs

Learn more about Pulumi and infrastructure as code.

[Pulumi Docs](https://www.pulumi.com/docs/)
