Skip to content

Commit

Permalink
Merge pull request #279 from NetApp/18-new-resource-security_login_me…
Browse files Browse the repository at this point in the history
…ssage

18 new resource security login message
  • Loading branch information
carchi8py committed Aug 30, 2024
2 parents 916b101 + 8ee900d commit 6027687
Show file tree
Hide file tree
Showing 22 changed files with 519 additions and 65 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ FEATURES:
* **New Resource:** `netapp-ontap_quota_rules` ([#136](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/136))
* **New Resource:** `netapp-ontap_volumes_files` ([#5](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/5))
* **New Resource:** `netapp-ontap_qos_policies` ([#76](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/76))
* **New Resource:** `netapp-security_login_messages` ([#18](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/18))

ENHANCEMENTS:
* **netapp-ontap_lun**: added `size_unit` option. ([#227](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/227))
Expand Down
12 changes: 6 additions & 6 deletions docs/resources/security_account_resource.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "netapp-ontap_security_account Resource - terraform-provider-netapp-ontap"
page_title: "netapp-ontap_security_accounts Resource - terraform-provider-netapp-ontap"
subcategory: "Security"
description: |-
SecurityAccount resource
SecurityAccounts resource
---

# Resource Security Account
Expand All @@ -27,7 +27,7 @@ Create/Modify/Delete a ONTAP user account
## Example Usage

```terraform
resource "netapp-ontap_security_account" "security_account" {
resource "netapp-ontap_security_accounts" "security_account" {
# required to know which system to interface with
cx_profile_name = "cluster4"
name = "carchitest"
Expand Down Expand Up @@ -103,7 +103,7 @@ id = `name`, `cx_profile_name`

For example
```shell
terraform import netapp-ontap_security_account.act_import acc_user,cluster4
terraform import netapp-ontap_security_accounts.act_import acc_user,cluster4
```

### Terraform Import Block
Expand All @@ -112,7 +112,7 @@ This requires Terraform 1.5 or higher, and will auto create the configuration fo
First create the block
```terraform
import {
to = netapp-ontap_security_account.act_import
to = netapp-ontap_security_accounts.act_import
id = "acc_user,cluster4"
}
```
Expand All @@ -125,7 +125,7 @@ This will generate a file called generated.tf, which will contain the configurat
# __generated__ by Terraform
# Please review these resources and move them into your main configuration files.
# __generated__ by Terraform from "acc_user,cluster4"
resource "netapp-ontap_security_account" "act_import" {
resource "netapp-ontap_security_accounts" "act_import" {
cx_profile_name = "cluster4"
name = "acc_user"
applications = [
Expand Down
104 changes: 104 additions & 0 deletions docs/resources/security_login_message.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "netapp-ontap_security_login_messages Resource - terraform-provider-netapp-ontap"
subcategory: "Security"
description: |-
SecurityLoginMessage resource
---

# Resource security login message

Update/Import Security Login Message
The `security_login_messages` resource does not support creation or deletion operations. Users must first import the existing resource and then perform updates as needed.

### Related ONTAP commands
```commandline
* security login banner show
* security login motd show
* security login banner modify
* security login motd modify
```

## Supported Platforms
* On-perm ONTAP system 9.6 or higher

## Example Usage

```terraform
resource "netapp-ontap_security_login_messages" "msg_import_cluster" {
banner = "test banner"
cx_profile_name = "cluster4"
message = "test message"
scope = "cluster"
show_cluster_message = true
}
resource "netapp-ontap_security_login_messages" "msg_import_svm" {
banner = "test banner"
cx_profile_name = "cluster4"
message = "test message"
scope = "svm"
show_cluster_message = true
svm_name = "svm5"
}
```

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

### Required

- `cx_profile_name` (String) Connection profile name

### Optional

- `banner` (String) SecurityLoginMessage login banner
- `message` (String) SecurityLoginMessage the message of the day (MOTD). This message appears just before the clustershell prompt after a successful login.
- `scope` (String) SecurityLoginMessage network scope
- `show_cluster_message` (Boolean) Specifies whether to show a cluster-level message before the SVM message when logging in as an SVM administrator
- `svm_name` (String) SecurityLoginMessage svm name

### Read-Only

- `id` (String) SecurityLoginMessage ID

### Terraform Import

For example
Import with cluster info only
```shell
terraform import netapp-ontap_security_login_messages.cluster_import cluster4
```
Import with svm and cluster info
```shell
terraform import netapp-ontap_security_login_messages.svm_import svm1,cluster4
```

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

First create the block. Use import svm message as an example
```terraform
import {
to = netapp-ontap_security_login_messages.svm_import
id = "svm1,cluster4"
}
```
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 "svm1,cluster4"
resource "netapp-ontap_security_login_messages" "msg_import_svm" {
banner = "test banner"
cx_profile_name = "cluster4"
message = "test message\n12345"
scope = "svm"
show_cluster_message = true
svm_name = "svm1"
}
```
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
resource "netapp-ontap_security_account" "security_account" {
resource "netapp-ontap_security_accounts" "security_account" {
# required to know which system to interface with
cx_profile_name = "cluster4"
name = "carchitest"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@

resource "netapp-ontap_security_login_messages" "msg_import_cluster" {
banner = "test banner"
cx_profile_name = "cluster4"
message = "test message"
scope = "cluster"
show_cluster_message = true
}

resource "netapp-ontap_security_login_messages" "msg_import_svm" {
banner = "test banner"
cx_profile_name = "cluster4"
message = "test message"
scope = "svm"
show_cluster_message = true
svm_name = "svm5"
}
58 changes: 16 additions & 42 deletions internal/interfaces/security_login_message.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,6 @@ import (
"github.com/netapp/terraform-provider-netapp-ontap/internal/utils"
)

// TODO:
// copy this file to match you data source (should match internal/interfaces/security_login_message.go)
// replace SecurityLoginMessage with the name of the resource, following go conventions, eg IPInterface
// replace security_login_message with the name of the resource, for logging purposes, eg ip_interface
// replace api_url with API, eg ip/interfaces
// delete these 5 lines

// SecurityLoginMessageGetDataModelONTAP describes the GET record data model using go types for mapping.
type SecurityLoginMessageGetDataModelONTAP struct {
Message string `mapstructure:"message"`
Expand All @@ -26,10 +19,11 @@ type SecurityLoginMessageGetDataModelONTAP struct {
UUID string `mapstructure:"uuid"`
}

// SecurityLoginMessageResourceBodyDataModelONTAP describes the body data model using go types for mapping.
// SecurityLoginMessageResourceBodyDataModelONTAP describes the body data model using go types for mapping. Both svm and scope are not allowed to be updated.
type SecurityLoginMessageResourceBodyDataModelONTAP struct {
Name string `mapstructure:"name"`
SVM svm `mapstructure:"svm"`
Banner string `mapstructure:"banner"`
Message string `mapstructure:"message"`
ShowClusterMessage bool `mapstructure:"show_cluster_message"`
}

// SecurityLoginMessageDataSourceFilterModel describes the data source data model for queries.
Expand All @@ -40,17 +34,12 @@ type SecurityLoginMessageDataSourceFilterModel struct {
SVMName string `mapstructure:"svm.name"`
}

// GetSecurityLoginMessageByBannerMotd to get security_login_message info
// Retrieves the login banner and messages of the day (MOTD) configured in the cluster and in specific SVMs.
func GetSecurityLoginMessageByBannerMotd(errorHandler *utils.ErrorHandler, r restclient.RestClient, banner string, message string, svmName string) (*SecurityLoginMessageGetDataModelONTAP, error) {
// GetSecurityLoginMessage to get security_login_message info
// Retrieves the login banner and messages of the day (MOTD) configured in the cluster and in a specific SVM.
func GetSecurityLoginMessage(errorHandler *utils.ErrorHandler, r restclient.RestClient, svmName string) (*SecurityLoginMessageGetDataModelONTAP, error) {
api := "security/login/messages"
query := r.NewQuery()
if message != "" {
query.Set("message", message)
}
if banner != "" {
query.Set("banner", banner)
}

if svmName == "" {
query.Set("scope", "cluster")
} else {
Expand All @@ -75,7 +64,7 @@ func GetSecurityLoginMessageByBannerMotd(errorHandler *utils.ErrorHandler, r res
return &dataONTAP, nil
}

// GetSecurityLoginMessages to get security_login_message info for all resources matching a filter
// GetSecurityLoginMessages to get security_login_messages info for all resources matching a filter
func GetSecurityLoginMessages(errorHandler *utils.ErrorHandler, r restclient.RestClient, filter *SecurityLoginMessageDataSourceFilterModel) ([]SecurityLoginMessageGetDataModelONTAP, error) {
api := "security/login/messages"
query := r.NewQuery()
Expand Down Expand Up @@ -108,34 +97,19 @@ func GetSecurityLoginMessages(errorHandler *utils.ErrorHandler, r restclient.Res
return dataONTAP, nil
}

// CreateSecurityLoginMessage to create security_login_message
func CreateSecurityLoginMessage(errorHandler *utils.ErrorHandler, r restclient.RestClient, body SecurityLoginMessageResourceBodyDataModelONTAP) (*SecurityLoginMessageGetDataModelONTAP, error) {
api := "api_url"
// UpdateSecurityLoginMessage to update security_login_messages
func UpdateSecurityLoginMessage(errorHandler *utils.ErrorHandler, r restclient.RestClient, uuid string, body SecurityLoginMessageResourceBodyDataModelONTAP) error {
api := "security/login/messages"
var bodyMap map[string]interface{}
if err := mapstructure.Decode(body, &bodyMap); err != nil {
return nil, errorHandler.MakeAndReportError("error encoding security_login_message body", fmt.Sprintf("error on encoding %s body: %s, body: %#v", api, err, body))
return errorHandler.MakeAndReportError("error encoding security_login_messages body", fmt.Sprintf("error on encoding %s body: %s, body: %#v", api, err, body))
}
tflog.Debug(errorHandler.Ctx, fmt.Sprintf("Update security login messages: %#v", body))
query := r.NewQuery()
query.Add("return_records", "true")
statusCode, response, err := r.CallCreateMethod(api, query, bodyMap)
if err != nil {
return nil, errorHandler.MakeAndReportError("error creating security_login_message", fmt.Sprintf("error on POST %s: %s, statusCode %d", api, err, statusCode))
}

var dataONTAP SecurityLoginMessageGetDataModelONTAP
if err := mapstructure.Decode(response.Records[0], &dataONTAP); err != nil {
return nil, errorHandler.MakeAndReportError("error decoding security_login_message info", fmt.Sprintf("error on decode storage/security_login_messages info: %s, statusCode %d, response %#v", err, statusCode, response))
}
tflog.Debug(errorHandler.Ctx, fmt.Sprintf("Create security_login_message source - udata: %#v", dataONTAP))
return &dataONTAP, nil
}

// DeleteSecurityLoginMessage to delete security_login_message
func DeleteSecurityLoginMessage(errorHandler *utils.ErrorHandler, r restclient.RestClient, uuid string) error {
api := "api_url"
statusCode, _, err := r.CallDeleteMethod(api+"/"+uuid, nil, nil)
statusCode, _, err := r.CallUpdateMethod(api+"/"+uuid, query, bodyMap)
if err != nil {
return errorHandler.MakeAndReportError("error deleting security_login_message", fmt.Sprintf("error on DELETE %s: %s, statusCode %d", api, err, statusCode))
return errorHandler.MakeAndReportError("error updating security_login_messages", fmt.Sprintf("error on PUT %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 @@ -174,6 +174,7 @@ func (p *ONTAPProvider) Resources(ctx context.Context) []func() resource.Resourc
protocols.NewProtocolsSanIgroupResource,
protocols.NewProtocolsSanLunMapsResource,
security.NewSecurityAccountResource,
security.NewSecurityLoginMessageResource,
snapmirror.NewSnapmirrorResource,
snapmirror.NewSnapmirrorPolicyResource,
storage.NewQOSPoliciesResource,
Expand Down
4 changes: 2 additions & 2 deletions internal/provider/security/security_account_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ var _ resource.ResourceWithImportState = &SecurityAccountResource{}
func NewSecurityAccountResource() resource.Resource {
return &SecurityAccountResource{
config: connection.ResourceOrDataSourceConfig{
Name: "security_account",
Name: "security_accounts",
},
}
}
Expand Down Expand Up @@ -82,7 +82,7 @@ func (r *SecurityAccountResource) Metadata(ctx context.Context, req resource.Met
func (r *SecurityAccountResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
// This description is used by the documentation generator and the language server.
MarkdownDescription: "SecurityAccount resource",
MarkdownDescription: "SecurityAccounts resource",

Attributes: map[string]schema.Attribute{
"cx_profile_name": schema.StringAttribute{
Expand Down
12 changes: 6 additions & 6 deletions internal/provider/security/security_account_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,24 @@ func TestAccSecurityAccountResource(t *testing.T) {
{
Config: testAccSecurityAccountResourceConfig("carchitest", "password"),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("netapp-ontap_security_account.security_account", "name", "carchitest"),
resource.TestCheckResourceAttr("netapp-ontap_security_accounts.security_account", "name", "carchitest"),
),
},
// Test updating a resource
{
Config: testAccSecurityAccountResourceConfig("carchitest", "password123"),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("netapp-ontap_security_account.security_account", "name", "carchitest"),
resource.TestCheckResourceAttr("netapp-ontap_security_account.security_account", "password", "password123"),
resource.TestCheckResourceAttr("netapp-ontap_security_accounts.security_account", "name", "carchitest"),
resource.TestCheckResourceAttr("netapp-ontap_security_accounts.security_account", "password", "password123"),
),
},
// Test importing a resource
{
ResourceName: "netapp-ontap_security_account.security_account",
ResourceName: "netapp-ontap_security_accounts.security_account",
ImportState: true,
ImportStateId: fmt.Sprintf("%s,%s", "acc_user", "cluster2"),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("netapp-ontap_security_account.security_account", "name", "acc_user"),
resource.TestCheckResourceAttr("netapp-ontap_security_accounts.security_account", "name", "acc_user"),
),
},
},
Expand Down Expand Up @@ -62,7 +62,7 @@ provider "netapp-ontap" {
]
}
resource "netapp-ontap_security_account" "security_account" {
resource "netapp-ontap_security_accounts" "security_account" {
# required to know which system to interface with
cx_profile_name = "cluster2"
name = "%s"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func (d *SecurityLoginMessageDataSource) Schema(ctx context.Context, req datasou
Required: true,
},
"message": schema.StringAttribute{
MarkdownDescription: "SecurityLoginMessage name",
MarkdownDescription: "SecurityLoginMessage message",
Optional: true,
Computed: true,
},
Expand Down Expand Up @@ -126,7 +126,7 @@ func (d *SecurityLoginMessageDataSource) Read(ctx context.Context, req datasourc
return
}

restInfo, err := interfaces.GetSecurityLoginMessageByBannerMotd(errorHandler, *client, data.Banner.ValueString(), data.Message.ValueString(), data.SVMName.ValueString())
restInfo, err := interfaces.GetSecurityLoginMessage(errorHandler, *client, data.SVMName.ValueString())
if err != nil {
// error reporting done inside GetSecurityLoginMessage
return
Expand Down
Loading

0 comments on commit 6027687

Please sign in to comment.