Skip to content

Commit 62611d1

Browse files
authored
Merge pull request #8 from phasehq/feat--secret-crud
feat: full secret crud
2 parents 7f816f4 + e109df9 commit 62611d1

File tree

5 files changed

+397
-133
lines changed

5 files changed

+397
-133
lines changed

build.sh

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
11
#!/bin/bash
22

33
# Set the version
4-
VERSION="0.1.1"
4+
VERSION="0.2.0"
55

6-
# Build the provider
7-
go build -o terraform-provider-phase
6+
# Build the provider with the expected naming convention
7+
go build -o terraform-provider-phase_v${VERSION}
88

9-
# Create the plugin directory if it doesn't exist
10-
mkdir -p ~/.terraform.d/plugins/registry.terraform.io/phasehq/phase/${VERSION}/$(go env GOOS)_$(go env GOARCH)/
11-
12-
# Move the binary to the plugin directory
13-
mv terraform-provider-phase ~/.terraform.d/plugins/registry.terraform.io/phasehq/phase/${VERSION}/$(go env GOOS)_$(go env GOARCH)/
9+
# Create a symlink with the exact naming convention Terraform expects
10+
# Format: terraform-provider-{NAME}_v{VERSION}_{OS}_{ARCH}
11+
OS=$(go env GOOS)
12+
ARCH=$(go env GOARCH)
13+
ln -sf terraform-provider-phase_v${VERSION} terraform-provider-phase_v${VERSION}_${OS}_${ARCH}
1414

1515
# Remove the lock file if it exists
1616
rm -f .terraform.lock.hcl
17-
18-
# Initialize Terraform
19-
terraform init

docs/index.md

Lines changed: 165 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,163 +1,232 @@
1-
# Phase Provider
1+
# Phase Provider Documentation
22

3-
The Phase provider is used to interact with secrets stored in Phase. The provider needs to be configured with the proper credentials before it can be used.
3+
The Phase Terraform provider allows you to manage secrets and interact with the Phase API directly from your Terraform configurations.
44

55
## Example Usage
66

7+
Here's a basic example of configuring the provider and managing a secret:
8+
79
```hcl
810
terraform {
911
required_providers {
1012
phase = {
1113
source = "phasehq/phase"
12-
version = "0.1.1" // replace with latest version
14+
version = ">= 0.2.0" // Use the latest appropriate version
15+
}
16+
random = {
17+
source = "hashicorp/random"
18+
version = "~> 3.6"
1319
}
1420
}
1521
}
1622
1723
# Configure the Phase Provider
24+
# Ensure PHASE_TOKEN environment variable is set, or provide phase_token directly.
1825
provider "phase" {
19-
phase_token = "pss_service:v1:..." # or "pss_user:v1:..." // A Phase Service Token or a Phase User Token (PAT)
26+
# host = "https://your-self-hosted-phase.com" # Optional: for self-hosted instances
27+
# skip_tls_verification = true # Optional: if using self-signed certs
2028
}
2129
22-
# Retrieve all secrets for an app
23-
data "phase_secrets" "all" {
24-
env = "development"
25-
app_id = "your-app-id"
26-
path = ""
30+
# Generate a random value for a secret
31+
resource "random_password" "db_password" {
32+
length = 32
33+
special = true
34+
override_special = "_%@"
2735
}
2836
29-
# Get all secrets
30-
output "all_secret_keys" {
31-
value = data.phase_secrets.all.secrets
32-
sensitive = true
37+
# Create or manage a secret in Phase
38+
resource "phase_secret" "database_password" {
39+
app_id = "your-app-id" # Replace with your actual App ID
40+
env = "production" # Specify the environment
41+
key = "DATABASE_PASSWORD" # The key for the secret
42+
value = random_password.db_password.result
43+
path = "/database" # Optional: specify a path (defaults to "/")
44+
tags = ["database", "credentials"] # Optional: add tags that have already been created
45+
comment = "Managed by Terraform" # Optional: add a comment
3346
}
3447
35-
# Alternatively, retrieve all secrets under a specific path
36-
data "phase_secrets" "path_secrets" {
37-
env = "development"
38-
app_id = "your-app-id"
39-
path = "/backend"
48+
# Fetch secrets (example: all secrets at a specific path)
49+
data "phase_secrets" "database_secrets" {
50+
app_id = phase_secret.database_password.app_id # Use values from managed resources
51+
env = phase_secret.database_password.env
52+
path = phase_secret.database_password.path
53+
tags = ["database"] # Optional: filter by tags
54+
}
55+
56+
# Output a specific secret fetched by the data source
57+
output "db_password_read" {
58+
value = data.phase_secrets.database_secrets.secrets["DATABASE_PASSWORD"]
59+
sensitive = true # Always mark sensitive outputs
4060
}
4161
42-
# Get a single secret from that path
43-
output "backend_secret_keys" {
44-
value = data.phase_secrets.path_secrets.secrets["JWT_SECRET"]
45-
sensitive = true
62+
# Output attributes of the managed secret
63+
output "managed_secret_version" {
64+
value = phase_secret.database_password.version
65+
}
66+
67+
output "managed_secret_updated_at" {
68+
value = phase_secret.database_password.updated_at
4669
}
4770
```
4871

49-
## Argument Reference
72+
## Provider Configuration
5073

51-
The following arguments are supported in the provider configuration:
74+
The following arguments are supported in the `provider "phase"` block:
5275

53-
* `phase_token` - (Required) The Phase authentication token. This can be either a service token or a personal access token. It can be specified with the `PHASE_SERVICE_TOKEN` or `PHASE_PAT_TOKEN` environment variable.
54-
* `host` - (Optional) The Phase API host. Defaults to `https://api.phase.dev` for Phase Cloud. This can be specified with the `PHASE_HOST` environment variable. If a custom host is provided, "/service/public" will be appended to the URL.
76+
* `phase_token` - (Optional, **Required** if env var not set) The Phase authentication token. This can be a Service Token (`pss_service:...`) or a Personal Access Token (`pss_user:...`).
77+
* **Environment Variables:** This value can be provided via `PHASE_TOKEN`, `PHASE_SERVICE_TOKEN`, or `PHASE_PAT_TOKEN` environment variables (checked in that order). Providing it in the configuration block takes precedence.
78+
* **Sensitive:** This value is sensitive.
79+
* `host` - (Optional) The base URL for the Phase API.
80+
* **Default:** `https://api.phase.dev` (for Phase Cloud).
81+
* **Environment Variable:** Can be set using `PHASE_HOST`.
82+
* **Behavior:** If a custom host is provided (not the default), the provider automatically appends `/service/public` to the URL to target the correct API endpoint (e.g., `https://your-host.com/service/public`).
83+
* `skip_tls_verification` - (Optional) Set to `true` to disable SSL/TLS certificate validation for the `host`. Useful for self-hosted instances with self-signed certificates. **Use with caution.** Defaults to `false`.
5584

56-
## Data Sources
85+
## Resources
5786

58-
### phase_secrets
87+
### `phase_secret`
5988

60-
Retrieve secrets from Phase.
89+
Manages a single secret within a specific application and environment in Phase.
6190

62-
#### Argument Reference
91+
The provider handles create, read, update, and delete operations. If a `phase_secret` resource is defined for a secret that already exists (based on `app_id`, `env`, `path`, `key`), the provider will manage the existing secret and update it if necessary, rather than failing.
6392

64-
The following arguments are supported:
93+
#### Argument Reference
6594

66-
* `env` - (Required) The environment name.
67-
* `app_id` - (Required) The application ID.
68-
* `path` - (Optional) The path to fetch secrets from. If not provided, fetches secrets from all paths.
69-
* `key` - (Optional) A specific secret key to fetch. If provided, only this secret will be returned.
95+
* `app_id` - (Required, ForceNew) The UUID of the Phase application where the secret resides. Changing this forces a new resource to be created.
96+
* `env` - (Required, ForceNew) The name of the environment within the application (e.g., `development`, `production`). Changing this forces a new resource to be created.
97+
* `key` - (Required) The key (name) of the secret (e.g., `DATABASE_URL`, `API_KEY`).
98+
* `value` - (Required, Sensitive) The value of the secret.
99+
* `path` - (Optional) The path where the secret is stored within the environment. Defaults to `/` (root). Example: `/database/credentials`.
100+
* `comment` - (Optional) A description or comment for the secret.
101+
* `tags` - (Optional) A list of strings to tag the secret with. Tags can be used for filtering when reading secrets.
102+
* `override` - (Optional) A block to configure a **Personal Secret Override**. This requires authenticating with a User Token (PAT). **Note:** This block *configures* the override value in Phase; its *activation* must still be done via the Phase Console or CLI.
103+
* `value` - (Required, Sensitive) The value to use when this override is active for the authenticated user.
104+
* `is_active` - (Required, Boolean) Must be set to `true` to configure the override. The provider currently only supports setting active overrides via this block. Setting it to `false` may not explicitly deactivate it via the API, but removes the override configuration from the state.
70105

71106
#### Attribute Reference
72107

73-
The following attributes are exported:
108+
In addition to the arguments above, the following attributes are exported:
74109

75-
* `secrets` - A map of secret keys to their corresponding values.
110+
* `id` - The unique UUID assigned to the secret by Phase upon creation.
111+
* `version` - The current version number of the secret. Incremented on each update.
112+
* `created_at` - The timestamp (UTC RFC3339 format) when the secret was first created.
113+
* `updated_at` - The timestamp (UTC RFC3339 format) when the secret was last updated.
76114

77-
## Fetching Secrets
115+
## Data Sources
78116

79-
### Fetching All Secrets for an App
117+
### `phase_secrets`
80118

81-
To fetch all secrets for a given app:
119+
Fetches multiple secrets from Phase based on specified filters.
82120

83-
```hcl
84-
data "phase_secrets" "all" {
85-
env = "development"
86-
app_id = "your-app-id"
87-
path = ""
88-
}
121+
#### Argument Reference
89122

90-
output "all_secret_keys" {
91-
value = data.phase_secrets.all.secrets
92-
sensitive = true
93-
}
94-
```
123+
* `app_id` - (Required) The UUID of the Phase application.
124+
* `env` - (Required) The name of the environment.
125+
* `path` - (Optional) The path to filter secrets by. If omitted or empty, secrets from the root path (`/`) are fetched by default (behavior might depend on API specifics, explicitly use `/` for root). **Note:** The API endpoint used might primarily fetch based on `key` if provided, potentially ignoring `path`. For guaranteed path-based fetching without a specific key, ensure `key` is omitted. For fetching *all* secrets regardless of path, this might require multiple data source calls or future provider enhancements if the API requires path specification.
126+
* `key` - (Optional) The key of a *specific* secret to fetch. If provided, only the secret matching this key (within the specified `app_id` and `env`, considering `path` behavior mentioned above) will be returned.
127+
* `tags` - (Optional) A list of strings (tags) to filter secrets by. Secrets matching *any* of the provided tags will be included (OR logic).
95128

96-
This will fetch all secrets for the specified app and environment, and output their keys.
129+
#### Attribute Reference
97130

98-
### Fetching a Single Secret
131+
* `secrets` - (Computed, Sensitive) A map where keys are the secret keys (e.g., `DATABASE_URL`) and values are their corresponding secret values. If a Personal Secret Override is active for the authenticated user, the override value will be returned here.
132+
* `id` - A unique identifier constructed by the provider for this data source instance based on the input arguments (`app_id`, `env`, `path`, `key`, `tags`).
99133

100-
To fetch a specific secret:
134+
## Importing
101135

102-
```hcl
103-
data "phase_secrets" "single" {
104-
env = "development"
105-
app_id = "your-app-id"
106-
}
136+
Existing secrets managed outside of Terraform can be imported into your Terraform state.
107137

108-
output "database_url" {
109-
value = data.phase_secrets.single.secrets["DATABASE_URL"]
110-
sensitive = true
111-
}
112-
```
138+
Use the `terraform import` command with the following ID format:
113139

114-
This will fetch only the specified secret and output its value.
140+
```bash
141+
terraform import phase_secret.<resource_name_in_tf> "{app_id}:{env}:{path}:{key}"
142+
```
115143

116-
### Fetching Secrets from a Specific Path
144+
**Components:**
117145

118-
To fetch all secrets under a specific path:
146+
* `phase_secret.<resource_name_in_tf>`: The type and name of the resource block in your Terraform configuration (`.tf` file) that corresponds to the secret you want to import.
147+
* `{app_id}`: The UUID of the application.
148+
* `{env}`: The name of the environment.
149+
* `{path}`: The **exact** path where the secret exists in Phase, including leading and trailing slashes if applicable (e.g., `/`, `/database/`, `/folder/path/`).
150+
* `{key}`: The key of the secret.
119151

120-
```hcl
121-
data "phase_secrets" "path_secrets" {
122-
env = "development"
123-
app_id = "your-app-id"
124-
path = "/backend"
125-
}
152+
**Example:**
126153

127-
output "backend_secret_keys" {
128-
value = data.phase_secrets.path_secrets.secrets["JWT_SECRET"]
129-
sensitive = true
130-
}
154+
```bash
155+
# Assuming a resource block like: resource "phase_secret" "imported_secret" { ... }
156+
terraform import phase_secret.imported_secret "907549ca-1430-4aa0-9998-290525741005:production:/database/:DB_HOST"
131157
```
132158

133-
This will fetch all secrets under the specified path and output their keys.
159+
After importing, run `terraform plan` to see any differences between your configuration and the imported state, and adjust your `.tf` file accordingly.
134160

135-
### Using Secrets
161+
## Advanced Topics
136162

137-
You can use the fetched secrets in your Terraform configurations like this:
163+
### Personal Secret Overrides
138164

139-
```hcl
140-
resource "some_resource" "example" {
141-
database_url = data.phase_secrets.single.secrets["DATABASE_URL"]
142-
api_key = data.phase_secrets.all.secrets["API_KEY"]
143-
backend_config = data.phase_secrets.path_secrets.secrets["BACKEND_CONFIG"]
144-
}
145-
```
165+
Personal Secret Overrides allow individual users (authenticating with a User Token/PAT) to temporarily use a different value for a secret without affecting the globally stored value.
166+
167+
* **Authentication:** Requires a `pss_user:...` token. Service tokens (`pss_service:...`) cannot read or manage overrides.
168+
* **Provider Interaction:**
169+
* **Reading (`data "phase_secrets"`):** If an override is *active* in Phase for the authenticated user, the data source will return the override value.
170+
* **Managing (`resource "phase_secret"`):** You can define the `override` block in a `phase_secret` resource to *configure* the override value in Phase. However, **activating** the override must still be done separately through the Phase Console or CLI. The provider essentially sets the stage for the override.
171+
* **Visibility:** Overrides are personal. Only the user who created and activated the override (and is authenticated with their PAT) will see the overridden value via the provider.
146172

147-
Always mark outputs containing secret values as sensitive to prevent them from being displayed in console output or logs.
173+
### Working with Tags
148174

149-
## Personal Secret Overrides
175+
Tags provide a way to categorize and filter secrets.
150176

151-
Personal Secret Overrides allow individual users to temporarily override the value of a secret for their own use, without affecting the secret's value for other users or systems. Here are some important points to note about Personal Secret Overrides:
177+
Please note: To be able to assign tags, they must be already created in the Phase Console before hand.
152178

153-
1. **User Token Requirement**: To use Personal Secret Overrides, you must authenticate with a Phase User Token (Personal Access Token or PAT). Service tokens do not support Personal Secret Overrides.
179+
* **Assigning Tags:** Use the `tags` argument in the `phase_secret` resource:
180+
```hcl
181+
resource "phase_secret" "api_key" {
182+
# ... other args ...
183+
key = "THIRD_PARTY_API_KEY"
184+
path = "/integrations/"
185+
tags = ["api", "billing", "external"]
186+
}
187+
```
188+
* **Filtering by Tags:** Use the `tags` argument in the `phase_secrets` data source. It returns secrets matching *any* of the specified tags (OR logic).
189+
```hcl
190+
# Fetch secrets tagged with 'database' OR 'redis'
191+
data "phase_secrets" "cache_and_db" {
192+
app_id = "your-app-id"
193+
env = "staging"
194+
tags = ["database", "redis"]
195+
}
154196
155-
2. **Activation**: Personal Secret Overrides must be activated through the Phase Console or the Phase CLI. They cannot be directly triggered or modified through the Terraform provider.
197+
# Fetch 'api' tagged secrets specifically from the '/backend' path
198+
data "phase_secrets" "backend_api" {
199+
app_id = "your-app-id"
200+
env = "staging"
201+
path = "/backend/"
202+
tags = ["api"]
203+
}
156204
157-
3. **Behavior**: When a Personal Secret Override is active for a user, the Terraform provider will automatically use the overridden value instead of the main secret value when fetching secrets.
205+
output "api_keys" {
206+
value = data.phase_secrets.backend_api.secrets
207+
sensitive = true
208+
}
209+
```
158210
159-
4. **Visibility**: Personal Secret Overrides are only visible and applicable to the user who created them. Other users and systems will continue to see and use the main secret value.
211+
### Secret Metadata
160212
161-
5. **Temporary Nature**: Personal Secret Overrides are intended for temporary use, such as during development or testing. They should not be relied upon for production configurations.
213+
The `phase_secret` resource exports metadata about the managed secret:
162214
163-
Remember that the presence and value of Personal Secret Overrides depend on the authenticated user and the state of overrides in the Phase system, not on the Terraform configuration itself.
215+
```hcl
216+
resource "phase_secret" "config" {
217+
app_id = "your-app-id"
218+
env = "production"
219+
key = "FEATURE_FLAG_X"
220+
value = "true"
221+
}
222+
223+
output "config_version" {
224+
description = "Current version of the feature flag secret."
225+
value = phase_secret.config.version
226+
}
227+
228+
output "config_last_updated" {
229+
description = "Timestamp when the feature flag was last modified."
230+
value = phase_secret.config.updated_at
231+
}
232+
```

0 commit comments

Comments
 (0)