Skip to main content
Outcome: After this guide, you can manage Formal resources using Pulumi infrastructure-as-code.
Prerequisites: Pulumi CLI, Formal API key, programming language environment (TypeScript, Python, Go, or C#).

Overview

The Formal Pulumi Provider enables you to manage Connectors, Resources, Policies, Users, and all other Formal objects as infrastructure-as-code using your preferred programming language.

Installation

The Formal provider is available for multiple programming languages:
npm install @formalco/pulumi

Authentication

Create an API key in the Formal console:
  1. Navigate to API Keys
  2. Click Create API Key
  3. Name the key (e.g., “pulumi-production”)
  4. Copy the key immediately
Set the key using Pulumi’s configuration system:
pulumi config set formal:apiKey <YOUR_API_KEY> --secret
Verify:
pulumi config get formal:apiKey
# Expected: <YOUR_API_KEY>
Alternatively, set as an environment variable:
export FORMAL_API_KEY="<YOUR_API_KEY>"

Examples

Full Production Stack

import * as pulumi from "@pulumi/pulumi";
import * as formal from "@formalco/pulumi";

// Create a Space
const productionSpace = new formal.Space("production", {
    name: "production"
});

// Create a Resource
const postgresResource = new formal.Resource("postgres", {
    name: "production-postgres",
    technology: "postgres",
    hostname: "prod-db.us-east-1.rds.amazonaws.com",
    port: 5432,
    spaceId: productionSpace.id,
    terminationProtection: true
});

// Create a Connector
const productionConnector = new formal.Connector("production", {
    name: "production-connector",
    spaceId: productionSpace.id,
    listener: [{
        port: 5432,
        rule: [{
            type: "resource",
            resourceId: postgresResource.id
        }]
    }]
});

// Create Users
const aliceUser = new formal.User("alice", {
    name: "Alice Smith",
    email: "alice@example.com",
    type: "human"
});

const lookerApp = new formal.User("looker_app", {
    name: "Looker Application",
    type: "machine"
});

// Create Groups
const engineeringGroup = new formal.Group("engineering", {
    name: "Engineering Team"
});

const aliceMembership = new formal.GroupMembership("alice_eng", {
    groupId: engineeringGroup.id,
    userId: aliceUser.id
});

// Create a Policy
const maskPiiPolicy = new formal.Policy("mask_pii", {
    name: "mask-pii-data",
    description: "Mask PII fields for non-privileged users",
    status: "active",
    owners: ["admin@example.com"],
    code: `package formal.v2

import future.keywords.if
import future.keywords.in

post_request := {
"action": "mask",
"type": "nullify",
"columns": pii_columns
} if {
not "pii_access" in input.user.groups

pii_columns := [col |
col := input.row[_]
col["data_label"] in ["email", "ssn", "phone"]
]

count(pii_columns) > 0
}`
});

// AWS Integration
const awsIntegration = new formal.IntegrationCloud("aws", {
    name: "aws-production",
    cloudRegion: "us-east-1",
    aws: {
        templateVersion: "1.2.0",
        enableRdsAutodiscovery: true,
        allowS3Access: true,
        s3BucketArn: "<S3_BUCKET_ARN>/*"
    }
});

// Log Integration
const datadogIntegration = new formal.IntegrationLog("datadog", {
    name: "datadog-logs",
    datadog: {
        accountId: "<DATADOG_APPLICATION_KEY>",
        apiKey: "<DATADOG_API_KEY>",
        site: "datadoghq.com"
    }
});

// Export outputs
export const connectorToken = productionConnector.apiKey;
export const connectorListeners = productionConnector.listener.map(l => l.port);
export const resourceIds = {
    postgres: postgresResource.id
};

Resource Documentation

Full documentation for all resources: Core Objects: Integrations:

Example Repositories

Formal provides complete Pulumi examples: Clone and customize for your needs:
git clone https://github.com/formalco/pulumi-formal.git
cd pulumi-formal/examples/deployments/aws/connector

Best Practices

Pin provider versions to avoid unexpected changes:TypeScript:
{
  "dependencies": {
    "@formalco/pulumi": "^1.0.0"
  }
}
Python:
pulumi-formal==1.0.0
Use Pulumi Cloud or self-hosted backends for team collaboration:
pulumi login https://api.pulumi.com
pulumi stack init production
Parameterize configurations for reusability:
pulumi config set environment production
pulumi config set formal:apiKey <KEY> --secret
Access in code:
const config = new pulumi.Config();
const environment = config.require("environment");
Use separate stacks for prod/staging/dev:
pulumi stack init production
pulumi stack init staging
pulumi stack init development
Protect production resources:
const resource = new formal.Resource("prod_db", {
    name: "production-postgres",
    terminationProtection: true,
    // ...
});

Importing Existing Resources

Bring existing Formal objects under Pulumi management:
# Import a Resource
pulumi import formal:index/resource:Resource postgres <resource-id>

# Import a Connector
pulumi import formal:index/connector:Connector main <connector-id>

# Import a User
pulumi import formal:index/user:User alice <user-id>
Get object IDs from the Formal console or API.

Outputs

Export useful information:
export const connectorToken = productionConnector.apiKey;
export const connectorListeners = productionConnector.listener.map(l => l.port);
export const resourceIds = {
    postgres: postgresResource.id
};

Troubleshooting

Possible causes:
  • Invalid API key
  • API key not set correctly
Solution:
  1. Verify API key in Formal console
  2. Check configuration: pulumi config get formal:apiKey
  3. Ensure no whitespace in key
Possible causes:
  • Package not installed
  • Wrong package name
Solution:
  1. Install correct package for your language
  2. Verify import statements
  3. Check package.json/requirements.txt
Possible causes:
  • Invalid resource configuration
  • Missing required fields
Solution:
  1. Check API documentation
  2. Verify all required fields are set
  3. Check resource naming conventions

Next Steps

Provider Docs

Complete Pulumi provider documentation

Examples

Deployment examples on GitHub

Pulumi Cloud

Manage state and collaborate

Terraform Provider

Alternative IaC solution