diff --git a/CHANGELOG.md b/CHANGELOG.md
index d3d06f18..2d11cd92 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,8 @@ FEATURES:
* **New Data Source:** `netapp-ontap_volumes_files` ([#8](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/8))
* **New Data Source:** `netapp-ontap_quota_rules` ([#135](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/135))
* **New Data Source:** `netapp-ontap_quota_rule` ([#135](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/135))
+* **New Data Source:** `netapp-ontap_security_role` ([#139](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/139))
+* **New Data Source:** `netapp-ontap_security_roles` ([#139](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/139))
* **New Data Source:** `netapp-ontap_security_login_message` ([#17](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/17))
* **New Data Source:** `netapp-ontap_security_login_messages` ([#17](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/17))
* **New Resource:** `netapp-ontap_volume_efficiency_policies` ([#80](https://github.com/NetApp/terraform-provider-netapp-ontap/issues/80))
diff --git a/docs/data-sources/security_role.md b/docs/data-sources/security_role.md
new file mode 100644
index 00000000..a2977ca0
--- /dev/null
+++ b/docs/data-sources/security_role.md
@@ -0,0 +1,44 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "netapp-ontap_security_role Data Source - terraform-provider-netapp-ontap"
+subcategory: "Security"
+description: |-
+ Retrieves a Security role
+---
+
+# netapp-ontap_security_role (Data Source)
+
+SecurityRole data source
+
+## Example Usage
+```terraform
+data "netapp-ontap_security_role" "security_role" {
+ # required to know which system to interface with
+ cx_profile_name = "cluster4"
+ name = "vsadmin"
+ svm_name = "acc_test"
+}
+```
+
+
+## Schema
+
+### Required
+
+- `cx_profile_name` (String) Connection profile name
+- `name` (String) SecurityRole name
+- `svm_name` (String) IPInterface svm name
+
+### Optional
+
+- `builtin` (Boolean) Indicates if this is a built-in (pre-defined) role which cannot be modified or deleted.
+- `privileges` (Attributes Set) The list of privileges that this role has been granted. (see [below for nested schema](#nestedatt--privileges))
+- `scope` (String) Scope of the entity. Set to 'cluster' for cluster owned objects and to 'svm' for SVM owned objects.
+
+
+### Nested Schema for `privileges`
+
+Optional:
+
+- `access` (String) Access level for the REST endpoint or command/command directory path. If it denotes the access level for a command/command directory path, the only supported enum values are 'none','readonly' and 'all'.
+- `path` (String) Either of REST URI/endpoint OR command/command directory path.
diff --git a/docs/data-sources/security_roles.md b/docs/data-sources/security_roles.md
new file mode 100644
index 00000000..fbe44984
--- /dev/null
+++ b/docs/data-sources/security_roles.md
@@ -0,0 +1,69 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "netapp-ontap_security_roles Data Source - terraform-provider-netapp-ontap"
+subcategory: "Security"
+description: |-
+ Retrieves Security Rules
+---
+
+# netapp-ontap_security_roles (Data Source)
+
+SecurityRules data source
+
+## Example Usage
+data "netapp-ontap_security_roles" "security_roles" {
+ cx_profile_name = "cluster4"
+ filter = {
+ svm_name = "svm_1"
+ scope = "svm"
+ }
+}
+
+
+
+## Schema
+
+### Required
+
+- `cx_profile_name` (String) Connection profile name
+
+### Optional
+
+- `filter` (Attributes) (see [below for nested schema](#nestedatt--filter))
+
+### Read-Only
+
+- `security_roles` (Attributes List) (see [below for nested schema](#nestedatt--security_roles))
+
+
+### Nested Schema for `filter`
+
+Optional:
+
+- `name` (String) SecurityRule name
+- `scope` (String) Scope of the entity. Set to 'cluster' for cluster owned objects and to 'svm' for SVM owned objects.
+- `svm_name` (String) SecurityRule svm name
+
+
+
+### Nested Schema for `security_roles`
+
+Required:
+
+- `cx_profile_name` (String) Connection profile name
+- `name` (String) SecurityRule name
+- `svm_name` (String) IPInterface svm name
+
+Optional:
+
+- `builtin` (Boolean) Indicates if this is a built-in (pre-defined) role which cannot be modified or deleted.
+- `privileges` (Attributes Set) The list of privileges that this role has been granted. (see [below for nested schema](#nestedatt--security_roles--privileges))
+- `scope` (String) Scope of the entity. Set to 'cluster' for cluster owned objects and to 'svm' for SVM owned objects.
+
+
+### Nested Schema for `security_roles.privileges`
+
+Optional:
+
+- `access` (String) Access level for the REST endpoint or command/command directory path. If it denotes the access level for a command/command directory path, the only supported enum values are 'none','readonly' and 'all'.
+- `path` (String) Either of REST URI/endpoint OR command/command directory path.
diff --git a/examples/data-sources/netapp-ontap_security_role/data-source.tf b/examples/data-sources/netapp-ontap_security_role/data-source.tf
new file mode 100644
index 00000000..439b7ce9
--- /dev/null
+++ b/examples/data-sources/netapp-ontap_security_role/data-source.tf
@@ -0,0 +1,6 @@
+data "netapp-ontap_security_role" "security_role" {
+ # required to know which system to interface with
+ cx_profile_name = "cluster4"
+ name = "vsadmin"
+ svm_name = "acc_test"
+}
diff --git a/examples/data-sources/netapp-ontap_security_role/provider.tf b/examples/data-sources/netapp-ontap_security_role/provider.tf
new file mode 120000
index 00000000..c6b7138f
--- /dev/null
+++ b/examples/data-sources/netapp-ontap_security_role/provider.tf
@@ -0,0 +1 @@
+../../provider/provider.tf
\ No newline at end of file
diff --git a/examples/data-sources/netapp-ontap_security_role/terraform.tfvars b/examples/data-sources/netapp-ontap_security_role/terraform.tfvars
new file mode 120000
index 00000000..8d9d1c96
--- /dev/null
+++ b/examples/data-sources/netapp-ontap_security_role/terraform.tfvars
@@ -0,0 +1 @@
+../../provider/terraform.tfvars
\ No newline at end of file
diff --git a/examples/data-sources/netapp-ontap_security_role/variables.tf b/examples/data-sources/netapp-ontap_security_role/variables.tf
new file mode 120000
index 00000000..395ce618
--- /dev/null
+++ b/examples/data-sources/netapp-ontap_security_role/variables.tf
@@ -0,0 +1 @@
+../../provider/variables.tf
\ No newline at end of file
diff --git a/examples/data-sources/netapp-ontap_security_roles/data-source.tf b/examples/data-sources/netapp-ontap_security_roles/data-source.tf
new file mode 100644
index 00000000..c739bcbe
--- /dev/null
+++ b/examples/data-sources/netapp-ontap_security_roles/data-source.tf
@@ -0,0 +1,8 @@
+data "netapp-ontap_security_roles" "security_roles" {
+ # required to know which system to interface with
+ cx_profile_name = "cluster4"
+ filter = {
+ svm_name = "acc_test"
+ scope = "cluster"
+ }
+}
diff --git a/examples/data-sources/netapp-ontap_security_roles/provider.tf b/examples/data-sources/netapp-ontap_security_roles/provider.tf
new file mode 120000
index 00000000..c6b7138f
--- /dev/null
+++ b/examples/data-sources/netapp-ontap_security_roles/provider.tf
@@ -0,0 +1 @@
+../../provider/provider.tf
\ No newline at end of file
diff --git a/examples/data-sources/netapp-ontap_security_roles/terraform.tfvars b/examples/data-sources/netapp-ontap_security_roles/terraform.tfvars
new file mode 120000
index 00000000..8d9d1c96
--- /dev/null
+++ b/examples/data-sources/netapp-ontap_security_roles/terraform.tfvars
@@ -0,0 +1 @@
+../../provider/terraform.tfvars
\ No newline at end of file
diff --git a/examples/data-sources/netapp-ontap_security_roles/variables.tf b/examples/data-sources/netapp-ontap_security_roles/variables.tf
new file mode 120000
index 00000000..395ce618
--- /dev/null
+++ b/examples/data-sources/netapp-ontap_security_roles/variables.tf
@@ -0,0 +1 @@
+../../provider/variables.tf
\ No newline at end of file
diff --git a/internal/interfaces/security_role.go b/internal/interfaces/security_role.go
new file mode 100644
index 00000000..68c40f2c
--- /dev/null
+++ b/internal/interfaces/security_role.go
@@ -0,0 +1,131 @@
+package interfaces
+
+import (
+ "fmt"
+
+ "github.com/hashicorp/terraform-plugin-log/tflog"
+ "github.com/mitchellh/mapstructure"
+ "github.com/netapp/terraform-provider-netapp-ontap/internal/restclient"
+ "github.com/netapp/terraform-provider-netapp-ontap/internal/utils"
+)
+
+// SecurityRoleGetDataModelONTAP describes the GET record data model using go types for mapping.
+type SecurityRoleGetDataModelONTAP struct {
+ Name string `mapstructure:"name"`
+ UUID string `mapstructure:"uuid"`
+ Owner SecurityRoleOwner `mapstructure:"owner"`
+ Privileges []SecurityRolePrivileges `mapstructure:"privileges"`
+ Scope string `mapstructure:"scope"`
+ Builtin bool `mapstructure:"builtin"`
+}
+
+type SecurityRolePrivileges struct {
+ Access string `mapstructure:"access"`
+ Path string `mapstructure:"path"`
+}
+
+type SecurityRoleOwner struct {
+ Name string `mapstructure:"name"`
+ Id string `mapstructure:"uuid"`
+}
+
+// SecurityRoleResourceBodyDataModelONTAP describes the body data model using go types for mapping.
+type SecurityRoleResourceBodyDataModelONTAP struct {
+ Name string `mapstructure:"name"`
+ SVM svm `mapstructure:"svm"`
+}
+
+// SecurityRoleDataSourceFilterModel describes the data source data model for queries.
+type SecurityRoleDataSourceFilterModel struct {
+ Name string `mapstructure:"name"`
+ SVMName string `mapstructure:"owner.name"`
+ Scope string `mapstructure:"scope"`
+}
+
+// GetSecurityRoleByName to get security_role info
+func GetSecurityRoleByName(errorHandler *utils.ErrorHandler, r restclient.RestClient, name string, svmUUID string) (*SecurityRoleGetDataModelONTAP, error) {
+ api := "security/roles/" + svmUUID + "/" + name
+ query := r.NewQuery()
+ query.Set("name", name)
+ query.Fields([]string{"name", "scope", "owner", "privileges", "builtin"})
+ statusCode, response, err := r.GetNilOrOneRecord(api, query, nil)
+ if err == nil && response == nil {
+ err = fmt.Errorf("no response for GET %s", api)
+ }
+ if err != nil {
+ return nil, errorHandler.MakeAndReportError("error reading security_role info", fmt.Sprintf("error on GET %s: %s, statusCode %d", api, err, statusCode))
+ }
+
+ var dataONTAP SecurityRoleGetDataModelONTAP
+ if err := mapstructure.Decode(response, &dataONTAP); err != nil {
+ return nil, errorHandler.MakeAndReportError(fmt.Sprintf("failed to decode response from GET %s", api),
+ fmt.Sprintf("error: %s, statusCode %d, response %#v", err, statusCode, response))
+ }
+ tflog.Debug(errorHandler.Ctx, fmt.Sprintf("Read security_role data source: %#v", dataONTAP))
+ return &dataONTAP, nil
+}
+
+// GetSecurityRoles to get security_role info for all resources matching a filter
+func GetSecurityRoles(errorHandler *utils.ErrorHandler, r restclient.RestClient, filter *SecurityRoleDataSourceFilterModel) ([]SecurityRoleGetDataModelONTAP, error) {
+ api := "security/roles"
+ query := r.NewQuery()
+ query.Fields([]string{"name", "scope", "owner", "privileges", "builtin"})
+ if filter != nil {
+ var filterMap map[string]interface{}
+ if err := mapstructure.Decode(filter, &filterMap); err != nil {
+ return nil, errorHandler.MakeAndReportError("error encoding security_roles filter info", fmt.Sprintf("error on filter %#v: %s", filter, err))
+ }
+ query.SetValues(filterMap)
+ }
+ statusCode, response, err := r.GetZeroOrMoreRecords(api, query, nil)
+ if err == nil && response == nil {
+ err = fmt.Errorf("no response for GET %s", api)
+ }
+ if err != nil {
+ return nil, errorHandler.MakeAndReportError("error reading security_roles info", fmt.Sprintf("error on GET %s: %s, statusCode %d", api, err, statusCode))
+ }
+
+ var dataONTAP []SecurityRoleGetDataModelONTAP
+ for _, info := range response {
+ var record SecurityRoleGetDataModelONTAP
+ if err := mapstructure.Decode(info, &record); err != nil {
+ return nil, errorHandler.MakeAndReportError(fmt.Sprintf("failed to decode response from GET %s", api),
+ fmt.Sprintf("error: %s, statusCode %d, info %#v", err, statusCode, info))
+ }
+ dataONTAP = append(dataONTAP, record)
+ }
+ tflog.Debug(errorHandler.Ctx, fmt.Sprintf("Read security_roles data source: %#v", dataONTAP))
+ return dataONTAP, nil
+}
+
+// CreateSecurityRole to create security_role
+func CreateSecurityRole(errorHandler *utils.ErrorHandler, r restclient.RestClient, body SecurityRoleResourceBodyDataModelONTAP) (*SecurityRoleGetDataModelONTAP, error) {
+ api := "api_url"
+ var bodyMap map[string]interface{}
+ if err := mapstructure.Decode(body, &bodyMap); err != nil {
+ return nil, errorHandler.MakeAndReportError("error encoding security_role 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("error creating security_role", fmt.Sprintf("error on POST %s: %s, statusCode %d", api, err, statusCode))
+ }
+
+ var dataONTAP SecurityRoleGetDataModelONTAP
+ if err := mapstructure.Decode(response.Records[0], &dataONTAP); err != nil {
+ return nil, errorHandler.MakeAndReportError("error decoding security_role info", fmt.Sprintf("error on decode storage/security_roles info: %s, statusCode %d, response %#v", err, statusCode, response))
+ }
+ tflog.Debug(errorHandler.Ctx, fmt.Sprintf("Create security_role source - udata: %#v", dataONTAP))
+ return &dataONTAP, nil
+}
+
+// DeleteSecurityRole to delete security_role
+func DeleteSecurityRole(errorHandler *utils.ErrorHandler, r restclient.RestClient, uuid string) error {
+ api := "api_url"
+ statusCode, _, err := r.CallDeleteMethod(api+"/"+uuid, nil, nil)
+ if err != nil {
+ return errorHandler.MakeAndReportError("error deleting security_role", fmt.Sprintf("error on DELETE %s: %s, statusCode %d", api, err, statusCode))
+ }
+ return nil
+}
diff --git a/internal/provider/provider.go b/internal/provider/provider.go
index 34711790..39c26cf1 100644
--- a/internal/provider/provider.go
+++ b/internal/provider/provider.go
@@ -232,6 +232,8 @@ func (p *ONTAPProvider) DataSources(ctx context.Context) []func() datasource.Dat
protocols.NewProtocolsSanLunMapsDataSource,
security.NewSecurityAccountDataSource,
security.NewSecurityAccountsDataSource,
+ security.NewSecurityRoleDataSource,
+ security.NewSecurityRolesDataSource,
security.NewSecurityLoginMessageDataSource,
security.NewSecurityLoginMessagesDataSource,
snapmirror.NewSnapmirrorDataSource,
diff --git a/internal/provider/security/security_role_data_source.go b/internal/provider/security/security_role_data_source.go
new file mode 100644
index 00000000..7f19b625
--- /dev/null
+++ b/internal/provider/security/security_role_data_source.go
@@ -0,0 +1,199 @@
+package security
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/netapp/terraform-provider-netapp-ontap/internal/provider/connection"
+
+ "github.com/hashicorp/terraform-plugin-framework/attr"
+ "github.com/hashicorp/terraform-plugin-framework/datasource"
+ "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/hashicorp/terraform-plugin-log/tflog"
+ "github.com/netapp/terraform-provider-netapp-ontap/internal/interfaces"
+ "github.com/netapp/terraform-provider-netapp-ontap/internal/utils"
+)
+
+// Ensure provider defined types fully satisfy framework interfaces
+var _ datasource.DataSource = &SecurityRoleDataSource{}
+
+// NewSecurityRoleDataSource is a helper function to simplify the provider implementation.
+func NewSecurityRoleDataSource() datasource.DataSource {
+ return &SecurityRoleDataSource{
+ config: connection.ResourceOrDataSourceConfig{
+ Name: "security_role",
+ },
+ }
+}
+
+// SecurityRoleDataSource defines the data source implementation.
+type SecurityRoleDataSource struct {
+ config connection.ResourceOrDataSourceConfig
+}
+
+// SecurityRoleDataSourceModel describes the data source data model.
+type SecurityRoleDataSourceModel struct {
+ CxProfileName types.String `tfsdk:"cx_profile_name"`
+ Name types.String `tfsdk:"name"`
+ SVMName types.String `tfsdk:"svm_name"`
+ Privileges types.Set `tfsdk:"privileges"`
+ Builtin types.Bool `tfsdk:"builtin"`
+ Scope types.String `tfsdk:"scope"`
+}
+
+type SecurityRoleOwnerSourceModel struct {
+ Name types.Int64 `tfsdk:"name"`
+ Id types.String `tfsdk:"id"`
+}
+
+// Metadata returns the data source type name.
+func (d *SecurityRoleDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+ resp.TypeName = req.ProviderTypeName + "_" + d.config.Name
+}
+
+// Schema defines the schema for the data source.
+func (d *SecurityRoleDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
+ resp.Schema = schema.Schema{
+ // This description is used by the documentation generator and the language server.
+ MarkdownDescription: "SecurityRole data source",
+
+ Attributes: map[string]schema.Attribute{
+ "cx_profile_name": schema.StringAttribute{
+ MarkdownDescription: "Connection profile name",
+ Required: true,
+ },
+ "name": schema.StringAttribute{
+ MarkdownDescription: "SecurityRole name",
+ Required: true,
+ },
+ "svm_name": schema.StringAttribute{
+ MarkdownDescription: "IPInterface svm name",
+ Required: true,
+ },
+ "privileges": schema.SetNestedAttribute{
+ MarkdownDescription: "The list of privileges that this role has been granted.",
+ Computed: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "path": schema.StringAttribute{
+ MarkdownDescription: "Either of REST URI/endpoint OR command/command directory path.",
+ Computed: true,
+ },
+ "access": schema.StringAttribute{
+ MarkdownDescription: "Access level for the REST endpoint or command/command directory path. If it denotes the access level for a command/command directory path, the only supported enum values are 'none','readonly' and 'all'.",
+ Computed: true,
+ },
+ },
+ },
+ },
+ "builtin": schema.BoolAttribute{
+ MarkdownDescription: "Indicates if this is a built-in (pre-defined) role which cannot be modified or deleted.",
+ Computed: true,
+ },
+ "scope": schema.StringAttribute{
+ MarkdownDescription: "Scope of the entity. Set to 'cluster' for cluster owned objects and to 'svm' for SVM owned objects.",
+ Computed: true,
+ },
+ },
+ }
+}
+
+// Configure adds the provider configured client to the data source.
+func (d *SecurityRoleDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
+ // Prevent panic if the provider has not been configured.
+ if req.ProviderData == nil {
+ return
+ }
+ config, ok := req.ProviderData.(connection.Config)
+ if !ok {
+ resp.Diagnostics.AddError(
+ "Unexpected Data Source Configure Type",
+ fmt.Sprintf("Expected Config, got: %T. Please report this issue to the provider developers.", req.ProviderData),
+ )
+ }
+ d.config.ProviderConfig = config
+}
+
+// Read refreshes the Terraform state with the latest data.
+func (d *SecurityRoleDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
+ var data SecurityRoleDataSourceModel
+
+ // Read Terraform configuration data into the model
+ resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
+
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ errorHandler := utils.NewErrorHandler(ctx, &resp.Diagnostics)
+ // we need to defer setting the client until we can read the connection profile name
+ client, err := connection.GetRestClient(errorHandler, d.config, data.CxProfileName)
+ if err != nil {
+ // error reporting done inside NewClient
+ return
+ }
+
+ restInfos, err := interfaces.GetSecurityRoles(errorHandler, *client, &interfaces.SecurityRoleDataSourceFilterModel{
+ Name: data.Name.ValueString(),
+ })
+
+ if err != nil {
+ // error reporting done inside GetSecurityRoles
+ return
+ }
+ foundRole := false
+ restInfo := interfaces.SecurityRoleGetDataModelONTAP{}
+ for _, role := range restInfos {
+ if role.Name == data.Name.ValueString() {
+ foundRole = true
+ restInfo = role
+ break
+ }
+ }
+ if !foundRole {
+ resp.Diagnostics.AddError("SecurityRole not found", fmt.Sprintf("SecurityRole %s not found", data.Name.ValueString()))
+ return
+ }
+
+ data.Name = types.StringValue(restInfo.Name)
+ data.Builtin = types.BoolValue(restInfo.Builtin)
+ data.Scope = types.StringValue(restInfo.Scope)
+
+ // Priviledges
+ setElements := []attr.Value{}
+ for _, privilege := range restInfo.Privileges {
+ nestedElementTypes := map[string]attr.Type{
+ "access": types.StringType,
+ "path": types.StringType,
+ }
+ nestedElements := map[string]attr.Value{
+ "access": types.StringValue(privilege.Access),
+ "path": types.StringValue(privilege.Path),
+ }
+ objectValue, diags := types.ObjectValue(nestedElementTypes, nestedElements)
+ if diags.HasError() {
+ resp.Diagnostics.Append(diags...)
+ return
+ }
+ setElements = append(setElements, objectValue)
+ }
+ setValue, diags := types.SetValue(types.ObjectType{
+ AttrTypes: map[string]attr.Type{
+ "access": types.StringType,
+ "path": types.StringType,
+ },
+ }, setElements)
+ if diags.HasError() {
+ resp.Diagnostics.Append(diags...)
+ return
+ }
+ data.Privileges = setValue
+
+ // Write logs using the tflog package
+ // Documentation: https://terraform.io/plugin/log
+ tflog.Debug(ctx, fmt.Sprintf("read a data source: %#v", data))
+
+ // Save data into Terraform state
+ resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+}
diff --git a/internal/provider/security/security_roles_data_source.go b/internal/provider/security/security_roles_data_source.go
new file mode 100644
index 00000000..11e80e5d
--- /dev/null
+++ b/internal/provider/security/security_roles_data_source.go
@@ -0,0 +1,224 @@
+package security
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/netapp/terraform-provider-netapp-ontap/internal/provider/connection"
+
+ "github.com/hashicorp/terraform-plugin-framework/attr"
+ "github.com/hashicorp/terraform-plugin-framework/datasource"
+ "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/hashicorp/terraform-plugin-log/tflog"
+ "github.com/netapp/terraform-provider-netapp-ontap/internal/interfaces"
+ "github.com/netapp/terraform-provider-netapp-ontap/internal/utils"
+)
+
+// Ensure provider defined types fully satisfy framework interfaces
+var _ datasource.DataSource = &SecurityRolesDataSource{}
+
+// NewSecurityRolesDataSource is a helper function to simplify the provider implementation.
+func NewSecurityRolesDataSource() datasource.DataSource {
+ return &SecurityRolesDataSource{
+ config: connection.ResourceOrDataSourceConfig{
+ Name: "security_roles",
+ },
+ }
+}
+
+// SecurityRulesDataSource defines the data source implementation.
+type SecurityRolesDataSource struct {
+ config connection.ResourceOrDataSourceConfig
+}
+
+// SecurityRolesDataSourceModel describes the data source data model.
+type SecurityRolesDataSourceModel struct {
+ CxProfileName types.String `tfsdk:"cx_profile_name"`
+ SecurityRules []SecurityRoleDataSourceModel `tfsdk:"security_roles"`
+ Filter *SecurityRulesDataSourceFilterModel `tfsdk:"filter"`
+}
+
+// SecurityRulesDataSourceFilterModel describes the data source data model for queries.
+type SecurityRulesDataSourceFilterModel struct {
+ Name types.String `tfsdk:"name"`
+ SVMName types.String `tfsdk:"svm_name"`
+ Scope types.String `tfsdk:"scope"`
+}
+
+// Metadata returns the data source type name.
+func (d *SecurityRolesDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+ resp.TypeName = req.ProviderTypeName + "_" + d.config.Name
+}
+
+// Schema defines the schema for the data source.
+func (d *SecurityRolesDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
+ resp.Schema = schema.Schema{
+ // This description is used by the documentation generator and the language server.
+ MarkdownDescription: "SecurityRules data source",
+
+ Attributes: map[string]schema.Attribute{
+ "cx_profile_name": schema.StringAttribute{
+ MarkdownDescription: "Connection profile name",
+ Required: true,
+ },
+ "filter": schema.SingleNestedAttribute{
+ Attributes: map[string]schema.Attribute{
+ "name": schema.StringAttribute{
+ MarkdownDescription: "SecurityRule name",
+ Optional: true,
+ },
+ "svm_name": schema.StringAttribute{
+ MarkdownDescription: "SecurityRule svm name",
+ Optional: true,
+ },
+ "scope": schema.StringAttribute{
+ MarkdownDescription: "Scope of the entity. Set to 'cluster' for cluster owned objects and to 'svm' for SVM owned objects.",
+ Optional: true,
+ },
+ },
+ Optional: true,
+ },
+ "security_roles": schema.ListNestedAttribute{
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "cx_profile_name": schema.StringAttribute{
+ MarkdownDescription: "Connection profile name",
+ Required: true,
+ },
+ "name": schema.StringAttribute{
+ MarkdownDescription: "SecurityRule name",
+ Required: true,
+ },
+ "svm_name": schema.StringAttribute{
+ MarkdownDescription: "IPInterface svm name",
+ Required: true,
+ },
+ "privileges": schema.SetNestedAttribute{
+ MarkdownDescription: "The list of privileges that this role has been granted.",
+ Computed: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "path": schema.StringAttribute{
+ MarkdownDescription: "Either of REST URI/endpoint OR command/command directory path.",
+ Computed: true,
+ },
+ "access": schema.StringAttribute{
+ MarkdownDescription: "Access level for the REST endpoint or command/command directory path. If it denotes the access level for a command/command directory path, the only supported enum values are 'none','readonly' and 'all'.",
+ Computed: true,
+ },
+ },
+ },
+ },
+ "builtin": schema.BoolAttribute{
+ MarkdownDescription: "Indicates if this is a built-in (pre-defined) role which cannot be modified or deleted.",
+ Computed: true,
+ },
+ "scope": schema.StringAttribute{
+ MarkdownDescription: "Scope of the entity. Set to 'cluster' for cluster owned objects and to 'svm' for SVM owned objects.",
+ Computed: true,
+ },
+ },
+ },
+ Computed: true,
+ MarkdownDescription: "",
+ },
+ },
+ }
+}
+
+// Configure adds the provider configured client to the data source.
+func (d *SecurityRolesDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
+ // Prevent panic if the provider has not been configured.
+ if req.ProviderData == nil {
+ return
+ }
+ config, ok := req.ProviderData.(connection.Config)
+ if !ok {
+ resp.Diagnostics.AddError(
+ "Unexpected Data Source Configure Type",
+ fmt.Sprintf("Expected Config, got: %T. Please report this issue to the provider developers.", req.ProviderData),
+ )
+ }
+ d.config.ProviderConfig = config
+}
+
+// Read refreshes the Terraform state with the latest data.
+func (d *SecurityRolesDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
+ var data SecurityRolesDataSourceModel
+
+ // Read Terraform configuration data into the model
+ resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
+
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ errorHandler := utils.NewErrorHandler(ctx, &resp.Diagnostics)
+ // we need to defer setting the client until we can read the connection profile name
+ client, err := connection.GetRestClient(errorHandler, d.config, data.CxProfileName)
+ if err != nil {
+ // error reporting done inside NewClient
+ return
+ }
+
+ var filter *interfaces.SecurityRoleDataSourceFilterModel = nil
+ if data.Filter != nil {
+ filter = &interfaces.SecurityRoleDataSourceFilterModel{
+ Name: data.Filter.Name.ValueString(),
+ SVMName: data.Filter.SVMName.ValueString(),
+ Scope: data.Filter.Scope.ValueString(),
+ }
+ }
+ restInfo, err := interfaces.GetSecurityRoles(errorHandler, *client, filter)
+ if err != nil {
+ // error reporting done inside GetSecurityRoles
+ return
+ }
+
+ data.SecurityRules = make([]SecurityRoleDataSourceModel, len(restInfo))
+ for index, record := range restInfo {
+ data.SecurityRules[index] = SecurityRoleDataSourceModel{
+ CxProfileName: types.String(data.CxProfileName),
+ Name: types.StringValue(record.Name),
+ }
+ data.SecurityRules[index].Scope = types.StringValue(record.Scope)
+ data.SecurityRules[index].Builtin = types.BoolValue(record.Builtin)
+
+ setElements := []attr.Value{}
+ for _, privilege := range record.Privileges {
+ nestedElementTypes := map[string]attr.Type{
+ "access": types.StringType,
+ "path": types.StringType,
+ }
+ nestedElements := map[string]attr.Value{
+ "access": types.StringValue(privilege.Access),
+ "path": types.StringValue(privilege.Path),
+ }
+ objectValue, diags := types.ObjectValue(nestedElementTypes, nestedElements)
+ if diags.HasError() {
+ resp.Diagnostics.Append(diags...)
+ return
+ }
+ setElements = append(setElements, objectValue)
+ }
+ setValue, diags := types.SetValue(types.ObjectType{
+ AttrTypes: map[string]attr.Type{
+ "access": types.StringType,
+ "path": types.StringType,
+ },
+ }, setElements)
+ if diags.HasError() {
+ resp.Diagnostics.Append(diags...)
+ return
+ }
+ data.SecurityRules[index].Privileges = setValue
+ }
+
+ // Write logs using the tflog package
+ // Documentation: https://terraform.io/plugin/log
+ tflog.Debug(ctx, fmt.Sprintf("read a data source: %#v", data))
+
+ // Save data into Terraform state
+ resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
+}