0
0
mirror of https://github.com/bpg/terraform-provider-proxmox.git synced 2025-07-05 13:33:58 +00:00

Add certificate resource

This commit is contained in:
Dan Petersen 2020-01-01 07:29:21 +01:00
parent 8396d92ddf
commit dffff063ab
10 changed files with 376 additions and 10 deletions

View File

@ -3,6 +3,7 @@
FEATURES: FEATURES:
* **New Data Source:** `proxmox_virtual_environment_dns` * **New Data Source:** `proxmox_virtual_environment_dns`
* **New Resource:** `proxmox_virtual_environment_certificate`
* **New Resource:** `proxmox_virtual_environment_dns` * **New Resource:** `proxmox_virtual_environment_dns`
ENHANCEMENTS: ENHANCEMENTS:

View File

@ -251,6 +251,17 @@ This data source doesn't accept arguments.
#### Virtual Environment #### Virtual Environment
##### Certificate (proxmox_virtual_environment_certificate)
###### Arguments
* `certificate` - (Required) The PEM encoded certificate
* `certificate_chain` - (Optional) The PEM encoded certificate chain
* `node_name` - (Required) A node name
* `private_key` - (Required) The PEM encoded private key
###### Attributes
This resource doesn't expose any additional attributes.
##### DNS (proxmox_virtual_environment_dns) ##### DNS (proxmox_virtual_environment_dns)
###### Arguments ###### Arguments

View File

@ -0,0 +1,28 @@
resource "proxmox_virtual_environment_certificate" "example" {
certificate = "${tls_self_signed_cert.proxmox_virtual_environment_certificate.cert_pem}"
node_name = "${data.proxmox_virtual_environment_nodes.example.names[0]}"
private_key = "${tls_private_key.proxmox_virtual_environment_certificate.private_key_pem}"
}
resource "tls_private_key" "proxmox_virtual_environment_certificate" {
algorithm = "RSA"
rsa_bits = 2048
}
resource "tls_self_signed_cert" "proxmox_virtual_environment_certificate" {
key_algorithm = "${tls_private_key.proxmox_virtual_environment_certificate.algorithm}"
private_key_pem = "${tls_private_key.proxmox_virtual_environment_certificate.private_key_pem}"
subject {
common_name = "example.com"
organization = "Terraform Provider for Proxmox"
}
validity_period_hours = 8760
allowed_uses = [
"key_encipherment",
"digital_signature",
"server_auth",
]
}

View File

@ -0,0 +1,37 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
package proxmox
import (
"errors"
"fmt"
"net/url"
)
// DeleteCertificate deletes the custom certificate for a node.
func (c *VirtualEnvironmentClient) DeleteCertificate(nodeName string, d *VirtualEnvironmentCertificateDeleteRequestBody) error {
return c.DoRequest(hmDELETE, fmt.Sprintf("nodes/%s/certificates/custom", url.PathEscape(nodeName)), d, nil)
}
// ListCertificates retrieves the list of certificates for a node.
func (c *VirtualEnvironmentClient) ListCertificates(nodeName string) (*[]VirtualEnvironmentCertificateListResponseData, error) {
resBody := &VirtualEnvironmentCertificateListResponseBody{}
err := c.DoRequest(hmGET, fmt.Sprintf("nodes/%s/certificates/info", url.PathEscape(nodeName)), nil, resBody)
if err != nil {
return nil, err
}
if resBody.Data == nil {
return nil, errors.New("The server did not include a data object in the response")
}
return resBody.Data, nil
}
// UpdateCertificate updates the custom certificate for a node.
func (c *VirtualEnvironmentClient) UpdateCertificate(nodeName string, d *VirtualEnvironmentCertificateUpdateRequestBody) error {
return c.DoRequest(hmPOST, fmt.Sprintf("nodes/%s/certificates/custom", url.PathEscape(nodeName)), d, nil)
}

View File

@ -0,0 +1,37 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
package proxmox
// VirtualEnvironmentCertificateDeleteRequestBody contains the data for a custom certificate delete request.
type VirtualEnvironmentCertificateDeleteRequestBody struct {
Restart *CustomBool `json:"restart,omitempty" url:"restart,omitempty,int"`
}
// VirtualEnvironmentCertificateListResponseBody contains the body from a certificate list response.
type VirtualEnvironmentCertificateListResponseBody struct {
Data *[]VirtualEnvironmentCertificateListResponseData `json:"data,omitempty"`
}
// VirtualEnvironmentCertificateListResponseData contains the data from a certificate list response.
type VirtualEnvironmentCertificateListResponseData struct {
Certificates *string `json:"pem,omitempty"`
FileName *string `json:"filename,omitempty"`
Fingerprint *string `json:"fingerprint,omitempty"`
Issuer *string `json:"issuer,omitempty"`
NotAfter *CustomTimestamp `json:"notafter,omitempty"`
NotBefore *CustomTimestamp `json:"notbefore,omitempty"`
PublicKeyBits *int `json:"public-key-bits,omitempty"`
PublicKeyType *string `json:"public-key-type,omitempty"`
Subject *string `json:"subject,omitempty"`
SubjectAlternativeNames *[]string `json:"san,omitempty"`
}
// VirtualEnvironmentCertificateUpdateRequestBody contains the body for a custom certificate update request.
type VirtualEnvironmentCertificateUpdateRequestBody struct {
Certificates string `json:"certificates" url:"certificates"`
Force *CustomBool `json:"force,omitempty" url:"force,omitempty,int"`
PrivateKey *string `json:"key,omitempty" url:"key,omitempty"`
Restart *CustomBool `json:"restart,omitempty" url:"restart,omitempty,int"`
}

View File

@ -4,12 +4,12 @@
package proxmox package proxmox
// VirtualEnvironmentDNSGetResponseBody contains the body from an pool get response. // VirtualEnvironmentDNSGetResponseBody contains the body from a DNS get response.
type VirtualEnvironmentDNSGetResponseBody struct { type VirtualEnvironmentDNSGetResponseBody struct {
Data *VirtualEnvironmentDNSGetResponseData `json:"data,omitempty"` Data *VirtualEnvironmentDNSGetResponseData `json:"data,omitempty"`
} }
// VirtualEnvironmentDNSGetResponseData contains the data from an pool get response. // VirtualEnvironmentDNSGetResponseData contains the data from a DNS get response.
type VirtualEnvironmentDNSGetResponseData struct { type VirtualEnvironmentDNSGetResponseData struct {
Server1 *string `json:"dns1,omitempty" url:"dns1,omitempty"` Server1 *string `json:"dns1,omitempty" url:"dns1,omitempty"`
Server2 *string `json:"dns2,omitempty" url:"dns2,omitempty"` Server2 *string `json:"dns2,omitempty" url:"dns2,omitempty"`
@ -17,7 +17,7 @@ type VirtualEnvironmentDNSGetResponseData struct {
SearchDomain *string `json:"search,omitempty" url:"search,omitempty"` SearchDomain *string `json:"search,omitempty" url:"search,omitempty"`
} }
// VirtualEnvironmentDNSUpdateRequestBody contains the data for an pool create request. // VirtualEnvironmentDNSUpdateRequestBody contains the body for a DNS update request.
type VirtualEnvironmentDNSUpdateRequestBody struct { type VirtualEnvironmentDNSUpdateRequestBody struct {
Server1 *string `json:"dns1,omitempty" url:"dns1,omitempty"` Server1 *string `json:"dns1,omitempty" url:"dns1,omitempty"`
Server2 *string `json:"dns2,omitempty" url:"dns2,omitempty"` Server2 *string `json:"dns2,omitempty" url:"dns2,omitempty"`

View File

@ -44,13 +44,14 @@ func Provider() *schema.Provider {
"proxmox_virtual_environment_version": dataSourceVirtualEnvironmentVersion(), "proxmox_virtual_environment_version": dataSourceVirtualEnvironmentVersion(),
}, },
ResourcesMap: map[string]*schema.Resource{ ResourcesMap: map[string]*schema.Resource{
"proxmox_virtual_environment_dns": resourceVirtualEnvironmentDNS(), "proxmox_virtual_environment_certificate": resourceVirtualEnvironmentCertificate(),
"proxmox_virtual_environment_file": resourceVirtualEnvironmentFile(), "proxmox_virtual_environment_dns": resourceVirtualEnvironmentDNS(),
"proxmox_virtual_environment_group": resourceVirtualEnvironmentGroup(), "proxmox_virtual_environment_file": resourceVirtualEnvironmentFile(),
"proxmox_virtual_environment_pool": resourceVirtualEnvironmentPool(), "proxmox_virtual_environment_group": resourceVirtualEnvironmentGroup(),
"proxmox_virtual_environment_role": resourceVirtualEnvironmentRole(), "proxmox_virtual_environment_pool": resourceVirtualEnvironmentPool(),
"proxmox_virtual_environment_user": resourceVirtualEnvironmentUser(), "proxmox_virtual_environment_role": resourceVirtualEnvironmentRole(),
"proxmox_virtual_environment_vm": resourceVirtualEnvironmentVM(), "proxmox_virtual_environment_user": resourceVirtualEnvironmentUser(),
"proxmox_virtual_environment_vm": resourceVirtualEnvironmentVM(),
}, },
Schema: map[string]*schema.Schema{ Schema: map[string]*schema.Schema{
mkProviderVirtualEnvironment: &schema.Schema{ mkProviderVirtualEnvironment: &schema.Schema{

View File

@ -0,0 +1,203 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
package proxmoxtf
import (
"fmt"
"strings"
"github.com/danitso/terraform-provider-proxmox/proxmox"
"github.com/hashicorp/terraform/helper/schema"
)
const (
dvResourceVirtualEnvironmentCertificateCertificateChain = ""
dvResourceVirtualEnvironmentCertificateOverwrite = false
mkResourceVirtualEnvironmentCertificateCertificate = "certificate"
mkResourceVirtualEnvironmentCertificateCertificateChain = "certificate_chain"
mkResourceVirtualEnvironmentCertificateNodeName = "node_name"
mkResourceVirtualEnvironmentCertificateOverwrite = "overwrite"
mkResourceVirtualEnvironmentCertificatePrivateKey = "private_key"
)
func resourceVirtualEnvironmentCertificate() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
mkResourceVirtualEnvironmentCertificateCertificate: &schema.Schema{
Type: schema.TypeString,
Description: "The PEM encoded certificate",
Required: true,
},
mkResourceVirtualEnvironmentCertificateCertificateChain: &schema.Schema{
Type: schema.TypeString,
Description: "The PEM encoded certificate chain",
Optional: true,
Default: dvResourceVirtualEnvironmentCertificateCertificateChain,
},
mkResourceVirtualEnvironmentCertificateNodeName: &schema.Schema{
Type: schema.TypeString,
Description: "The node name",
Required: true,
ForceNew: true,
},
mkResourceVirtualEnvironmentCertificateOverwrite: &schema.Schema{
Type: schema.TypeBool,
Description: "Whether to overwrite an existing certificate",
Optional: true,
Default: dvResourceVirtualEnvironmentCertificateOverwrite,
},
mkResourceVirtualEnvironmentCertificatePrivateKey: &schema.Schema{
Type: schema.TypeString,
Description: "The PEM encoded private key",
Required: true,
Sensitive: true,
},
},
Create: resourceVirtualEnvironmentCertificateCreate,
Read: resourceVirtualEnvironmentCertificateRead,
Update: resourceVirtualEnvironmentCertificateUpdate,
Delete: resourceVirtualEnvironmentCertificateDelete,
}
}
func resourceVirtualEnvironmentCertificateCreate(d *schema.ResourceData, m interface{}) error {
err := resourceVirtualEnvironmentCertificateUpdate(d, m)
if err != nil {
return err
}
nodeName := d.Get(mkResourceVirtualEnvironmentCertificateNodeName).(string)
d.SetId(fmt.Sprintf("%s_certificate", nodeName))
return nil
}
func resourceVirtualEnvironmentCertificateGetUpdateBody(d *schema.ResourceData, m interface{}) (*proxmox.VirtualEnvironmentCertificateUpdateRequestBody, error) {
certificate := d.Get(mkResourceVirtualEnvironmentCertificateCertificate).(string)
certificateChain := d.Get(mkResourceVirtualEnvironmentCertificateCertificateChain).(string)
overwrite := proxmox.CustomBool(d.Get(mkResourceVirtualEnvironmentCertificateOverwrite).(bool))
privateKey := d.Get(mkResourceVirtualEnvironmentCertificatePrivateKey).(string)
combinedCertificates := strings.TrimSpace(certificate) + "\n"
if certificateChain != "" {
combinedCertificates += strings.TrimSpace(certificateChain) + "\n"
}
force := overwrite
if d.Id() != "" {
force = proxmox.CustomBool(true)
}
restart := proxmox.CustomBool(true)
body := &proxmox.VirtualEnvironmentCertificateUpdateRequestBody{
Certificates: combinedCertificates,
Force: &force,
PrivateKey: &privateKey,
Restart: &restart,
}
return body, nil
}
func resourceVirtualEnvironmentCertificateRead(d *schema.ResourceData, m interface{}) error {
config := m.(providerConfiguration)
veClient, err := config.GetVEClient()
if err != nil {
return err
}
nodeName := d.Get(mkResourceVirtualEnvironmentCertificateNodeName).(string)
list, err := veClient.ListCertificates(nodeName)
if err != nil {
return err
}
d.Set(mkResourceVirtualEnvironmentCertificateCertificate, "")
d.Set(mkResourceVirtualEnvironmentCertificateCertificateChain, "")
certificateChain := d.Get(mkResourceVirtualEnvironmentCertificateCertificateChain).(string)
for _, c := range *list {
if c.FileName != nil && *c.FileName == "pveproxy-ssl.pem" {
if c.Certificates != nil {
newCertificate := ""
newCertificateChain := ""
if certificateChain != "" {
certificates := strings.Split(strings.TrimSpace(*c.Certificates), "\n")
newCertificate = certificates[0] + "\n"
if len(certificates) > 1 {
newCertificateChain = strings.Join(certificates[1:], "\n") + "\n"
}
} else {
newCertificate = *c.Certificates
}
d.Set(mkResourceVirtualEnvironmentCertificateCertificate, newCertificate)
d.Set(mkResourceVirtualEnvironmentCertificateCertificateChain, newCertificateChain)
}
}
}
return nil
}
func resourceVirtualEnvironmentCertificateUpdate(d *schema.ResourceData, m interface{}) error {
config := m.(providerConfiguration)
veClient, err := config.GetVEClient()
if err != nil {
return err
}
nodeName := d.Get(mkResourceVirtualEnvironmentCertificateNodeName).(string)
body, err := resourceVirtualEnvironmentCertificateGetUpdateBody(d, m)
if err != nil {
return err
}
err = veClient.UpdateCertificate(nodeName, body)
if err != nil {
return err
}
return resourceVirtualEnvironmentCertificateRead(d, m)
}
func resourceVirtualEnvironmentCertificateDelete(d *schema.ResourceData, m interface{}) error {
config := m.(providerConfiguration)
veClient, err := config.GetVEClient()
if err != nil {
return err
}
nodeName := d.Get(mkResourceVirtualEnvironmentCertificateNodeName).(string)
restart := proxmox.CustomBool(true)
err = veClient.DeleteCertificate(nodeName, &proxmox.VirtualEnvironmentCertificateDeleteRequestBody{
Restart: &restart,
})
if err != nil {
return err
}
d.SetId("")
return nil
}

View File

@ -0,0 +1,47 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
package proxmoxtf
import (
"testing"
"github.com/hashicorp/terraform/helper/schema"
)
// TestResourceVirtualEnvironmentCertificateInstantiation tests whether the ResourceVirtualEnvironmentCertificate instance can be instantiated.
func TestResourceVirtualEnvironmentCertificateInstantiation(t *testing.T) {
s := resourceVirtualEnvironmentCertificate()
if s == nil {
t.Fatalf("Cannot instantiate resourceVirtualEnvironmentCertificate")
}
}
// TestResourceVirtualEnvironmentCertificateSchema tests the resourceVirtualEnvironmentCertificate schema.
func TestResourceVirtualEnvironmentCertificateSchema(t *testing.T) {
s := resourceVirtualEnvironmentCertificate()
testRequiredArguments(t, s, []string{
mkResourceVirtualEnvironmentCertificateCertificate,
mkResourceVirtualEnvironmentCertificateNodeName,
mkResourceVirtualEnvironmentCertificatePrivateKey,
})
testOptionalArguments(t, s, []string{
mkResourceVirtualEnvironmentCertificateCertificateChain,
})
testSchemaValueTypes(t, s, []string{
mkResourceVirtualEnvironmentCertificateCertificate,
mkResourceVirtualEnvironmentCertificateCertificateChain,
mkResourceVirtualEnvironmentCertificateNodeName,
mkResourceVirtualEnvironmentCertificatePrivateKey,
}, []schema.ValueType{
schema.TypeString,
schema.TypeString,
schema.TypeString,
schema.TypeString,
})
}

View File

@ -349,6 +349,7 @@ func resourceVirtualEnvironmentVM() *schema.Resource {
Description: "The SSH password", Description: "The SSH password",
Optional: true, Optional: true,
ForceNew: true, ForceNew: true,
Sensitive: true,
Default: dvResourceVirtualEnvironmentVMCloudInitUserAccountPassword, Default: dvResourceVirtualEnvironmentVMCloudInitUserAccountPassword,
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
return strings.ReplaceAll(old, "*", "") == "" return strings.ReplaceAll(old, "*", "") == ""