Skip to content

portainer/terraform-provider-portainer

Repository files navigation

Terraform Logo     portainer-provider-terraform     portainer-provider-opentofu

Terraform Provider for Portainer

Contributors GitHub go.mod Go version GitHub Workflow Status GitHub release (latest by date including pre-releases)

Explore the docs »

Portainer Terraform Provider

A Terraform provider to manage Portainer resources via its REST API using Terraform.

It supports provisioning and configuration of Portainer users and will be extended to support other objects such as teams, stacks, endpoints, and access control.

Requirements

  • Terraform v0.13+
  • Portainer 2.x with admin API key support enabled
  • Go 1.21+ (if building from source)

Building and Installing

make build

Provider Support

Provider Provider Support Status
Terraform
OpenTofu

Example Provider Configuration

provider "portainer" {
  endpoint = "https://portainer.example.com"

  # Option 1: API key authentication
  api_key  = "your-api-key"

  # Option 2: Username/password authentication (generates JWT token internally)
  # api_user     = "admin"
  # api_password = "your-password"

  skip_ssl_verify  = true # optional (default value is `false`)
}

Authentication

The Portainer Terraform provider supports two authentication methods:

  1. API Key (via X-API-Key header)
  2. Username & Password (via /api/auth → JWT token internally used)

Only one method is required – if both are provided, api_key takes precedence.

Usage – API Key:

provider "portainer" {
  api_key  = "your-api-key"
}

Usage – Username & Password:

provider "portainer" {
  api_user     = "admin"
  api_password = "your-password"
}

Environment variables

You can also configure the provider via environment variables:

API key method

$ export PORTAINER_ENDPOINT="https://portainer.example.com"
$ export PORTAINER_API_KEY="your-api-key"
$ export PORTAINER_SKIP_SSL_VERIFY=true

Username and password method

$ export PORTAINER_ENDPOINT="https://portainer.example.com"
$ export PORTAINER_USER="admin"
$ export PORTAINER_PASSWORD="your-password"
$ export PORTAINER_SKIP_SSL_VERIFY=true

Arguments Reference

Name Type Required Description
endpoint string ✅ yes URL of the Portainer instance. /api will be appended automatically if missing.
api_key string ❌ no API key for authentication. Mutually exclusive with api_user and api_password.
api_user string ❌ no Username for authentication (must be used with api_password). Mutually exclusive with api_key.
api_password string ❌ no Password for authentication (must be used with api_user). Mutually exclusive with api_key.
skip_ssl_verify boolean ❌ no Skip TLS certificate verification (useful for self-signed certs). Default: false.

Usage

See our examples per resources in docs.

🧩 Supported Resources

Resource Documentation Example Status Terraform Import / Create => Update E2E Tests
portainer_user user.md example ✅ / ✅
portainer_team team.md example ✅ / ✅
portainer_team_membership team_membership.md example ✅ / ❌
portainer_environment environment.md example ✅ / ❌
portainer_tag tag.md example ✅ / ✅
portainer_endpoint_group endpoint_group.md example ✅ / ✅
portainer_registry registry.md example ✅ / ✅
portainer_backup backup.md example ❌ / ❌
portainer_backup_s3 backup_s3.md example ❌ / ❌
portainer_auth auth.md example ❌ / ❌
portainer_edge_group edge_group.md example ✅ / ✅
portainer_edge_stack edge_stack.md example ✅ / ✅
portainer_edge_job edge_job.md example ✅ / ✅
portainer_endpoints_edge_generate_key endpoints_edge_generate_key.md example ❌ / ❌
portainer_edge_configurations edge_configurations.md example ✅ / ❌
portainer_edge_update_schedules edge_update_schedules.md example ✅ / ❌
portainer_stack stack.md example ✅ / ✅
portainer_custom_template custom_template.md example ✅ / ✅
portainer_container_exec container_exec.md example ❌ / ❌
portainer_docker_network docker_network.md example ✅ / ❌
portainer_docker_plugin docker_plugin.md example ✅ / ❌
portainer_docker_image docker_image.md example ❌ / ❌
portainer_docker_volume docker_volume.md example ✅ / ❌
portainer_docker_secret docker_secret.md example ✅ / ✅
portainer_docker_config docker_config.md example ✅ / ✅
portainer_docker_node docker_node.md example ❌ / ❌
portainer_open_amt open_amt.md example ❌ / ❌
portainer_open_amt_activate open_amt_activate.md example ❌ / ❌
portainer_open_amt_devices_action open_amt_devices_action.md example ❌ / ❌
portainer_open_amt_devices_features open_amt_devices_features.md example ❌ / ❌
portainer_settings settings.md example ✅ / ❌
portainer_settings_experimental settings_experimental.md example ✅ / ❌
portainer_endpoint_settings endpoint_settings.md example ❌ / ❌
portainer_portainer_endpoint_service_update endpoint_service_update.md example ❌ / ❌
portainer_endpoint_snapshot endpoint_snapshot.md example ❌ / ❌
portainer_endpoint_association endpoint_association.md example ❌ / ❌
portainer_stack_associate stack_associate.md example ❌ / ❌
portainer_ssl ssl.md example ✅ / ❌
portainer_tls tls.md example ❌ / ❌
portainer_webhook webhook.md example ❌ / ❌
portainer_stack_webhook stack_webhook.md example ❌ / ❌
portainer_edge_stack_webhook edge_stack_webhook.md example ❌ / ❌
portainer_webhook_execute webhook_execute.md example ❌ / ❌
portainer_resource_control resource_control.md example ❌ / ❌
portainer_licenses licenses.md example ✅ / ❌
portainer_cloud_credentials cloud_credentials.md example ✅ / ❌
portainer_cloud_provider_provision cloud_provider_provision.md example ❌ / ❌
portainer_compose_convert compose_convert.md example ❌ / ❌
portainer_chat chat.md example ❌ / ❌
portainer_support_debug_log support_debug_log.md example ✅ / ❌
portainer_sshkeygen sshkeygen.md example ❌ / ❌
portainer_kubernetes_delete_object kubernetes_delete_object.md example ❌ / ❌
portainer_kubernetes_helm kubernetes_helm.md example ❌ / ❌
portainer_kubernetes_ingresscontrollers kubernetes_ingresscontrollers.md example ❌ / ❌
portainer_kubernetes_namespace_ingresscontrollers kubernetes_namespace_ingresscontrollers.md example ❌ / ❌
portainer_kubernetes_namespace_system kubernetes_namespace_system.md example ❌ / ❌
portainer_kubernetes_namespace kubernetes_namespace.md example ❌ / ❌
portainer_kubernetes_namespace_access kubernetes_namespace_access.md example ❌ / ❌
portainer_kubernetes_cronjob kubernetes_cronjob.md example ❌ / ❌
portainer_kubernetes_job kubernetes_job.md example ❌ / ❌
portainer_kubernetes_service_accounts kubernetes_service_account.md example ❌ / ❌
portainer_kubernetes_configmaps kubernetes_configmaps.md example ❌ / ❌
portainer_kubernetes_secret kubernetes_secret.md example ❌ / ❌
portainer_kubernetes_service kubernetes_service.md example ❌ / ❌
portainer_kubernetes_role kubernetes_role.md example ❌ / ❌
portainer_kubernetes_rolebinding kubernetes_rolebinding.md example ❌ / ❌
portainer_kubernetes_clusterrole kubernetes_clusterrole.md example ❌ / ❌
portainer_kubernetes_clusterrolebinding kubernetes_clusterrolebinding.md example ❌ / ❌
portainer_kubernetes_application kubernetes_application.md example ❌ / ❌
portainer_kubernetes_ingress kubernetes_ingress.md example ❌ / ❌
portainer_kubernetes_volume kubernetes_volume.md example ❌ / ❌
portainer_kubernetes_storage kubernetes_storage.md example ❌ / ❌

🐳 Podman Support via Docker Resources

Podman is compatible with the Docker API, which means you can use existing portainer_docker_* resources with Podman – no special portainer_podman_* resources are needed.

Use Docker resources for Podman
Podman works out of the box with most portainer_docker_* Terraform resources.

⚠️ Note:
Podman does not support Docker Swarm – any swarm-based features are not compatible.

Docker Compose to Kubernetes Conversion

You can now use the portainer_compose_convert resource to convert Docker Compose YAML directly into Kubernetes manifests using Kompose.

This is especially useful when migrating applications from Docker standalone or Swarm mode to Kubernetes – while keeping your deployment definitions fully managed as code in Terraform.

ℹ️ The resource uses Kompose internally and supports both the installed CLI binary. See full documentation: docs/resources/compose_convert.md

ℹ️ Note on Create ⇒ Update Behavior

Some resources support a "Create-or-Update" mechanism, when this behavior is implemented, it means:

During the initial terraform apply, if an entity with the given name already exists, the resource will detect it and perform an update instead of attempting to create a duplicate => this is achieved by filtering existing entities by name before creation.

  • This avoids the need for manual terraform import without having to have a terraform tfstate file or cleanup of existing resources in Portainer.
  • It's especially useful during migrations, initial setup, or when applying configuration into environments with pre-existing state.

💡 Missing a resource?

Is there a Portainer resource you'd like to see supported?

👉 Open an issue and we’ll consider it for implementation — or even better, submit a Pull Request to contribute directly!

📘 See CONTRIBUTING.md for guidelines.

💬 Community & Feedback

Have questions, suggestions or want to contribute ideas?
Join the Portainer Community Slack and hop into the #portainer-terraform channel!

Want to report issues, submit pull requests or browse the source code?
Check out the GitHub Repository for this provider.

♻️ Terraform Import Guide

You can import existing Portainer-managed resources into Terraform using the terraform import command. This is useful for adopting GitOps practices or migrating manually created resources into code.

✅ General Syntax

terraform import <RESOURCE_TYPE>.<NAME> <ID>
  • <RESOURCE_TYPE> – the Terraform resource type, e.g., portainer_tag
  • <NAME> – the local name you've chosen in your .tf file
  • <ID> – the Portainer object ID (usually numeric)

🛠 Example: Import an existing tag

Let's say you already have a tag with ID 3 in Portainer. First, define it in your configuration:

resource "portainer_tag" "existing_tag" {
  name = "production"
}

Then run the import:

terraform import portainer_tag.existing_tag 3

Terraform will fetch the current state of the resource and start managing it. You can now safely plan and apply updates from Terraform.

📦 Auto-generate Terraform configuration

After a successful import, you can automatically generate the resource definition from the Terraform state:

./generate-tf.sh

This script reads the current Terraform state and generates a file named generated.tf with the proper configuration of the imported resources. You can copy or refactor the output into your main Terraform files.

ℹ️ Note: Only resources with import support listed as ✅ in the table above can be imported.

✅ Daily End-to-End Testing

To ensure maximum reliability and functionality of this provider, automated end-to-end tests are executed every day via GitHub Actions.

These tests run against a real Portainer instance (started using docker compose) and validate the majority of supported resources using real Terraform plans and applies.

💡 This helps catch regressions early and ensures the provider remains fully operational and compatible with the Portainer API.

🔄 Workflows

The project uses GitHub Actions to automate validation and testing of the provider.

  • Validate and lint documentation files (README.md and docs/)
  • Initialize, test and check the Portainer provider with Terraform and OpenTofu
  • Publish the new version of the Portainer Terraform provider to Terraform Registry
  • Run daily E2E Terraform tests against a live Portainer instance spun up via Docker Compose (make up) at 07:00 UTC

🧪 Localy Testing

To test the provider locally, start the Portainer Web UI using Docker Compose:

make up

Then open http://localhost:9000 in your browser.

🔐 Predefined Test Credentials for Login (use also E2E tests)

Thanks to the portainer_data directory included in this repository, a test user and token are preloaded when you launch the local Portainer instance:

Field Value
Username admin
Password password123456789
API Token ptr_xrP7XWqfZEOoaCJRu5c8qKaWuDtVc2Zb07Q5g22YpS8=

You can now apply your Terraform templates and observe changes live in the UI.

☸️ Testing Kubernetes Resources Locally

If you want to test Kubernetes-related resources, you can spin up a local Kubernetes cluster with k3d, deploy the Portainer Agent into it, and connect Portainer to that environment:

make install-k3d             # Install k3d CLI
make k3d-up                  # Create a local k3d cluster
make k8s-deploy-agent        # Deploy Portainer Agent into Kubernetes
make k3d-connect-portainer   # Connect Portainer container to the k3d network
make k3d-export-ip           # Export Kubernetes IP into terraform.tfvars

Then you can apply your Kubernetes environemnt from directory e2e-tests/environment run by:

cd e2e-tests/environment
terraform init
terraform apply

and than Kubernetes-related Terraform templates under e2e-tests/kubernetes* (or a similar directory):

cd e2e-tests/kubernetes*
terraform init
terraform apply

Testing a new version of the Portainer provider

After making changes to the provider source code, follow these steps: Build the provider binary:

make build

Install the binary into the local Terraform plugin directory:

make install-plugin

Update your main.tf to use the local provider source Add the following to your Terraform configuration:

terraform {
  required_providers {
    portainer = {
      source  = "localdomain/local/portainer"
    }
  }
}

Now you're ready to test your provider against the local Portainer instance.

Roadmap

See the open issues for a list of proposed features (and known issues). See CONTRIBUTING for more information.

License

This module is 100% Open Source and is distributed under the MIT License.
See the LICENSE file for more information.

Acknowledgements