Skip to content

Commit

Permalink
Merge pull request #311 from NetApp/138-new-resource-security_certifi…
Browse files Browse the repository at this point in the history
…cates

138 new resource security certificate
  • Loading branch information
carchi8py authored Dec 9, 2024
2 parents 6f16570 + c72450d commit 3c2b0ea
Show file tree
Hide file tree
Showing 12 changed files with 893 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ FEATURES:
* **New Resource:** `netapp-ontap_qtree` ([#82](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/82))
* **New Resource:** `netapp-ontap_qos_policy` ([#76](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/76))
* **New Resource:** `netapp-security_login_message` ([#18](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/18))
* **New Resource:** `netapp-ontap_security_certificate` ([#138](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/138))

ENHANCEMENTS:

Expand Down
164 changes: 164 additions & 0 deletions docs/resources/security_certificate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "netapp-ontap_security_certificate Resource - terraform-provider-netapp-ontap"
subcategory: ""
description: |-
SecurityCertificate resource
---

# netapp-ontap_security_certificate (Resource)

Create/ install/ sign a certificate

### Related ONTAP commands
```commandline
* security certificate show
* security certificate create
* security certificate install
* security certificate sign
```

## Supported Platforms
* On-prem ONTAP system 9.6 or higher
* Amazon FSx for NetApp ONTAP

## Example Usage

```terraform
# creating a cluster-scoped certificate
resource "netapp-ontap_security_certificate" "create_certificate1" {
cx_profile_name = "cluster5"
name = "test_ca_cert1"
common_name = "test_ca_cert"
type = "root_ca"
expiry_time = "P365DT"
}
# creating a certificate
resource "netapp-ontap_security_certificate" "create_certificate2" {
cx_profile_name = "cluster5"
name = "tfsvm_ca_cert1"
common_name = "tfsvm_ca_cert"
type = "root_ca"
svm_name = "tfsvm"
expiry_time = "P365DT"
}
# signing a certificate
resource "netapp-ontap_security_certificate" "sign_certificate" {
cx_profile_name = "cluster5"
name = "tfsvm_ca_cert1"
common_name = "tfsvm_ca_cert"
type = "root_ca"
svm_name = "svm1" # SVM on which the signed certificate will exist
expiry_time = "P90DT"
signing_request = <<-EOT
-----BEGIN CERTIFICATE REQUEST-----
signing-request
-----END CERTIFICATE REQUEST-----
EOT
}
# installing a certificate
resource "netapp-ontap_security_certificate" "install_certificate" {
cx_profile_name = "cluster5"
common_name = "svm1_cert1"
type = "server"
svm_name = "svm1"
expiry_time = "P90DT"
public_certificate = <<-EOT
-----BEGIN CERTIFICATE-----
certificate
-----END CERTIFICATE-----
EOT
private_key = <<-EOT
-----BEGIN PRIVATE KEY-----
private-key
-----END PRIVATE KEY-----
EOT
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `cx_profile_name` (String) Connection profile name.
- `common_name` (String) Common name of the certificate.
- `type` (String) Type of certificate.

### Optional

- `expiry_time` (String) Certificate expiration time, in ISO 8601 duration format or date and time format.
- `hash_function` (String) Hashing function.
- `key_size` (Number) Key size of the certificate in bits.
- `name` (String) The unique name of the security certificate per SVM.
- `private_key` (String, Sensitive) Private key Certificate in PEM format. Only valid when installing a CA-signed certificate.
- `public_certificate` (String) Public key Certificate in PEM format. If this is not provided during create action, a self-signed certificate is created.
- `signing_request` (String) Certificate signing request to be signed by the given certificate authority. Request should be in X509 PEM format.
- `svm_name` (String) Name of the SVM in which the certificate is created or installed or the SVM on which the signed certificate will exist.

### Read-Only

- `ca` (String) Certificate authority.
- `id` (String) UUID of the certificate.
- `scope` (String) Set to 'svm' for certificates installed in a SVM. Otherwise, set to 'cluster'.
- `serial_number` (String) Serial number of the certificate.
- `signed_certificate` (String) Signed public key Certificate in PEM format that is returned while signing a certificate.

## Import
This resource supports import, which allows you to import existing security certificate into the state of this resource.
Import require a unique ID composed of the security certificate name, common name, type and connection profile, separated by a comma or security certificate common name, type, and connection profile, separated by a comma.

id = `name`,`common_name`,`type`,`cx_profile_name`

### Terraform Import

For example

Import with certificate name; recommended for ONTAP 9.8 or later
```shell
terraform import netapp-ontap_security_certificate.cert_import tfsvm_ca_cert1,tfsvm_ca_cert,root_ca,cluster5
```

Import with certificate common name & type; applicable for ONTAP 9.6 or 9.7
```shell
terraform import netapp-ontap_security_certificate.cert_import svm1_cert1,server,cluster5
```

### Terraform Import Block
This requires Terraform 1.5 or higher, and will auto create the configuration for you

First create the block
```terraform
import {
to = netapp-ontap_security_certificate.cert_import
id = "tfsvm_ca_cert1,tfsvm_ca_cert,root_ca,cluster5"
}
```
Next run, this will auto create the configuration for you
```shell
terraform plan -generate-config-out=generated.tf
```
This will generate a file called generated.tf, which will contain the configuration for the imported resource
```terraform
# __generated__ by Terraform
# Please review these resources and move them into your main configuration files.
# __generated__ by Terraform from "tfsvm_ca_cert1,tfsvm_ca_cert,root_ca,cluster5"
resource "netapp-ontap_security_certificate" "cert_import" {
common_name = "tfsvm_ca_cert"
cx_profile_name = "cluster5"
expiry_time = "2025-10-04T01:24:54-04:00"
hash_function = "sha256"
key_size = 2048
name = "tfsvm_ca_cert1"
private_key = null # sensitive
public_certificate = "-----BEGIN CERTIFICATE-----\ncertificate\n-----END CERTIFICATE-----\n"
signing_request = null
svm_name = "tfsvm"
type = "root_ca"
}
```
53 changes: 53 additions & 0 deletions examples/resources/netapp-ontap_security_certificate/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# creating a cluster-scoped certificate
resource "netapp-ontap_security_certificate" "create_certificate1" {
cx_profile_name = "cluster5"
name = "test_ca_cert1"
common_name = "test_ca_cert"
type = "root_ca"
expiry_time = "P365DT"
}

# creating a certificate
resource "netapp-ontap_security_certificate" "create_certificate2" {
cx_profile_name = "cluster5"
name = "tfsvm_ca_cert1"
common_name = "tfsvm_ca_cert"
type = "root_ca"
svm_name = "tfsvm"
expiry_time = "P365DT"
}

# signing a certificate
resource "netapp-ontap_security_certificate" "sign_certificate" {
cx_profile_name = "cluster5"
name = "tfsvm_ca_cert1"
common_name = "tfsvm_ca_cert"
type = "root_ca"
svm_name = "svm1" # SVM on which the signed certificate will exist
expiry_time = "P90DT"
signing_request = <<-EOT
-----BEGIN CERTIFICATE REQUEST-----
signing-request
-----END CERTIFICATE REQUEST-----
EOT
}

# installing a certificate
resource "netapp-ontap_security_certificate" "install_certificate" {
cx_profile_name = "cluster5"
common_name = "svm1_cert1"
type = "server"
svm_name = "svm1"
expiry_time = "P90DT"
public_certificate = <<-EOT
-----BEGIN CERTIFICATE-----
certificate
-----END CERTIFICATE-----
EOT

private_key = <<-EOT
-----BEGIN PRIVATE KEY-----
private-key
-----END PRIVATE KEY-----
EOT
}
82 changes: 82 additions & 0 deletions internal/interfaces/security_certificate.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,32 @@ type SecurityCertificateGetDataModelONTAP struct {
PublicCertificate string `mapstructure:"public_certificate"`
}

// SignedSecurityCertificateGetDataModelONTAP describes the GET record data model using go types for mapping.
type SignedSecurityCertificateGetDataModelONTAP struct {
SignedCertificate string `mapstructure:"public_certificate"`
}

// SecurityCertificateResourceCreateBodyDataModelONTAP describes the create/install body data model using go types for mapping.
type SecurityCertificateResourceCreateBodyDataModelONTAP struct {
Name string `mapstructure:"name,omitempty"`
CommonName string `mapstructure:"common_name"`
Type string `mapstructure:"type"`
SVM svm `mapstructure:"svm,omitempty"`
Scope string `mapstructure:"scope,omitempty"`
PublicCertificate string `mapstructure:"public_certificate,omitempty"`
PrivateKey string `mapstructure:"private_key,omitempty"`
HashFunction string `mapstructure:"hash_function,omitempty"`
KeySize int64 `mapstructure:"key_size,omitempty"`
ExpiryTime string `mapstructure:"expiry_time,omitempty"`
}

// SecurityCertificateResourceSignBodyDataModelONTAP describes the signing body data model using go types for mapping.
type SecurityCertificateResourceSignBodyDataModelONTAP struct {
SigningRequest string `mapstructure:"signing_request"`
HashFunction string `mapstructure:"hash_function,omitempty"`
ExpiryTime string `mapstructure:"expiry_time,omitempty"`
}

// SecurityCertificateDataSourceFilterModel describes the data source data model for queries.
type SecurityCertificateDataSourceFilterModel struct {
SVMName string `mapstructure:"svm.name"`
Expand Down Expand Up @@ -166,3 +192,59 @@ func GetSecurityCertificates(errorHandler *utils.ErrorHandler, r restclient.Rest
tflog.Debug(errorHandler.Ctx, fmt.Sprintf("Read security_certificates data source: %#v", dataONTAP))
return dataONTAP, nil
}

// CreateOrInstallSecurityCertificate to create/ install a security certificate
func CreateOrInstallSecurityCertificate(errorHandler *utils.ErrorHandler, r restclient.RestClient, body SecurityCertificateResourceCreateBodyDataModelONTAP, operation string) (*SecurityCertificateGetDataModelONTAP, error) {
api := "security/certificates"
var bodyMap map[string]interface{}
if err := mapstructure.Decode(body, &bodyMap); err != nil {
return nil, errorHandler.MakeAndReportError("error encoding security certificate body", fmt.Sprintf("error on encoding %s body: %s, body: %#v", api, err, body))
}
query := r.NewQuery()
query.Add("return_records", "true")

statusCode, response, err := r.CallCreateMethod(api, query, bodyMap)
if err != nil {
return nil, errorHandler.MakeAndReportError(fmt.Sprintf("error %s security certificate", operation), fmt.Sprintf("error on POST %s: %s, statusCode %d", api, err, statusCode))
}

var dataONTAP SecurityCertificateGetDataModelONTAP
if err := mapstructure.Decode(response.Records[0], &dataONTAP); err != nil {
return nil, errorHandler.MakeAndReportError("error decoding security certificate info", fmt.Sprintf("error on decode storage/security_certificatess info: %s, statusCode %d, response %#v", err, statusCode, response))
}
tflog.Debug(errorHandler.Ctx, fmt.Sprintf("Created security certificate: %#v", dataONTAP))
return &dataONTAP, nil
}

// SignSecurityCertificate to sign a security_certificate
func SignSecurityCertificate(errorHandler *utils.ErrorHandler, r restclient.RestClient, uuid string, body SecurityCertificateResourceSignBodyDataModelONTAP) (*SignedSecurityCertificateGetDataModelONTAP, error) {
api := "security/certificates"
var bodyMap map[string]interface{}
if err := mapstructure.Decode(body, &bodyMap); err != nil {
return nil, errorHandler.MakeAndReportError("error encoding security certificate body", fmt.Sprintf("error on encoding %s body: %s, body: %#v", api, err, body))
}
query := r.NewQuery()
query.Add("return_records", "true")

statusCode, response, err := r.CallCreateMethod(api+"/"+uuid+"/sign", query, bodyMap)
if err != nil {
return nil, errorHandler.MakeAndReportError("error signing security certificate", fmt.Sprintf("error on POST %s: %s, statusCode %d", api, err, statusCode))
}

var dataONTAP SignedSecurityCertificateGetDataModelONTAP
if err := mapstructure.Decode(response.Records[0], &dataONTAP); err != nil {
return nil, errorHandler.MakeAndReportError("error decoding signed security certificate info", fmt.Sprintf("error on decode storage/security_certificatess/{ca.uuid}/sign info: %s, statusCode %d, response %#v", err, statusCode, response))
}
tflog.Debug(errorHandler.Ctx, fmt.Sprintf("Signed security certificate: %#v", dataONTAP))
return &dataONTAP, nil
}

// DeleteSecurityCertificate to delete a security_certificate
func DeleteSecurityCertificate(errorHandler *utils.ErrorHandler, r restclient.RestClient, uuid string) error {
api := "security/certificates"
statusCode, _, err := r.CallDeleteMethod(api+"/"+uuid, nil, nil)
if err != nil {
return errorHandler.MakeAndReportError("error deleting security certificate", fmt.Sprintf("error on DELETE %s: %s, statusCode %d", api, err, statusCode))
}
return nil
}
1 change: 1 addition & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ func (p *ONTAPProvider) Resources(ctx context.Context) []func() resource.Resourc
protocols.NewProtocolsSanIgroupResource,
protocols.NewProtocolsSanLunMapResource,
security.NewSecurityAccountResource,
security.NewSecurityCertificateResource,
security.NewSecurityLoginMessageResource,
security.NewSecurityRoleResource,
snapmirror.NewSnapmirrorPolicyResource,
Expand Down
Loading

0 comments on commit 3c2b0ea

Please sign in to comment.