Agent instructions, skills, SSH host aliases and expansion triggers are worth having identical on every machine one person owns, and belong in none of the shared configuration. They live in user/ now, with a manifest saying where each piece goes and a link-user stage that puts it there. That stage does nothing unless the machine said yes. Somebody who clones Panama to try the desktop keeps their own ~/.claude/CLAUDE.md exactly where it was; the question names the destinations and defaults to no. Anything displaced goes to config/old rather than being deleted. ~/.claude/CLAUDE.md and ~/.codex/AGENTS.md were byte-identical copies of one file, which is the drift this exists to prevent. Also adds the vitals toggles for the battery and Claude usage readouts, which had preferences and no way to reach them.
418 lines
9.7 KiB
Markdown
418 lines
9.7 KiB
Markdown
# Resources & Data Sources
|
|
|
|
Guide to Infisical Terraform resources and data sources for managing secrets, roles, and access policies.
|
|
|
|
## Ephemeral Resource: infisical_secret
|
|
|
|
The `ephemeral` resource is the **recommended way** to fetch secrets in Terraform 1.10+. Secret values are **never stored in state**.
|
|
|
|
**Key Benefit**: Prevents secrets from being persisted in your Terraform state files, reducing security risk.
|
|
|
|
**Requires**: Terraform 1.10 or later.
|
|
|
|
### Configuration
|
|
|
|
```hcl
|
|
ephemeral "infisical_secret" "example" {
|
|
workspace_id = "your-workspace-id"
|
|
env_slug = "prod"
|
|
secret_key = "DATABASE_PASSWORD"
|
|
folder_path = "/" # Optional, defaults to "/"
|
|
}
|
|
|
|
output "db_password" {
|
|
value = ephemeral.infisical_secret.example.value
|
|
sensitive = true
|
|
}
|
|
```
|
|
|
|
### Attributes
|
|
|
|
| Attribute | Type | Description |
|
|
|-----------|------|-------------|
|
|
| `workspace_id` | string | ID of the Infisical workspace |
|
|
| `env_slug` | string | Environment slug (e.g., "dev", "staging", "prod") |
|
|
| `secret_key` | string | Name of the secret to retrieve |
|
|
| `folder_path` | string | Path within the environment (optional, defaults to "/") |
|
|
| `value` | string (computed) | The secret value (only available during apply, never stored in state) |
|
|
|
|
### JSON Secrets
|
|
|
|
For JSON-formatted secrets, use `jsondecode()` to parse the value:
|
|
|
|
```hcl
|
|
ephemeral "infisical_secret" "api_config" {
|
|
workspace_id = "your-workspace-id"
|
|
env_slug = "prod"
|
|
secret_key = "API_CONFIG"
|
|
}
|
|
|
|
locals {
|
|
config = jsondecode(ephemeral.infisical_secret.api_config.value)
|
|
}
|
|
|
|
output "api_key" {
|
|
value = local.config.api_key
|
|
sensitive = true
|
|
}
|
|
|
|
output "api_url" {
|
|
value = local.config.api_url
|
|
}
|
|
```
|
|
|
|
### Usage with Resources
|
|
|
|
```hcl
|
|
# Fetch secret and use it to configure a provider
|
|
ephemeral "infisical_secret" "aws_access_key" {
|
|
workspace_id = "your-workspace-id"
|
|
env_slug = "prod"
|
|
secret_key = "AWS_ACCESS_KEY_ID"
|
|
}
|
|
|
|
ephemeral "infisical_secret" "aws_secret_key" {
|
|
workspace_id = "your-workspace-id"
|
|
env_slug = "prod"
|
|
secret_key = "AWS_SECRET_ACCESS_KEY"
|
|
}
|
|
|
|
provider "aws" {
|
|
access_key = ephemeral.infisical_secret.aws_access_key.value
|
|
secret_key = ephemeral.infisical_secret.aws_secret_key.value
|
|
region = "us-east-1"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Data Source: infisical_secrets
|
|
|
|
The `data` source retrieves all secrets from a specific environment and folder. **⚠️ Warning**: Secret values **ARE stored in Terraform state**. Use `ephemeral` instead when possible.
|
|
|
|
### Configuration
|
|
|
|
```hcl
|
|
data "infisical_secrets" "all_secrets" {
|
|
workspace_id = "your-workspace-id"
|
|
env_slug = "prod"
|
|
folder_path = "/" # Optional, defaults to "/"
|
|
}
|
|
|
|
output "all_secrets" {
|
|
value = data.infisical_secrets.all_secrets.secrets
|
|
sensitive = true
|
|
}
|
|
```
|
|
|
|
### Attributes
|
|
|
|
| Attribute | Type | Description |
|
|
|-----------|------|-------------|
|
|
| `workspace_id` | string | ID of the Infisical workspace |
|
|
| `env_slug` | string | Environment slug |
|
|
| `folder_path` | string | Path within the environment (optional, defaults to "/") |
|
|
| `secrets` | map(string) | Map of all secrets in the folder (key → value) |
|
|
|
|
### Usage Example
|
|
|
|
```hcl
|
|
data "infisical_secrets" "all_secrets" {
|
|
workspace_id = "your-workspace-id"
|
|
env_slug = "prod"
|
|
}
|
|
|
|
# Access individual secrets
|
|
output "database_password" {
|
|
value = data.infisical_secrets.all_secrets.secrets["DB_PASSWORD"]
|
|
sensitive = true
|
|
}
|
|
|
|
# Use secrets in resource configuration
|
|
resource "aws_db_instance" "main" {
|
|
allocated_storage = 100
|
|
engine = "postgres"
|
|
engine_version = "15.2"
|
|
instance_class = "db.t3.micro"
|
|
username = "admin"
|
|
password = data.infisical_secrets.all_secrets.secrets["DB_PASSWORD"]
|
|
skip_final_snapshot = true
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Resource: infisical_project_role
|
|
|
|
Create and manage custom project roles with granular permissions.
|
|
|
|
### Permissions v2 Format (Recommended)
|
|
|
|
Use `permissions_v2` for modern, flexible permission definitions with subject-action structure.
|
|
|
|
```hcl
|
|
resource "infisical_project_role" "developer" {
|
|
project_id = "your-project-id"
|
|
name = "Developer"
|
|
|
|
permissions_v2 = [
|
|
{
|
|
subject = "secrets"
|
|
actions = ["read", "create", "edit"]
|
|
},
|
|
{
|
|
subject = "secret-folders"
|
|
actions = ["read", "create", "edit"]
|
|
},
|
|
{
|
|
subject = "secret-imports"
|
|
actions = ["read"]
|
|
},
|
|
{
|
|
subject = "dynamic-secrets"
|
|
actions = ["read", "lease"]
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### Permissions v2 Subjects & Actions
|
|
|
|
| Subject | Available Actions |
|
|
|---------|-------------------|
|
|
| `secrets` | read, create, edit, delete, read-metadata |
|
|
| `secret-folders` | read, create, edit, delete |
|
|
| `secret-imports` | read, create, edit, delete |
|
|
| `dynamic-secrets` | read, read-root-credential, create-root-credential, edit-root-credential, delete-root-credential, lease |
|
|
|
|
### Permissions v1 Format (Deprecated)
|
|
|
|
The old `permissions` attribute uses inverted logic (denying actions). It's deprecated—always prefer `permissions_v2`.
|
|
|
|
```hcl
|
|
# ⚠️ DEPRECATED — Do not use in new code
|
|
resource "infisical_project_role" "viewer" {
|
|
project_id = "your-project-id"
|
|
name = "Viewer"
|
|
|
|
permissions = [
|
|
{
|
|
action = "create"
|
|
inverted = true
|
|
},
|
|
{
|
|
action = "edit"
|
|
inverted = true
|
|
},
|
|
{
|
|
action = "delete"
|
|
inverted = true
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### Complete Example with Multiple Roles
|
|
|
|
```hcl
|
|
resource "infisical_project_role" "admin" {
|
|
project_id = "your-project-id"
|
|
name = "Admin"
|
|
|
|
permissions_v2 = [
|
|
{
|
|
subject = "secrets"
|
|
actions = ["read", "create", "edit", "delete"]
|
|
},
|
|
{
|
|
subject = "secret-folders"
|
|
actions = ["read", "create", "edit", "delete"]
|
|
},
|
|
{
|
|
subject = "secret-imports"
|
|
actions = ["read", "create", "edit", "delete"]
|
|
},
|
|
{
|
|
subject = "dynamic-secrets"
|
|
actions = ["read", "read-root-credential", "create-root-credential", "edit-root-credential", "delete-root-credential", "lease"]
|
|
}
|
|
]
|
|
}
|
|
|
|
resource "infisical_project_role" "ops" {
|
|
project_id = "your-project-id"
|
|
name = "Ops"
|
|
|
|
permissions_v2 = [
|
|
{
|
|
subject = "secrets"
|
|
actions = ["read"]
|
|
},
|
|
{
|
|
subject = "dynamic-secrets"
|
|
actions = ["read", "lease"]
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Resource: infisical_access_approval_policy
|
|
|
|
Enforce approval workflows for sensitive secret operations.
|
|
|
|
### Configuration
|
|
|
|
```hcl
|
|
resource "infisical_access_approval_policy" "prod_secrets" {
|
|
project_id = "your-project-id"
|
|
name = "Production Secrets Approval"
|
|
environment_slug = "prod"
|
|
secret_path = "/" # Optional, specific path or "/" for all
|
|
|
|
approvers = [
|
|
{
|
|
type = "username"
|
|
username = "[email protected]"
|
|
},
|
|
{
|
|
type = "username"
|
|
username = "[email protected]"
|
|
}
|
|
]
|
|
|
|
required_approvals = 1
|
|
enforcement_level = "hard" # "soft" (warning) or "hard" (blocking)
|
|
}
|
|
```
|
|
|
|
### Attributes
|
|
|
|
| Attribute | Type | Description |
|
|
|-----------|------|-------------|
|
|
| `project_id` | string | ID of the Infisical project |
|
|
| `name` | string | Policy name for identification |
|
|
| `environment_slug` | string | Environment to apply policy (e.g., "prod") |
|
|
| `secret_path` | string | Secret path (optional, "/" for all secrets) |
|
|
| `approvers` | list(object) | List of approvers with `type` and `username` |
|
|
| `required_approvals` | number | Number of approvals required before access granted |
|
|
| `enforcement_level` | string | "soft" (warning) or "hard" (blocking access without approval) |
|
|
|
|
### Example: Multiple Approval Policies
|
|
|
|
```hcl
|
|
# Require approval for all production secrets
|
|
resource "infisical_access_approval_policy" "prod_all" {
|
|
project_id = "your-project-id"
|
|
name = "Production - All Secrets"
|
|
environment_slug = "prod"
|
|
secret_path = "/"
|
|
|
|
approvers = [
|
|
{
|
|
type = "username"
|
|
username = "[email protected]"
|
|
},
|
|
{
|
|
type = "username"
|
|
username = "[email protected]"
|
|
}
|
|
]
|
|
|
|
required_approvals = 2
|
|
enforcement_level = "hard"
|
|
}
|
|
|
|
# Require approval for database secrets only
|
|
resource "infisical_access_approval_policy" "prod_database" {
|
|
project_id = "your-project-id"
|
|
name = "Production - Database Secrets"
|
|
environment_slug = "prod"
|
|
secret_path = "/database"
|
|
|
|
approvers = [
|
|
{
|
|
type = "username"
|
|
username = "[email protected]"
|
|
}
|
|
]
|
|
|
|
required_approvals = 1
|
|
enforcement_level = "hard"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Complete Example: Secrets + Roles + Approvals
|
|
|
|
```hcl
|
|
terraform {
|
|
required_providers {
|
|
infisical = {
|
|
source = "infisical/infisical"
|
|
version = "~> 0.13"
|
|
}
|
|
}
|
|
}
|
|
|
|
provider "infisical" {
|
|
client_id = var.infisical_client_id
|
|
client_secret = var.infisical_client_secret
|
|
}
|
|
|
|
variable "infisical_client_id" {
|
|
type = string
|
|
sensitive = true
|
|
}
|
|
|
|
variable "infisical_client_secret" {
|
|
type = string
|
|
sensitive = true
|
|
}
|
|
|
|
# Fetch production database password (never in state)
|
|
ephemeral "infisical_secret" "db_password" {
|
|
workspace_id = "ws-abc123"
|
|
env_slug = "prod"
|
|
secret_key = "DATABASE_PASSWORD"
|
|
}
|
|
|
|
# Define developer role with permission to read secrets
|
|
resource "infisical_project_role" "developer" {
|
|
project_id = "proj-xyz789"
|
|
name = "Developer"
|
|
|
|
permissions_v2 = [
|
|
{
|
|
subject = "secrets"
|
|
actions = ["read"]
|
|
}
|
|
]
|
|
}
|
|
|
|
# Require approval for production secret access
|
|
resource "infisical_access_approval_policy" "prod_approval" {
|
|
project_id = "proj-xyz789"
|
|
name = "Production Approval"
|
|
environment_slug = "prod"
|
|
secret_path = "/"
|
|
|
|
approvers = [
|
|
{
|
|
type = "username"
|
|
username = "[email protected]"
|
|
}
|
|
]
|
|
|
|
required_approvals = 1
|
|
enforcement_level = "hard"
|
|
}
|
|
|
|
output "database_password" {
|
|
value = ephemeral.infisical_secret.db_password.value
|
|
sensitive = true
|
|
}
|
|
```
|