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.
- Terraform v0.13+
- Portainer 2.x with admin API key support enabled
- Go 1.21+ (if building from source)
make build
Provider | Provider Support Status |
---|---|
Terraform | |
OpenTofu |
provider "portainer" {
endpoint = "https://portainer.example.com"
api_key = "your-api-key"
skip_ssl_verify = true # optional (default value is `false`)
}
- Static API key
Static credentials can be provided by adding the api_key
variables in-line in the Portainer provider block:
π Authentication: This provider supports only API keys via the
X-API-Key
header. JWT tokens curentlly are not supported in this provider.
Usage:
provider "portainer" {
api_key = "your-api-key"
}
You can provide your configuration via the environment variables representing your minio credentials:
$ export PORTAINER_ENDPOINT="https://portainer.example.com"
$ export PORTAINER_API_KEY="your-api-key"
$ export PORTAINER_SKIP_SSL_VERIFY=true
Name | Type | Required | Description |
---|---|---|---|
endpoint |
string | β yes | The URL of the Portainer instance. /api will be appended automatically if missing. |
api_key |
string | β yes | API key used to authenticate requests. |
skip_ssl_verify |
boolean | β no | Set to true to skip TLS certificate verification (useful for self-signed certs). Default: false |
See our examples per resources in docs.
Resource | Documentation | Example | Status | Terraform Import | 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_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_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_open_amt |
open_amt.md | example | |||
portainer_settings |
settings.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_ssl |
ssl.md | example | |||
portainer_webhook |
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_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_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 |
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.
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.
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.
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)
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.
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.
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.
The project uses GitHub Actions to automate validation and testing of the provider.
- Validate and lint documentation files (
README.md
anddocs/
) - 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
To test the provider locally, start the Portainer Web UI using Docker Compose:
make up
Then open http://localhost:9000
in your browser.
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.
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
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.
See the open issues for a list of proposed features (and known issues). See CONTRIBUTING for more information.
This module is 100% Open Source and is distributed under the MIT License.
See the LICENSE file for more information.