Skip to content

Commit f2efcc4

Browse files
authored
Merge pull request #311 from NetApp/138-new-resource-security_certificates
138 new resource security certificate
2 parents 451fb49 + 6be281f commit f2efcc4

File tree

12 files changed

+893
-4
lines changed

12 files changed

+893
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ FEATURES:
140140
* **New Resource:** `netapp-ontap_qtree` ([#82](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/82))
141141
* **New Resource:** `netapp-ontap_qos_policy` ([#76](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/76))
142142
* **New Resource:** `netapp-security_login_message` ([#18](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/18))
143+
* **New Resource:** `netapp-ontap_security_certificate` ([#138](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/138))
143144

144145
ENHANCEMENTS:
145146

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "netapp-ontap_security_certificate Resource - terraform-provider-netapp-ontap"
4+
subcategory: ""
5+
description: |-
6+
SecurityCertificate resource
7+
---
8+
9+
# netapp-ontap_security_certificate (Resource)
10+
11+
Create/ install/ sign a certificate
12+
13+
### Related ONTAP commands
14+
```commandline
15+
* security certificate show
16+
* security certificate create
17+
* security certificate install
18+
* security certificate sign
19+
```
20+
21+
## Supported Platforms
22+
* On-prem ONTAP system 9.6 or higher
23+
* Amazon FSx for NetApp ONTAP
24+
25+
## Example Usage
26+
27+
```terraform
28+
# creating a cluster-scoped certificate
29+
resource "netapp-ontap_security_certificate" "create_certificate1" {
30+
cx_profile_name = "cluster5"
31+
name = "test_ca_cert1"
32+
common_name = "test_ca_cert"
33+
type = "root_ca"
34+
expiry_time = "P365DT"
35+
}
36+
37+
# creating a certificate
38+
resource "netapp-ontap_security_certificate" "create_certificate2" {
39+
cx_profile_name = "cluster5"
40+
name = "tfsvm_ca_cert1"
41+
common_name = "tfsvm_ca_cert"
42+
type = "root_ca"
43+
svm_name = "tfsvm"
44+
expiry_time = "P365DT"
45+
}
46+
47+
# signing a certificate
48+
resource "netapp-ontap_security_certificate" "sign_certificate" {
49+
cx_profile_name = "cluster5"
50+
name = "tfsvm_ca_cert1"
51+
common_name = "tfsvm_ca_cert"
52+
type = "root_ca"
53+
svm_name = "svm1" # SVM on which the signed certificate will exist
54+
expiry_time = "P90DT"
55+
signing_request = <<-EOT
56+
-----BEGIN CERTIFICATE REQUEST-----
57+
signing-request
58+
-----END CERTIFICATE REQUEST-----
59+
EOT
60+
}
61+
62+
# installing a certificate
63+
resource "netapp-ontap_security_certificate" "install_certificate" {
64+
cx_profile_name = "cluster5"
65+
common_name = "svm1_cert1"
66+
type = "server"
67+
svm_name = "svm1"
68+
expiry_time = "P90DT"
69+
public_certificate = <<-EOT
70+
-----BEGIN CERTIFICATE-----
71+
certificate
72+
-----END CERTIFICATE-----
73+
EOT
74+
75+
private_key = <<-EOT
76+
-----BEGIN PRIVATE KEY-----
77+
private-key
78+
-----END PRIVATE KEY-----
79+
EOT
80+
}
81+
```
82+
83+
<!-- schema generated by tfplugindocs -->
84+
## Schema
85+
86+
### Required
87+
88+
- `cx_profile_name` (String) Connection profile name.
89+
- `common_name` (String) Common name of the certificate.
90+
- `type` (String) Type of certificate.
91+
92+
### Optional
93+
94+
- `expiry_time` (String) Certificate expiration time, in ISO 8601 duration format or date and time format.
95+
- `hash_function` (String) Hashing function.
96+
- `key_size` (Number) Key size of the certificate in bits.
97+
- `name` (String) The unique name of the security certificate per SVM.
98+
- `private_key` (String, Sensitive) Private key Certificate in PEM format. Only valid when installing a CA-signed certificate.
99+
- `public_certificate` (String) Public key Certificate in PEM format. If this is not provided during create action, a self-signed certificate is created.
100+
- `signing_request` (String) Certificate signing request to be signed by the given certificate authority. Request should be in X509 PEM format.
101+
- `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.
102+
103+
### Read-Only
104+
105+
- `ca` (String) Certificate authority.
106+
- `id` (String) UUID of the certificate.
107+
- `scope` (String) Set to 'svm' for certificates installed in a SVM. Otherwise, set to 'cluster'.
108+
- `serial_number` (String) Serial number of the certificate.
109+
- `signed_certificate` (String) Signed public key Certificate in PEM format that is returned while signing a certificate.
110+
111+
## Import
112+
This resource supports import, which allows you to import existing security certificate into the state of this resource.
113+
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.
114+
115+
id = `name`,`common_name`,`type`,`cx_profile_name`
116+
117+
### Terraform Import
118+
119+
For example
120+
121+
Import with certificate name; recommended for ONTAP 9.8 or later
122+
```shell
123+
terraform import netapp-ontap_security_certificate.cert_import tfsvm_ca_cert1,tfsvm_ca_cert,root_ca,cluster5
124+
```
125+
126+
Import with certificate common name & type; applicable for ONTAP 9.6 or 9.7
127+
```shell
128+
terraform import netapp-ontap_security_certificate.cert_import svm1_cert1,server,cluster5
129+
```
130+
131+
### Terraform Import Block
132+
This requires Terraform 1.5 or higher, and will auto create the configuration for you
133+
134+
First create the block
135+
```terraform
136+
import {
137+
to = netapp-ontap_security_certificate.cert_import
138+
id = "tfsvm_ca_cert1,tfsvm_ca_cert,root_ca,cluster5"
139+
}
140+
```
141+
Next run, this will auto create the configuration for you
142+
```shell
143+
terraform plan -generate-config-out=generated.tf
144+
```
145+
This will generate a file called generated.tf, which will contain the configuration for the imported resource
146+
```terraform
147+
# __generated__ by Terraform
148+
# Please review these resources and move them into your main configuration files.
149+
150+
# __generated__ by Terraform from "tfsvm_ca_cert1,tfsvm_ca_cert,root_ca,cluster5"
151+
resource "netapp-ontap_security_certificate" "cert_import" {
152+
common_name = "tfsvm_ca_cert"
153+
cx_profile_name = "cluster5"
154+
expiry_time = "2025-10-04T01:24:54-04:00"
155+
hash_function = "sha256"
156+
key_size = 2048
157+
name = "tfsvm_ca_cert1"
158+
private_key = null # sensitive
159+
public_certificate = "-----BEGIN CERTIFICATE-----\ncertificate\n-----END CERTIFICATE-----\n"
160+
signing_request = null
161+
svm_name = "tfsvm"
162+
type = "root_ca"
163+
}
164+
```
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../provider/provider.tf
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# creating a cluster-scoped certificate
2+
resource "netapp-ontap_security_certificate" "create_certificate1" {
3+
cx_profile_name = "cluster5"
4+
name = "test_ca_cert1"
5+
common_name = "test_ca_cert"
6+
type = "root_ca"
7+
expiry_time = "P365DT"
8+
}
9+
10+
# creating a certificate
11+
resource "netapp-ontap_security_certificate" "create_certificate2" {
12+
cx_profile_name = "cluster5"
13+
name = "tfsvm_ca_cert1"
14+
common_name = "tfsvm_ca_cert"
15+
type = "root_ca"
16+
svm_name = "tfsvm"
17+
expiry_time = "P365DT"
18+
}
19+
20+
# signing a certificate
21+
resource "netapp-ontap_security_certificate" "sign_certificate" {
22+
cx_profile_name = "cluster5"
23+
name = "tfsvm_ca_cert1"
24+
common_name = "tfsvm_ca_cert"
25+
type = "root_ca"
26+
svm_name = "svm1" # SVM on which the signed certificate will exist
27+
expiry_time = "P90DT"
28+
signing_request = <<-EOT
29+
-----BEGIN CERTIFICATE REQUEST-----
30+
signing-request
31+
-----END CERTIFICATE REQUEST-----
32+
EOT
33+
}
34+
35+
# installing a certificate
36+
resource "netapp-ontap_security_certificate" "install_certificate" {
37+
cx_profile_name = "cluster5"
38+
common_name = "svm1_cert1"
39+
type = "server"
40+
svm_name = "svm1"
41+
expiry_time = "P90DT"
42+
public_certificate = <<-EOT
43+
-----BEGIN CERTIFICATE-----
44+
certificate
45+
-----END CERTIFICATE-----
46+
EOT
47+
48+
private_key = <<-EOT
49+
-----BEGIN PRIVATE KEY-----
50+
private-key
51+
-----END PRIVATE KEY-----
52+
EOT
53+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../provider/terraform.tfvars
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../provider/variables.tf

internal/interfaces/security_certificate.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,32 @@ type SecurityCertificateGetDataModelONTAP struct {
2626
PublicCertificate string `mapstructure:"public_certificate"`
2727
}
2828

29+
// SignedSecurityCertificateGetDataModelONTAP describes the GET record data model using go types for mapping.
30+
type SignedSecurityCertificateGetDataModelONTAP struct {
31+
SignedCertificate string `mapstructure:"public_certificate"`
32+
}
33+
34+
// SecurityCertificateResourceCreateBodyDataModelONTAP describes the create/install body data model using go types for mapping.
35+
type SecurityCertificateResourceCreateBodyDataModelONTAP struct {
36+
Name string `mapstructure:"name,omitempty"`
37+
CommonName string `mapstructure:"common_name"`
38+
Type string `mapstructure:"type"`
39+
SVM svm `mapstructure:"svm,omitempty"`
40+
Scope string `mapstructure:"scope,omitempty"`
41+
PublicCertificate string `mapstructure:"public_certificate,omitempty"`
42+
PrivateKey string `mapstructure:"private_key,omitempty"`
43+
HashFunction string `mapstructure:"hash_function,omitempty"`
44+
KeySize int64 `mapstructure:"key_size,omitempty"`
45+
ExpiryTime string `mapstructure:"expiry_time,omitempty"`
46+
}
47+
48+
// SecurityCertificateResourceSignBodyDataModelONTAP describes the signing body data model using go types for mapping.
49+
type SecurityCertificateResourceSignBodyDataModelONTAP struct {
50+
SigningRequest string `mapstructure:"signing_request"`
51+
HashFunction string `mapstructure:"hash_function,omitempty"`
52+
ExpiryTime string `mapstructure:"expiry_time,omitempty"`
53+
}
54+
2955
// SecurityCertificateDataSourceFilterModel describes the data source data model for queries.
3056
type SecurityCertificateDataSourceFilterModel struct {
3157
SVMName string `mapstructure:"svm.name"`
@@ -166,3 +192,59 @@ func GetSecurityCertificates(errorHandler *utils.ErrorHandler, r restclient.Rest
166192
tflog.Debug(errorHandler.Ctx, fmt.Sprintf("Read security_certificates data source: %#v", dataONTAP))
167193
return dataONTAP, nil
168194
}
195+
196+
// CreateOrInstallSecurityCertificate to create/ install a security certificate
197+
func CreateOrInstallSecurityCertificate(errorHandler *utils.ErrorHandler, r restclient.RestClient, body SecurityCertificateResourceCreateBodyDataModelONTAP, operation string) (*SecurityCertificateGetDataModelONTAP, error) {
198+
api := "security/certificates"
199+
var bodyMap map[string]interface{}
200+
if err := mapstructure.Decode(body, &bodyMap); err != nil {
201+
return nil, errorHandler.MakeAndReportError("error encoding security certificate body", fmt.Sprintf("error on encoding %s body: %s, body: %#v", api, err, body))
202+
}
203+
query := r.NewQuery()
204+
query.Add("return_records", "true")
205+
206+
statusCode, response, err := r.CallCreateMethod(api, query, bodyMap)
207+
if err != nil {
208+
return nil, errorHandler.MakeAndReportError(fmt.Sprintf("error %s security certificate", operation), fmt.Sprintf("error on POST %s: %s, statusCode %d", api, err, statusCode))
209+
}
210+
211+
var dataONTAP SecurityCertificateGetDataModelONTAP
212+
if err := mapstructure.Decode(response.Records[0], &dataONTAP); err != nil {
213+
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))
214+
}
215+
tflog.Debug(errorHandler.Ctx, fmt.Sprintf("Created security certificate: %#v", dataONTAP))
216+
return &dataONTAP, nil
217+
}
218+
219+
// SignSecurityCertificate to sign a security_certificate
220+
func SignSecurityCertificate(errorHandler *utils.ErrorHandler, r restclient.RestClient, uuid string, body SecurityCertificateResourceSignBodyDataModelONTAP) (*SignedSecurityCertificateGetDataModelONTAP, error) {
221+
api := "security/certificates"
222+
var bodyMap map[string]interface{}
223+
if err := mapstructure.Decode(body, &bodyMap); err != nil {
224+
return nil, errorHandler.MakeAndReportError("error encoding security certificate body", fmt.Sprintf("error on encoding %s body: %s, body: %#v", api, err, body))
225+
}
226+
query := r.NewQuery()
227+
query.Add("return_records", "true")
228+
229+
statusCode, response, err := r.CallCreateMethod(api+"/"+uuid+"/sign", query, bodyMap)
230+
if err != nil {
231+
return nil, errorHandler.MakeAndReportError("error signing security certificate", fmt.Sprintf("error on POST %s: %s, statusCode %d", api, err, statusCode))
232+
}
233+
234+
var dataONTAP SignedSecurityCertificateGetDataModelONTAP
235+
if err := mapstructure.Decode(response.Records[0], &dataONTAP); err != nil {
236+
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))
237+
}
238+
tflog.Debug(errorHandler.Ctx, fmt.Sprintf("Signed security certificate: %#v", dataONTAP))
239+
return &dataONTAP, nil
240+
}
241+
242+
// DeleteSecurityCertificate to delete a security_certificate
243+
func DeleteSecurityCertificate(errorHandler *utils.ErrorHandler, r restclient.RestClient, uuid string) error {
244+
api := "security/certificates"
245+
statusCode, _, err := r.CallDeleteMethod(api+"/"+uuid, nil, nil)
246+
if err != nil {
247+
return errorHandler.MakeAndReportError("error deleting security certificate", fmt.Sprintf("error on DELETE %s: %s, statusCode %d", api, err, statusCode))
248+
}
249+
return nil
250+
}

internal/provider/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ func (p *ONTAPProvider) Resources(ctx context.Context) []func() resource.Resourc
235235
protocols.NewProtocolsSanIgroupResource,
236236
protocols.NewProtocolsSanLunMapResource,
237237
security.NewSecurityAccountResource,
238+
security.NewSecurityCertificateResource,
238239
security.NewSecurityLoginMessageResource,
239240
security.NewSecurityRoleResource,
240241
snapmirror.NewSnapmirrorPolicyResource,

0 commit comments

Comments
 (0)