Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## [Unreleased]

- Fix secret handling `elasticstack_fleet_integration_policy` resource. ([#821](https://github.com/elastic/terraform-provider-elasticstack/pull/821))
- Add `description` attribute to `elasticstack_elasticsearch_security_role` resource. ([#824](https://github.com/elastic/terraform-provider-elasticstack/pull/824))
- Fix merge values for `elasticstack_kibana_synthetics_monitor` monitor locations ([#823](https://github.com/elastic/terraform-provider-elasticstack/pull/823)

## [0.11.8] - 2024-10-02
Expand Down
6 changes: 4 additions & 2 deletions docs/resources/elasticsearch_security_role.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ provider "elasticstack" {
}
resource "elasticstack_elasticsearch_security_role" "role" {
name = "testrole"
cluster = ["all"]
name = "testrole"
description = "Role for testing"
cluster = ["all"]
indices {
names = ["index1", "index2"]
Expand Down Expand Up @@ -55,6 +56,7 @@ output "role" {

- `applications` (Block Set) A list of application privilege entries. (see [below for nested schema](#nestedblock--applications))
- `cluster` (Set of String) A list of cluster privileges. These privileges define the cluster level actions that users with this role are able to execute.
- `description` (String) The description of the role.
- `elasticsearch_connection` (Block List, Max: 1, Deprecated) Elasticsearch connection configuration block. This property will be removed in a future provider version. Configure the Elasticsearch connection via the provider configuration instead. (see [below for nested schema](#nestedblock--elasticsearch_connection))
- `global` (String) An object defining global privileges.
- `indices` (Block Set) A list of indices permissions entries. (see [below for nested schema](#nestedblock--indices))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ provider "elasticstack" {
}

resource "elasticstack_elasticsearch_security_role" "role" {
name = "testrole"
cluster = ["all"]
name = "testrole"
description = "Role for testing"
cluster = ["all"]

indices {
names = ["index1", "index2"]
Expand Down
69 changes: 47 additions & 22 deletions internal/elasticsearch/security/role.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
)

var minSupportedRemoteIndicesVersion = version.Must(version.NewVersion("8.10.0"))
var minSupportedDescriptionVersion = version.Must(version.NewVersion("8.15.0"))

func ResourceRole() *schema.Resource {
roleSchema := map[string]*schema.Schema{
Expand All @@ -32,6 +33,11 @@ func ResourceRole() *schema.Resource {
Required: true,
ForceNew: true,
},
"description": {
Description: "The description of the role.",
Type: schema.TypeString,
Optional: true,
},
"applications": {
Description: "A list of application privilege entries.",
Type: schema.TypeSet,
Expand Down Expand Up @@ -258,6 +264,18 @@ func resourceSecurityRolePut(ctx context.Context, d *schema.ResourceData, meta i
}
var role models.Role
role.Name = roleId

// Add description to the role
if v, ok := d.GetOk("description"); ok {
// Return an error if the server version is less than the minimum supported version
if serverVersion.LessThan(minSupportedDescriptionVersion) {
return diag.FromErr(fmt.Errorf("'description' is supported only for Elasticsearch v%s and above", minSupportedDescriptionVersion.String()))
}

description := v.(string)
role.Description = &description
}

if v, ok := d.GetOk("applications"); ok {
definedApps := v.(*schema.Set)
applications := make([]models.Application, definedApps.Len())
Expand Down Expand Up @@ -364,37 +382,37 @@ func resourceSecurityRolePut(ctx context.Context, d *schema.ResourceData, meta i
if definedRemoteIndices.Len() > 0 && serverVersion.LessThan(minSupportedRemoteIndicesVersion) {
return diag.FromErr(fmt.Errorf("'remote_indices' is supported only for Elasticsearch v%s and above", minSupportedRemoteIndicesVersion.String()))
}
remote_indices := make([]models.RemoteIndexPerms, definedRemoteIndices.Len())
remoteIndices := make([]models.RemoteIndexPerms, definedRemoteIndices.Len())
for i, idx := range definedRemoteIndices.List() {
remote_index := idx.(map[string]interface{})
remoteIndex := idx.(map[string]interface{})

definedRemoteNames := remote_index["names"].(*schema.Set)
remote_names := make([]string, definedRemoteNames.Len())
definedRemoteNames := remoteIndex["names"].(*schema.Set)
remoteNames := make([]string, definedRemoteNames.Len())
for i, name := range definedRemoteNames.List() {
remote_names[i] = name.(string)
remoteNames[i] = name.(string)
}
definedRemoteClusters := remote_index["clusters"].(*schema.Set)
remote_clusters := make([]string, definedRemoteClusters.Len())
definedRemoteClusters := remoteIndex["clusters"].(*schema.Set)
remoteClusters := make([]string, definedRemoteClusters.Len())
for i, cluster := range definedRemoteClusters.List() {
remote_clusters[i] = cluster.(string)
remoteClusters[i] = cluster.(string)
}
definedRemotePrivs := remote_index["privileges"].(*schema.Set)
remote_privs := make([]string, definedRemotePrivs.Len())
definedRemotePrivs := remoteIndex["privileges"].(*schema.Set)
remotePrivs := make([]string, definedRemotePrivs.Len())
for i, pr := range definedRemotePrivs.List() {
remote_privs[i] = pr.(string)
remotePrivs[i] = pr.(string)
}

newRemoteIndex := models.RemoteIndexPerms{
Names: remote_names,
Clusters: remote_clusters,
Privileges: remote_privs,
Names: remoteNames,
Clusters: remoteClusters,
Privileges: remotePrivs,
}

if query := remote_index["query"].(string); query != "" {
if query := remoteIndex["query"].(string); query != "" {
newRemoteIndex.Query = &query
}
if fieldSec := remote_index["field_security"].([]interface{}); len(fieldSec) > 0 {
remote_fieldSecurity := models.FieldSecurity{}
if fieldSec := remoteIndex["field_security"].([]interface{}); len(fieldSec) > 0 {
remoteFieldSecurity := models.FieldSecurity{}
// there must be only 1 entry
definedRemoteFieldSec := fieldSec[0].(map[string]interface{})

Expand All @@ -404,22 +422,22 @@ func resourceSecurityRolePut(ctx context.Context, d *schema.ResourceData, meta i
for i, grant := range gr.List() {
grants[i] = grant.(string)
}
remote_fieldSecurity.Grant = grants
remoteFieldSecurity.Grant = grants
}
// except
if exp := definedRemoteFieldSec["except"].(*schema.Set); exp != nil {
excepts := make([]string, exp.Len())
for i, except := range exp.List() {
excepts[i] = except.(string)
}
remote_fieldSecurity.Except = excepts
remoteFieldSecurity.Except = excepts
}
newRemoteIndex.FieldSecurity = &remote_fieldSecurity
newRemoteIndex.FieldSecurity = &remoteFieldSecurity
}

remote_indices[i] = newRemoteIndex
remoteIndices[i] = newRemoteIndex
}
role.RemoteIndices = remote_indices
role.RemoteIndices = remoteIndices
}

if v, ok := d.GetOk("metadata"); ok {
Expand Down Expand Up @@ -473,6 +491,13 @@ func resourceSecurityRoleRead(ctx context.Context, d *schema.ResourceData, meta
return diag.FromErr(err)
}

// Set the description if it exists
if role.Description != nil {
if err := d.Set("description", *role.Description); err != nil {
return diag.FromErr(err)
}
}

apps := role.Applications
applications := flattenApplicationsData(&apps)
if err := d.Set("applications", applications); err != nil {
Expand Down
52 changes: 49 additions & 3 deletions internal/elasticsearch/security/role_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ import (
)

var minSupportedRemoteIndicesVersion = version.Must(version.NewSemver("8.10.0"))
var minSupportedDescriptionVersion = version.Must(version.NewVersion("8.15.0"))

func TestAccResourceSecurityRole(t *testing.T) {
// generate a random username
roleName := sdkacctest.RandStringFromCharSet(10, sdkacctest.CharSetAlphaNum)
roleNameRemoteIndices := sdkacctest.RandStringFromCharSet(10, sdkacctest.CharSetAlphaNum)
roleNameDescription := sdkacctest.RandStringFromCharSet(10, sdkacctest.CharSetAlphaNum)

resource.Test(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
Expand Down Expand Up @@ -81,6 +83,22 @@ func TestAccResourceSecurityRole(t *testing.T) {
resource.TestCheckTypeSetElemAttr("elasticstack_elasticsearch_security_role.test", "remote_indices.*.names.*", "sample2"),
),
},
{
SkipFunc: versionutils.CheckIfVersionIsUnsupported(minSupportedDescriptionVersion),
Config: testAccResourceSecurityRoleDescriptionCreate(roleNameDescription),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("elasticstack_elasticsearch_security_role.test", "name", roleNameDescription),
resource.TestCheckResourceAttr("elasticstack_elasticsearch_security_role.test", "description", "test description"),
),
},
{
SkipFunc: versionutils.CheckIfVersionIsUnsupported(minSupportedDescriptionVersion),
Config: testAccResourceSecurityRoleDescriptionUpdate(roleNameDescription),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("elasticstack_elasticsearch_security_role.test", "name", roleNameDescription),
resource.TestCheckResourceAttr("elasticstack_elasticsearch_security_role.test", "description", "updated test description"),
),
},
},
})
}
Expand All @@ -92,7 +110,8 @@ provider "elasticstack" {
}
resource "elasticstack_elasticsearch_security_role" "test" {
name = "%s"
name = "%s"
cluster = ["all"]
indices {
Expand Down Expand Up @@ -123,7 +142,8 @@ provider "elasticstack" {
}
resource "elasticstack_elasticsearch_security_role" "test" {
name = "%s"
name = "%s"
cluster = ["all"]
indices {
Expand Down Expand Up @@ -211,6 +231,32 @@ resource "elasticstack_elasticsearch_security_role" "test" {
`, roleName)
}

func testAccResourceSecurityRoleDescriptionCreate(roleName string) string {
return fmt.Sprintf(`
provider "elasticstack" {
elasticsearch {}
}
resource "elasticstack_elasticsearch_security_role" "test" {
name = "%s"
description = "test description"
}
`, roleName)
}

func testAccResourceSecurityRoleDescriptionUpdate(roleName string) string {
return fmt.Sprintf(`
provider "elasticstack" {
elasticsearch {}
}
resource "elasticstack_elasticsearch_security_role" "test" {
name = "%s"
description = "updated test description"
}
`, roleName)
}

func checkResourceSecurityRoleDestroy(s *terraform.State) error {
client, err := clients.NewAcceptanceTestingClient()
if err != nil {
Expand All @@ -234,7 +280,7 @@ func checkResourceSecurityRoleDestroy(s *terraform.State) error {
}

if res.StatusCode != 404 {
return fmt.Errorf("Role (%s) still exists", compId.ResourceId)
return fmt.Errorf("role (%s) still exists", compId.ResourceId)
}
}
return nil
Expand Down
1 change: 1 addition & 0 deletions internal/models/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ type UserPassword struct {

type Role struct {
Name string `json:"-"`
Description *string `json:"description,omitempty"`
Applications []Application `json:"applications,omitempty"`
Global map[string]interface{} `json:"global,omitempty"`
Cluster []string `json:"cluster,omitempty"`
Expand Down
Loading