HashiCorp Terraform provider that uses Venafi to streamline machine identity (certificate and key) acquisition.
This open source project is community-supported. To report a problem or share an idea, use
Issues; and if you have a suggestion for fixing the issue, please include those details, too.
In addition, use Pull Requests to contribute actual bug fixes or proposed enhancements.
We welcome and appreciate all contributions. Got questions or want to discuss something with our team?
Join us on Slack!
This solution adds certificate enrollment capabilities to HashiCorp Terraform by seamlessly
integrating with the Venafi Trust Protection Platform or
Venafi Control Plane in a manner that ensures compliance with corporate security
policy and provides visibility into certificate issuance enterprise wide.
Test drive our integration examples today
Let us show you step-by-step how to add certificates to your Infrastucture as Code automation using Terraform.
NOTE If you don’t see an example for a product you use, check back later. We’re working hard to add more integration examples.
Make sure that you are protecting your terraform state file as per the best practices by Hashicorp:
https://developer.hashicorp.com/terraform/language/state/sensitive-data.
This is an important step to prevent data breaches or leaks of sensitive data like usernames, passwords, tokens,
secrets, etc.
Your certificate authority (CA) must be able to issue a certificate in under one minute. Microsoft Active Directory
Certificate Services (ADCS) is a popular choice. Other CA choices may have slightly different requirements.
Within Trust Protection Platform, configure these settings. For more information see the Venafi Administration Guide.
Venafi Provider for HashiCorp Terraform
(IDhashicorp-terraform-by-venafi
) API Application as of 20.1 (or scope certificate:manage
for 19.2 through 19.4) orView
, Read
, Write
, Create
.Enterprise compliant policies applied to the folder including:
Enrollment
.Service Generated CSR
.No
.SHA1 3DES
or SHA256 AES256
to Service Generated CSR
.Yes
.2048
or higher.NOTE: If you are using Microsoft ACDS, the CRL distribution point and Authority Information Access (AIA) URIs
must start with an HTTP URI (non-default configuration). If an LDAP URI appears first in the X509v3 extensions, some
applications will fail, such as NGINX ingress controllers. These applications aren’t able to retrieve CRL and OCSP
information.
The Trust Protection Platform REST API (WebSDK) must be secured with a certificate. Generally, the certificate is issued
by a CA that is not publicly trusted so establishing trust is a critical part of your setup.
Two methods can be used to establish trust. Both require the trust anchor (root CA certificate) of the WebSDK
certificate. If you have administrative access, you can import the root certificate into the trust store for your
operating system. If you don’t have administrative access, or prefer not to make changes to your system configuration,
save the root certificate to a file in PEM
format (e.g. /opt/venafi/bundle.pem) and include it using the trust_bundle
parameter of your Venafi provider.
The Venafi provider offers several authentication methods to Trust Protection Platform. All of them work by requesting
an access token that will grant access to the REST API. Automation becomes complex to manage when access tokens are
introduced as they have an expiration date. When that date is met, the token is no longer valid.
A new Venafi-token provider has been released that
allows customers to manage their access tokens. This way the Venafi provider will always have a valid token to use, and
automation will not be disrupted by token expiration.
If you are using Venafi Control Plane, verify the following:
Resource Owner
role, and know your API key.The Venafi Provider for HashiCorp Terraform is an officially verified integration. As such, releases are published to
the Terraform Registry where they are available forterraform init
to automatically download whenever the provider is referenced by a configuration file. No setup steps
are required to use an official release of this provider other than to download and install Terraform itself.
To use a pre-release or custom-built version of this provider, manually install the plugin binary into
required directory
using the prescribed subdirectory structure
that must align with how the provider is referenced in the required_providers
block of the configuration file.
A Terraform module is a container for multiple resources that are used together and the steps that follow illustrate the
resources required to enroll certificates using the Venafi Provider with HashiCorp Terraform 0.13 or higher.
NOTE: For Terraform 0.12, omit the
required_providers
block and specify any desired version constraints
for the provider in theprovider
block using the
older way to manage provider versions.
We dropped support for RSA PKCS#1 formatted keys for TLS certificates in version 15.0 and also for EC Keys
in version 0.15.4 (you can find out more about this transition in here).
For backward compatibility during Terraform state refresh please update to version 0.15.5 or above.
As a part for upgrading our provider to SDK version 2, we dropped support for Terraform version 0.11 and
below.
With the introduction of version 0.18.0
the Venafi Terraform provider now incorporates a new feature related to certificate retirement. When an infrastructure
is decommissioned, the associated certificate will be automatically retired from the Venafi Platform (Trust Protection
Platform and Venafi Control Plane).
Declare that the Venafi Provider is required:
terraform {
required_providers {
venafi = {
source = "venafi/venafi"
version = "~> 0.16.0"
}
}
required_version = ">= 0.13"
}
Specify the connection and authentication settings for the venafi
provider:
Trust Protection Platform:
provider "venafi" {
url = "https://tpp.venafi.example"
trust_bundle = file("/path/to/bundle.pem")
access_token = "p0WTt3sDPbzm2BDIkoJROQ=="
zone = "DevOps\\Terraform"
}
Venafi Control Plane:
provider "venafi" {
api_key = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
zone = "Business App\\Enterprise CIT"
}
Venafi Control Plane with access token:
provider "venafi" {
token_url = "xxxxxxxx-xxxx"
external_jwt = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
zone = "Business App\\Enterprise CIT"
}
Venafi Control Plane for EU:
provider "venafi" {
url = "https://api.venafi.eu"
api_key = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
zone = "Business App\\Enterprise CIT"
}
Venafi Control Plane for AU:
provider "venafi" {
url = "https://api.au.venafi.cloud"
api_key = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
zone = "Business App\\Enterprise CIT"
}
Venafi Control Plane for UK:
provider "venafi" {
url = "https://api.uk.venafi.cloud"
api_key = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
zone = "Business App\\Enterprise CIT"
}
The venafi
provider has the following options:
| Property | Type | Description | Env. Variable |
|——————————-|————————————————————————————————————————————————————————————————————————————————|—————————————————————————————————————————————————————————————————————————————————-|————————————-|
| api_key
| String | Venafi Control Plane
API key | VENAFI_API |
| access_token
| String | Trust Protection Platform
access token for the “hashicorp-terraform-by-venafi” API Application | VENAFI_TOKEN |
| client_id
| String | ID of the application that will request tokens. Not necessary when access_token
provided. If not provided, defaults to hashicorp-terraform-by-venafi
| VENAFI_CLIENT_ID |
| external_jwt
| String | JWT of the Identity Provider associated to a Venafi Control Plane
service account. Use it along with tenant_id
to request access tokens | VENAFI_EXTERNAL_JWT |
| p12_cert_filename
| String | Filename of PKCS#12 keystore containing a client certificate, private key, and chain certificates to authenticate to Venafi Platform | VENAFI_P12_CERTIFICATE |
| p12_cert_password
| String | Password for the PKCS#12 keystore declared in p12_cert_filename
| VENAFI_P12_PASSWORD |
| token_url
| String | URL to request an access token from Venafi Control Plane
. Use it along with external_jwt
| VENAFI_TENANT_ID |
| tpp_username
| String | [DEPRECATED] Trust Protection Platform WebSDK username, use access_token
if possible | VENAFI_USER |
| tpp_password
| String | [DEPRECATED] Trust Protection Platform WebSDK password, use access_token
if possible | VENAFI_PASS |
| trust_bundle
| String | Text file containing trust anchor certificates in PEM format, generally required for Trust Protection Platform | |
| url
| String | Trust Protection Platform
service URL (e.g. “https://tpp.venafi.example“) | VENAFI_URL |
| zone
| String | Policy folder for Trust Protection Platform
or Application name and Issuing Template API Alias for Venafi Control Plane
(e.g. “Business App\Enterprise CIT”) | VENAFI_ZONE |
| skip_retirement
| Boolean | When true
the certificate retirement on the related Venafi Platform (Trust Protection Platform
or Venafi Control Plane
) will be skipped | VENAFI_SKIP_RETIREMENT |
| dev_mode
| Boolean | When true
, the provider operates without connecting to Trust Protection Platform
or Venafi Control Plane
| VENAFI_DEVMODE |
NOTE: The indicated environment variables can be used to specify values for provider settings rather
than including them in a configuration file. Avoid specifying a value forapi_key
unless you are using Venafi Control Plane
since that variable is used by the provider to decide which Venafi product to use.
Create a venafi_certificate
resource that will generate a new key pair and enroll the certificate needed by atls_server
application:
resource "venafi_certificate" "tls_server" {
common_name = "web.venafi.example"
san_dns = [
"web01.venafi.example",
"web02.venafi.example"
]
algorithm = "RSA"
rsa_bits = "2048"
key_password = "${var.pk_pass}"
}
The venafi_certificate
resource has the following options, only common_name
is required:
NOTE: Updating only
expiration_window
will not trigger another resource to be created by itself,
thus won’t enroll a new certificate. This won’t apply if the expiration_window constraint allows it, this means, if
time to expire of the certificate is within the expiration window.
| Property | Type | Description | Default |
|——————————-|—————————————————————————————————————————————————————————————————————————————————————————-|————————————————————————————————————————- | ————- |
| common_name
| String | Common name of certificate | none
|
| nickname
| String | Use to specify a name for the new certificate object that will be created and placed in a policy. Only valid for Trust Protection Platform. |none
|
| san_dns
| List | String array of DNS names to use as alternative subjects of the certificate | none
|
| san_email
| List | String array of email addresses to use as alternative subjects of the certificate | none
|
| san_ip
| List | String array of IP addresses to use as alternative subjects of the certificate | none
|
| san_uri
| List | String array of Uniform Resource Identifiers (URIs) to use as alternative subjects of the certificate | none
|
| algorithm
| String | Key encryption algorithm (i.e. RSA or ECDSA) | RSA |
| rsa_bits
| Integer | Number of bits to use when generating an RSA key pair (i.e. 2048 or 4096). Applies when algorithm
=RSA | 2048 |
| ecdsa_curve
| String | ECDSA curve to use when generating a key pair (i.e. P256, P384, P521). Applies when algorithm
=ECDSA | P521 |
| key_password
| String | Private key password | none
|
| custom_fields
| Map | Collection of key-value pairs where the key is the name of the Custom Field in Trust Protection Platform. For list type Custom Fields, use the \ | character to delimit mulitple values.
Example: custom_fields = { "Number List" = "2\|4\|6" }
| none
|
| valid_days
| Integer | Desired number of days for which the new certificate will be valid | none
|
| issuer_hint
| String | Used with valid_days
to indicate the target issuer when using Trust Protection Platform and the CA is DigiCert, Entrust, or Microsoft.
Example: issuer_hint = "Microsoft"
| none
|
| expiration_window
| Integer | Number of hours before certificate expiry to request a new certificate | 168 |
| csr_origin
| String | Option to decide whether key-pair generation will be local
or service
generated | local
|
NOTE: The
venafi_certificate
resource handles certificate renewals as long as aterraform apply
is
done within theexpiration_window
period. Keep in mind that theexpiration_window
in the Terraform
configuration needs to align with the renewal window of the issuing CA to achieve the desired result.
After enrollment, the venafi_certificate
resource will expose the following:
| Property | Type | Description |
| ————————- | ——— | —————- |
| private_key_pem
| String | Private key in PEM format encrypted using key_password
, if specified |
| chain
| String | Trust chain CA certificate(s) in PEM format concatenated one after the other |
| certificate
| String | End-entity certificate in PEM format |
| pkcs12
| String | Base64-encoded PKCS#12 keystore encrypted using key_password
, if specified. Useful when working with resources like azurerm_key_vault_certificate. Base64 decode to obtain file bytes. |
For verification purposes, output the certificate, private key, and chain in PEM format and as a PKCS#12 keystore
(base64-encoded):
output "my_private_key" {
value = venafi_certificate.tls_server.private_key_pem
sensitive = true
}
output "my_certificate" {
value = venafi_certificate.tls_server.certificate
}
output "my_trust_chain" {
value = venafi_certificate.tls_server.chain
}
output "my_p12_keystore" {
value = venafi_certificate.tls_server.pkcs12
}
Execute terraform init
, terraform plan
, terraform apply
, and finally terraform show
from the directory
containing the configuration file.
NOTE: Don’t specify an
expiration_window
within your Terraform file when importing, since will trigger
a new update on re-applying your configuration unless that’s desired. By default, we set a value of168
hours.
NOTE: This operation doesn’t support
issuer_hint
among the attributes for importing, neither local
generated certificate key-pair.
The venafi_certificate
resource supports the Terraform import
method.
The import_id
is composed by an id
which is different for each platform, a comma (,) and the key-password
.
The id
for each platform is:
Trust Protection Platform:
The nickname
of the certificate, which represents the name of the certificate object in Trust Protection Platform. Internally we built the pickup_id
using the zone
defined at the provider block.
NOTE: The certificate object name at Trust Protection Platform, usually, should be the same as the
common_name
provided as it is considered good practice, but thenickname
actually could differ from the common
name, as there some use cases whenever you want to handle certificates with different nicknames. For example, you
could have certificates with same common name and different SANs, then, you could manage many certificate resources
that share the same common name usingfor_each
andcount
meta arguments.
Venafi Control Plane:
The pickup-id
.
NOTE: You can learn more about the
pickup-id
and pickup actions for Trust Protection Platform
here, and for
Venafi Control Plane, here
terraform import "venafi_certificate.<resource_name>" "<id>,<key-password>"
Example (assuming our resource name is
imported_certificate
):
resource "venafi_certificate" "imported_certificate" {}
Trust Protection Platform:
terraform import "venafi_certificate.imported_certificate" "tpp.venafi.example,my_key_password"
Venafi Control Plane:
terraform import "venafi_certificate.imported_certificate" "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx,my_key_password"
Declare that the Venafi Provider and specify the connection and authentication settings as described in the previous
section.
NOTE: For Trust Protection Platform, the
access_token
assigned to thevenafi
provider must have
the configuration:manage scope in order to apply certificate policy.
Create a venafi_policy
resource that will create or update the certificate policy for a Venafi zone:
resource "venafi_policy" "tls_server_certificates" {
zone = "My Business App\\Server Certificates"
policy_specification = file("/path/to/tls_server_cert_policy.json")
}
The venafi_policy
resource has the following options, all of which are required when setting policy:
| Property | Type | Description | Default |
| —————————- | ——————- | ————————————————————————————————————————- | ————- |
| zone
| String | The Trust Protection Plaform policy folder or Venafi Control Plane application and issuing template | none
|
| policy_specification
| String | The JSON-formatted certificate policy specification as documented here. Typically read from a file using the file function. Use the VCert CLI to generate a policy specification template to get started (i.e. vcert getpolicy --starter
) | none
|
NOTE: The
venafi_policy
resource supports theterraform import
method. When used, thezone
andpolicy_specification
options are not required since the zone is a required parameter of the import method and the
policy specification is populated from the existing infrastructure. Policy that is successfully imported is also
output to a file named after the zone that was specified. The certificate:manage scope is require to import
policy from Trust Protection Platform.
Declare the Venafi provider and specify the connection and authentication settings as described in the previous
sections.
NOTE: For Trust Protection Platform, the access_token assigned to the Venafi provider must have the
ssh:manage scope in order to create SSH certificates.
Trust Protection Platform:
provider "venafi" {
url = "https://tpp.venafi.example"
trust_bundle = file("/path/to/bundle.pem")
access_token = "p0WTt3sDPbzm2BDIkoJROQ=="
}
Create a resource venafi_ssh_certificate
that will generate a new key pair and enroll the ssh certificate needed by
a remote host:
resource "venafi_ssh_certificate" "remote-host" {
key_id = "my_remote"
template = "devops-terraform"
public_key_method = "service"
source_address = ["test.com"]
key_passphrase = "abcd"
extension = ["login@github.com:alice@github.com"]
valid_hours = 4
}
The venafi_ssh_certificate
resource has the following options, which only key_id
and template
are required:
| Property | Type | Description | Default |
| —————————- | ——————- | ————————————————————————————————————————- | ————- |
| key_id
|String |The identifier of the requested certificate|none
|
| template
|String|The certificate issuing template|none
|
| key_passphrase
|String|Passphrase for encrypting the private key|none
|
| folder
|String |The DN of the policy folder where the certificate object will be created. It will overwrite the default folder set at the template |none
|
| force_command
|String|The requested force command|none
|
| key_size
|Int|The key size bits, they will be used for creating keypair|3072
|
| windows
|Bool|Output certificate and key files in Windows format (i.e. with \r\n line endings) instead of Unix format (i.e. \n line endings).|false
|
| valid_hours
|Int|How much time the requester wants to have the certificate valid, the format is hours|none
|
| object_name
|String |The friendly name for the certificate object. If not specified, the value of the key_id
is used.|none
|
| public_key
|String|The path of the public key that will be used to generate the certificate if public_key_method
set to file
|none
|
| public_key_method
|String | If the public key will be: local
or service
generated or file
provided|local
|
| principal
|List|[DEPRECATED] This will be removed in the future. Use principals
instead. The requested principals|none
|
| principals
|List|The requested principals|none
|
| source_address
|List|The requested source addresses as list of IP/CIDR|none
|
| destination_address
|List|The address (FQDN/hostname/IP/CIDR) of the destination host where the certificate will be used for authentication. Applicable for client certificates and is used for reporting/auditing only.|none
|
| extension
|List|The requested certificate extensions|none
|
Create a resource venafi_ssh_config
that will hold configuration needed by a remote host:
resource "venafi_ssh_config" "cit" {
template = "devops-terraform-cit"
}
The venafi_ssh_config
resource has the following option, which is required when obtaining configuration from the
template:
| Property | Type | Description | Default |
| —————————- | ——————- | ————————————————————————————————————————- | ————- |
|template
|String|The certificate issuing template|none
|
In addition, the following attributes are exported:
| Property | Type | Description |
| —————————- | ——————- | ————————————————————————————————————————- |
|ca_public_key
|String |The template’s CA PublicKey|
|principals
|List|The requested principals|
Copyright © Venafi, Inc. All rights reserved.
This solution is licensed under the Mozilla Public License, Version 2.0. See LICENSE
for the full license text.
Please direct questions/comments to opensource@venafi.com.