chore: remove coder_secret Terraform integration (#25512)

Removes the coder_secret Terraform integration: the data.coder_secret
consumption path through provisionerdserver → provisioner.proto →
provisioner/terraform, the dynamic-parameter secret-requirement
validation, and the workspace-update / resolve-autostart surfaces that
depended on it. This is being done due to a product/feature direction
change (see PLAT-243). User-secret CRUD (DB, REST, CLI, UI, telemetry, audit)
and the agent-manifest secret-injection path are untouched.

The provisionerd API is bumped from v1.17 to v1.18 rather than rolled
back: v1.17 shipped in v2.33.x, so user_secrets field numbers are
reserved and the changelog documents both versions.

Generated with assistance from Coder Agents.
This commit is contained in:
Zach
2026-05-21 09:19:29 -06:00
committed by GitHub
parent 26a0805dcd
commit ddc0e99c69
45 changed files with 835 additions and 3859 deletions
+2 -89
View File
@@ -3,7 +3,6 @@ package dynamicparameters
import (
"context"
"fmt"
"strings"
"github.com/google/uuid"
"github.com/hashicorp/hcl/v2"
@@ -11,7 +10,6 @@ import (
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/util/slice"
"github.com/coder/coder/v2/codersdk"
previewtypes "github.com/coder/preview/types"
"github.com/coder/terraform-provider-coder/v2/provider"
)
@@ -24,33 +22,11 @@ const (
sourcePreset
)
const (
secretRequirementKindEnv = "env"
secretRequirementKindFile = "file"
)
type parameterValue struct {
Value string
Source parameterValueSource
}
// ResolveOption configures optional behavior for ResolveParameters.
type ResolveOption func(*resolveOptions)
type resolveOptions struct {
skipSecretRequirements bool
}
// SkipSecretRequirements skips structured secret-requirement validation and
// enforcement. Callers must pass this for non-start transitions so an
// unsatisfied coder_secret, or an admin who can't read the owner's secrets,
// doesn't block stop or delete.
func SkipSecretRequirements() ResolveOption {
return func(o *resolveOptions) {
o.skipSecretRequirements = true
}
}
//nolint:revive // firstbuild is a control flag to turn on immutable validation
func ResolveParameters(
ctx context.Context,
@@ -60,12 +36,7 @@ func ResolveParameters(
previousValues []database.WorkspaceBuildParameter,
buildValues []codersdk.WorkspaceBuildParameter,
presetValues []database.TemplateVersionPresetParameter,
opts ...ResolveOption,
) (map[string]string, error) {
o := resolveOptions{}
for _, opt := range opts {
opt(&o)
}
previousValuesMap := slice.ToMapFunc(previousValues, func(p database.WorkspaceBuildParameter) (string, string) {
return p.Name, p.Value
})
@@ -99,7 +70,7 @@ func ResolveParameters(
//
// This is how the form should look to the user on their workspace settings page.
// This is the original form truth that our validations should initially be based on.
result, diags := renderer.Render(ctx, ownerID, previousValuesMap)
output, diags := renderer.Render(ctx, ownerID, previousValuesMap)
if diags.HasErrors() {
// Top level diagnostics should break the build. Previous values (and new) should
// always be valid. If there is a case where this is not true, then this has to
@@ -107,7 +78,6 @@ func ResolveParameters(
return nil, parameterValidationError(diags)
}
output := result.Output
// The user's input now needs to be validated against the parameters.
// Mutability & Ephemeral parameters depend on sequential workspace builds.
@@ -128,33 +98,10 @@ func ResolveParameters(
// This is the final set of values that will be used. Any errors at this stage
// are fatal. Additional validation for immutability has to be done manually.
var renderOpts []RenderOption
if !o.skipSecretRequirements {
renderOpts = append(renderOpts, IncludeSecretRequirements())
}
result, diags = renderer.Render(ctx, ownerID, values.ValuesMap(), renderOpts...)
if !o.skipSecretRequirements && !diags.HasErrors() {
var missing []codersdk.SecretRequirementStatus
for _, req := range result.SecretRequirements {
if !req.Satisfied {
missing = append(missing, req)
}
}
if len(missing) > 0 {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Missing required secrets",
Detail: formatMissingSecrets(missing),
Extra: previewtypes.DiagnosticExtra{
Code: DiagCodeMissingSecret,
},
})
}
}
output, diags = renderer.Render(ctx, ownerID, values.ValuesMap())
if diags.HasErrors() {
return nil, parameterValidationError(diags)
}
output = result.Output
// parameterNames is going to be used to remove any excess values left
// around without a parameter.
@@ -281,37 +228,3 @@ func (p parameterValueMap) ValuesMap() map[string]string {
}
return values
}
func secretRequirementKind(env, file string) string {
switch {
case env != "" && file == "":
return secretRequirementKindEnv
case file != "" && env == "":
return secretRequirementKindFile
default:
return ""
}
}
func formatMissingSecrets(reqs []codersdk.SecretRequirementStatus) string {
var b strings.Builder
for i, req := range reqs {
if i > 0 {
_, _ = b.WriteString("\n")
}
switch secretRequirementKind(req.Env, req.File) {
case secretRequirementKindEnv:
_, _ = fmt.Fprintf(&b, "%s %s", secretRequirementKindEnv, req.Env)
case secretRequirementKindFile:
_, _ = fmt.Fprintf(&b, "%s %s", secretRequirementKindFile, req.File)
default:
// checkSecretRequirements filters malformed requirements produced
// by preview before they reach the resolver.
_, _ = b.WriteString("malformed secret requirement")
}
if req.HelpMessage != "" {
_, _ = fmt.Fprintf(&b, ": %s", req.HelpMessage)
}
}
return b.String()
}