terraform-provider-shoehorn

command module
v0.2.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 6, 2026 License: MIT Imports: 5 Imported by: 0

README

Shoehorn Terraform Provider

Terraform Registry

Terraform provider for managing Shoehorn Intelligent Developer Platform resources as code.

Features

  • Catalog Entities - Define services, libraries, APIs, and infrastructure as code with full metadata (links, relations, licenses, interfaces)
  • Teams - Manage teams with members and role assignments
  • Feature Flags - Toggle feature flags across environments
  • Tenant Settings - Configure portal branding, appearance, hidden pages, and forge settings
  • Platform Policies - Enforce organizational standards and governance
  • API Keys - Provision API keys for service-to-service authentication
  • User Roles - Assign RBAC roles to users
  • Group Role Mappings - Map IdP groups to Cerbos roles so group members inherit permissions
  • Integrations - Configure third-party integrations (GitHub, PagerDuty, etc.)
  • Kubernetes Agents - Register K8s cluster agents for workload discovery
  • Governance Actions - Track and manage governance action items with priority, SLA, and assignment
  • Forge Molds - Define workflow templates as code with input schemas and actions
  • Forge Approval Policies - Configure multi-step approval workflows for forge operations
  • Marketplace Installations - Install and configure marketplace addons

Requirements

  • Terraform >= 1.0
  • Go >= 1.21 (to build the provider plugin)
  • A running Shoehorn instance with an API key

Installation

From the Terraform Registry
terraform {
  required_providers {
    shoehorn = {
      source  = "shoehorn-dev/shoehorn"
      version = "~> 0.1"
    }
  }
}
Local Development
# Clone the repository
git clone https://github.com/shoehorn-dev/terraform-provider-shoehorn.git
cd terraform-provider-shoehorn

# Build the provider
go build -o terraform-provider-shoehorn

# Install to local plugin directory
# Linux/macOS:
mkdir -p ~/.terraform.d/plugins/registry.terraform.io/shoehorn-dev/shoehorn/0.1.0/$(go env GOOS)_$(go env GOARCH)
cp terraform-provider-shoehorn ~/.terraform.d/plugins/registry.terraform.io/shoehorn-dev/shoehorn/0.1.0/$(go env GOOS)_$(go env GOARCH)/

# Windows: Use a dev_overrides block in your .terraformrc (see Development section below)

Authentication

The provider requires a Shoehorn API host and API key. These can be configured in three ways:

1. Provider Block
provider "shoehorn" {
  host    = "https://shoehorn.example.com"
  api_key = var.shoehorn_api_key
}
export SHOEHORN_HOST="https://shoehorn.example.com"
export SHOEHORN_API_KEY="shp_your_api_key_here"
provider "shoehorn" {}
3. Mixed
provider "shoehorn" {
  host = "https://shoehorn.example.com"
  # api_key read from SHOEHORN_API_KEY env var
}
Configuring the provider when no resources are managed

The provider validates host and api_key at init time, before any resource operations. If you consume terraform-shoehorn-modules/modules/kubernetes with deploy_agent = false, no shoehorn_* resources will be created. Terraform still requires the provider block to be valid because the module declares an agent resource (gated count = 0).

Pass stub values:

provider "shoehorn" {
  host    = "https://${var.domain}"
  api_key = var.deploy_agent ? var.shoehorn_api_key : "stub-not-used"
}

Quick Start

terraform {
  required_providers {
    shoehorn = {
      source  = "shoehorn-dev/shoehorn"
      version = "~> 0.1"
    }
  }
}

provider "shoehorn" {
  host    = "https://shoehorn.example.com"
  api_key = var.shoehorn_api_key
}

variable "shoehorn_api_key" {
  type      = string
  sensitive = true
}

# Create a team
resource "shoehorn_team" "platform" {
  name         = "Platform Engineering"
  slug         = "platform-engineering"
  display_name = "Platform Engineering"
  description  = "Core platform team"

  members = jsonencode([
    { user_id = "alice@example.com", role = "manager" },
    { user_id = "bob@example.com", role = "member" }
  ])
}

# Create a service entity
resource "shoehorn_entity" "api_gateway" {
  name             = "api-gateway"
  type             = "service"
  description      = "Central API gateway for all backend services"
  entity_lifecycle = "production"
  tier             = "critical"
  owner            = shoehorn_team.platform.slug
  tags             = ["gateway", "go", "infrastructure"]

  links = jsonencode([
    { name = "Repository", url = "https://github.com/org/api-gateway", icon = "github" },
    { name = "Dashboard",  url = "https://grafana.internal/d/api-gw",  icon = "grafana" },
    { name = "Runbook",    url = "https://wiki.internal/api-gateway",  icon = "docs" }
  ])

  relations = jsonencode([
    { type = "depends_on", target = "service:auth-service" },
    { type = "calls",      target = "service:user-service" }
  ])
}

Resources

shoehorn_entity

Manages a catalog entity (service, library, API, resource, etc.).

resource "shoehorn_entity" "payments" {
  name             = "payments-service"
  type             = "service"
  description      = "Payment processing microservice"
  entity_lifecycle = "production"
  tier             = "critical"
  owner            = "platform-engineering"
  tags             = ["payments", "go", "grpc"]

  links = jsonencode([
    { name = "Repository", url = "https://github.com/org/payments", icon = "github" },
    { name = "API Docs",   url = "https://docs.internal/payments",  icon = "api" }
  ])

  relations = jsonencode([
    { type = "depends_on",  target = "resource:postgres-primary" },
    { type = "calls",       target = "service:notification-service" },
    { type = "reads_from",  target = "resource:kafka-cluster" }
  ])

  licenses = jsonencode([
    {
      title   = "Stripe Enterprise"
      vendor  = "Stripe"
      expires = "2026-12-31"
      seats   = 50
      cost    = "$25000/year"
    }
  ])

  interfaces = jsonencode({
    http = {
      openapi = "https://api.example.com/openapi.json"
      baseUrl = "https://api.example.com/v1"
    }
    grpc = {
      package = "payments.v1"
      proto   = "https://github.com/org/payments/proto/payments.proto"
    }
  })

  changelog_path = "CHANGELOG.md"
}
Attributes
Name Type Required Description
name String Yes Entity name (used as service ID). Forces replacement if changed.
type String Yes Entity type: service, library, api, website, resource, system, component
description String No Description of the entity
entity_lifecycle String No Lifecycle stage: experimental, production, deprecated
tier String No Service tier: critical, gold, silver, bronze, tier1-tier4
owner String No Owner team slug
tags Set of String No Tags for categorization
links JSON String No Array of {name, url, icon} objects. Icons: github, grafana, docs, api, slack, jira, etc.
relations JSON String No Array of {type, target} objects. Types: depends_on, calls, reads_from, writes_to, owned_by
licenses JSON String No Array of {title, vendor, expires, seats, cost} objects
interfaces JSON String No Object with http and/or grpc interface definitions
changelog_path String No Path to changelog file in repository

Computed: id, repository_path, created_at, updated_at

shoehorn_team

Manages a team with optional member assignments.

resource "shoehorn_team" "backend" {
  name         = "Backend Engineering"
  slug         = "backend-engineering"
  display_name = "Backend Engineering"
  description  = "Backend services team"

  members = jsonencode([
    { user_id = "alice@example.com", role = "manager" },
    { user_id = "bob@example.com",   role = "admin" },
    { user_id = "carol@example.com", role = "member" }
  ])
}
Name Type Required Description
name String Yes Team name
slug String Yes Unique slug (forces replacement if changed)
display_name String No Display name
description String No Team description
members JSON String No Array of {user_id, role} objects. Roles: manager, admin, member
metadata JSON String No Arbitrary JSON metadata

Computed: id, is_active, member_count, created_at, updated_at

shoehorn_tenant_settings

Manages portal branding and appearance (singleton per tenant).

resource "shoehorn_tenant_settings" "main" {
  platform_name        = "Acme Developer Portal"
  platform_description = "Internal developer portal for Acme Corp"
  company_name         = "Acme Corp"
  primary_color        = "#3b82f6"
  default_theme        = "system"
  hidden_pages         = ["forge", "insights"]

  forge {
    allowed_orgs = ["my-org", "my-other-org"]
    default_org  = "my-org"
  }
}
shoehorn_platform_policy

Configures platform governance policies. Policies are pre-seeded and cannot be created or destroyed - Terraform only manages their enabled and enforcement state.

resource "shoehorn_platform_policy" "require_docs" {
  key         = "required-entity-docs"
  enabled     = true
  enforcement = "warning"
}
shoehorn_feature_flag

Manages feature flags.

resource "shoehorn_feature_flag" "dark_mode" {
  key             = "enable-dark-mode"
  name            = "Dark Mode"
  description     = "Enable dark mode for the platform UI"
  default_enabled = true
}
shoehorn_api_key

Provisions API keys for service-to-service authentication.

resource "shoehorn_api_key" "ci" {
  name       = "CI Pipeline Key"
  expires_in = "90d"
  scopes     = ["entities:read", "catalog:read"]
}

output "api_key" {
  value     = shoehorn_api_key.ci.raw_key
  sensitive = true
}
shoehorn_integration

Configures third-party integrations.

resource "shoehorn_integration" "github" {
  name = "GitHub Production"
  type = "github"
  config_json = jsonencode({
    token        = var.github_token
    organization = "acme-corp"
  })
}
shoehorn_k8s_agent

Registers Kubernetes cluster agents.

resource "shoehorn_k8s_agent" "production" {
  name       = "production-cluster"
  cluster_id = "prod-us-east-1"
}

output "agent_token" {
  value     = shoehorn_k8s_agent.production.token
  sensitive = true
}
shoehorn_user_role

Assigns RBAC roles to users.

resource "shoehorn_user_role" "admin" {
  user_id = "user-abc-123"
  role    = "admin"
}
shoehorn_group_role_mapping

Maps an IdP group to a Cerbos role so all members of the group inherit that role.

resource "shoehorn_group_role_mapping" "platform_editors" {
  group_name  = "team-developer-platform"
  role_name   = "entity:editor"
  description = "Grant entity editor access to the developer platform group"
}
Name Type Required Description
group_name String Yes The IdP group name. Changing this forces replacement.
role_name String Yes The Cerbos role name (e.g., entity:editor, workflow:executor, tenant:admin). Changing this forces replacement.
auth_provider String No The auth provider identifier. Defaults to default.
description String No Optional description for the mapping.

Computed: id (format: group_name:role_name)

shoehorn_governance_action

Manages a governance action item for tracking remediation and compliance work.

resource "shoehorn_governance_action" "update_docs" {
  entity_id   = "service:my-service"
  title       = "Update API documentation"
  priority    = "high"
  source_type = "policy"
  description = "API docs are outdated and need to reflect the v2 endpoints"
  assigned_to = "user-123"
  sla_days    = 14
}
Name Type Required Description
entity_id String Yes The entity this action applies to (e.g., service:my-service)
title String Yes Action item title
priority String Yes Priority level: critical, high, medium, low
source_type String Yes Origin type: scorecard, security, policy
description String No Detailed description of the action
source_id String No Identifier of the originating source
assigned_to String No User ID of the assignee
sla_days Number No Number of days allowed to resolve the action

Computed: id, status, due_date, created_at, updated_at

Import: terraform import shoehorn_governance_action.example <id>

shoehorn_forge_mold

Defines a forge workflow template with input schemas and actions.

resource "shoehorn_forge_mold" "new_service" {
  slug        = "create-microservice"
  name        = "Create Microservice"
  description = "Scaffold a new Go microservice with CI/CD pipeline"
  version     = "1.0.0"
  visibility  = "tenant"
  category    = "scaffolding"
  tags        = ["go", "microservice", "cicd"]

  schema_json = jsonencode({
    properties = {
      service_name = { type = "string", description = "Service name" }
      team         = { type = "string", description = "Owning team" }
    }
    required = ["service_name", "team"]
  })

  actions {
    action  = "create-repo"
    label   = "Create Repository"
    primary = true
  }
}
Name Type Required Description
slug String Yes Unique slug identifier. Forces replacement if changed.
name String No Display name for the mold
description String No Description of what the mold does
version String No Semantic version string
visibility String No Visibility scope (e.g., tenant)
category String No Category for organization (e.g., scaffolding)
schema_json JSON String No JSON Schema defining the mold's input parameters
defaults_json JSON String No Default values for schema properties
actions Block List No Action blocks with action, label, and primary attributes
tags Set of String No Tags for categorization
icon String No Icon identifier

Computed: id, published, created_at, updated_at

Import by slug: terraform import shoehorn_forge_mold.example <slug>

shoehorn_forge_approval_policy

Configures a multi-step approval workflow for forge operations.

resource "shoehorn_forge_approval_policy" "production_deploy" {
  name        = "Production Deployment Approval"
  description = "Requires team lead and SRE approval for production deployments"
  enabled     = true

  steps {
    step        = 1
    name        = "Team Lead Review"
    approvers   = ["lead@example.com"]
    require_all = true
  }

  steps {
    step        = 2
    name        = "SRE Approval"
    approvers   = ["sre-oncall@example.com", "sre-lead@example.com"]
    require_all = false
  }
}
Name Type Required Description
name String Yes Policy name
description String No Description of the approval policy
enabled Boolean No Whether the policy is active
steps Block List No Approval steps, each with step (order), name, approvers (list of emails), and require_all (boolean)

Computed: id, created_at, updated_at

Import: terraform import shoehorn_forge_approval_policy.example <id>

shoehorn_marketplace_installation

Installs and configures a marketplace addon.

resource "shoehorn_marketplace_installation" "cost_tracker" {
  slug    = "cost-tracker"
  enabled = true

  config_json = jsonencode({
    refresh_interval = "1h"
    currency         = "USD"
  })
}
Name Type Required Description
slug String Yes Marketplace addon slug. Forces replacement if changed.
enabled Boolean No Whether the installation is active
config_json JSON String No Addon-specific configuration as JSON

Computed: id, created_at, updated_at

Import by slug: terraform import shoehorn_marketplace_installation.example <slug>

Data Sources

All resources have corresponding data sources for reading existing state:

# List all entities
data "shoehorn_entities" "all" {}

# List all teams
data "shoehorn_teams" "all" {}

# List all feature flags
data "shoehorn_feature_flags" "all" {}

# List all integrations
data "shoehorn_integrations" "all" {}

# List all API keys
data "shoehorn_api_keys" "all" {}

# List all K8s agents
data "shoehorn_k8s_agents" "all" {}

# List all platform policies
data "shoehorn_platform_policies" "all" {}

# List all IdP directory users
data "shoehorn_users" "all" {}

# List all IdP groups with role mappings
data "shoehorn_groups" "all" {}

# List governance actions (filterable by priority and status)
data "shoehorn_governance_actions" "critical" {
  priority = "critical"
  status   = "open"
}

# List all forge molds
data "shoehorn_forge_molds" "all" {}

# List marketplace items (filterable by kind)
data "shoehorn_marketplace_items" "addons" {
  kind = "addon"
}

# List gitops resources (filterable by cluster and tool)
data "shoehorn_gitops_resources" "production" {
  cluster_id = "prod-us-east-1"
  tool       = "argocd"
}

Importing Existing Resources

Resources can be imported into Terraform state:

# Import an entity by name
terraform import shoehorn_entity.api_gateway api-gateway

# Import a team by slug
terraform import shoehorn_team.platform platform-engineering

# Import tenant settings (singleton, use any ID)
terraform import shoehorn_tenant_settings.main singleton

# Import a platform policy by key
terraform import shoehorn_platform_policy.require_docs required-entity-docs

# Import a group role mapping (format: group_name:role_name)
terraform import shoehorn_group_role_mapping.example "team-developer-platform:entity:editor"

# Import a governance action by ID
terraform import shoehorn_governance_action.example action-abc-123

# Import a forge mold by slug
terraform import shoehorn_forge_mold.example create-microservice

# Import a forge approval policy by ID
terraform import shoehorn_forge_approval_policy.example policy-abc-123

# Import a marketplace installation by slug
terraform import shoehorn_marketplace_installation.example cost-tracker

Development

Building
go build -o terraform-provider-shoehorn
Testing
# Run all unit tests
go test ./...

# Run with verbose output
go test -v ./...

# Run with race detector
go test -race ./...
Local Testing with dev_overrides

Create or update your Terraform CLI configuration file:

Linux/macOS (~/.terraformrc):

provider_installation {
  dev_overrides {
    "shoehorn-dev/shoehorn" = "/path/to/terraform-provider-shoehorn"
  }
  direct {}
}

Windows (%APPDATA%\terraform.rc):

provider_installation {
  dev_overrides {
    "shoehorn-dev/shoehorn" = "C:\\path\\to\\terraform-provider-shoehorn"
  }
  direct {}
}

Then build and test:

go build -o terraform-provider-shoehorn
cd test-local
terraform plan
terraform apply
Generating Documentation

Documentation is generated with tfplugindocs:

go generate ./...

Support

License

This project is licensed under the MIT License - see the LICENSE file for details.

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL