Skip to content

Commit

Permalink
Merge pull request #26503 from hashicorp/f-opensearch-fgac
Browse files Browse the repository at this point in the history
opensearch/domain: Allow fine-grained access control to be enabled
  • Loading branch information
YakDriver authored Aug 29, 2022
2 parents 1b503fa + a382aab commit aedfd9a
Show file tree
Hide file tree
Showing 7 changed files with 260 additions and 129 deletions.
3 changes: 3 additions & 0 deletions .changelog/26503.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/aws_opensearch_domain: Add support for enabling fine-grained access control on existing domains with `advanced_security_options` `anonymous_auth_enabled`
```
4 changes: 2 additions & 2 deletions internal/service/elasticsearch/domain_data_source_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func TestAccElasticsearchDomainDataSource_basic(t *testing.T) {
resourceName := "aws_elasticsearch_domain.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t); testAccPreCheckIAMServiceLinkedRoleEs(t) },
PreCheck: func() { acctest.PreCheck(t); testAccPreCheckIAMServiceLinkedRole(t) },
ErrorCheck: acctest.ErrorCheck(t, elasticsearchservice.EndpointsID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
Steps: []resource.TestStep{
Expand Down Expand Up @@ -63,7 +63,7 @@ func TestAccElasticsearchDomainDataSource_advanced(t *testing.T) {
resourceName := "aws_elasticsearch_domain.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t); testAccPreCheckIAMServiceLinkedRoleEs(t) },
PreCheck: func() { acctest.PreCheck(t); testAccPreCheckIAMServiceLinkedRole(t) },
ErrorCheck: acctest.ErrorCheck(t, elasticsearchservice.EndpointsID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
Steps: []resource.TestStep{
Expand Down
106 changes: 38 additions & 68 deletions internal/service/elasticsearch/domain_test.go

Large diffs are not rendered by default.

21 changes: 15 additions & 6 deletions internal/service/opensearch/domain.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,14 @@ func ResourceDomain() *schema.Resource {

return !inPlaceEncryptionEnableVersion(d.Get("engine_version").(string))
}),
customdiff.ForceNewIf("advanced_security_options.0.enabled", func(_ context.Context, d *schema.ResourceDiff, meta interface{}) bool {
o, n := d.GetChange("advanced_security_options.0.enabled")
if o.(bool) && !n.(bool) {
return true
}

return false
}),
verify.SetTagsDiff,
),

Expand Down Expand Up @@ -107,10 +115,14 @@ func ResourceDomain() *schema.Resource {
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"anonymous_auth_enabled": {
Type: schema.TypeBool,
Optional: true,
Computed: true,
},
"enabled": {
Type: schema.TypeBool,
Required: true,
ForceNew: true,
},
"internal_user_database_enabled": {
Type: schema.TypeBool,
Expand Down Expand Up @@ -383,6 +395,7 @@ func ResourceDomain() *schema.Resource {
"iops": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"throughput": {
Type: schema.TypeInt,
Expand Down Expand Up @@ -805,13 +818,9 @@ func resourceDomainRead(d *schema.ResourceData, meta interface{}) error {
// DescribeDomainConfig, if enabled, else use
// values from resource; additionally, append MasterUserOptions
// from resource as they are not returned from the API
if ds.AdvancedSecurityOptions != nil {
if ds.AdvancedSecurityOptions != nil && aws.BoolValue(ds.AdvancedSecurityOptions.Enabled) {
advSecOpts := flattenAdvancedSecurityOptions(ds.AdvancedSecurityOptions)
if !aws.BoolValue(ds.AdvancedSecurityOptions.Enabled) {
advSecOpts[0]["internal_user_database_enabled"] = getUserDBEnabled(d)
}
advSecOpts[0]["master_user_options"] = getMasterUserOptions(d)

if err := d.Set("advanced_security_options", advSecOpts); err != nil {
return fmt.Errorf("error setting advanced_security_options: %w", err)
}
Expand Down
24 changes: 10 additions & 14 deletions internal/service/opensearch/domain_structure.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ func expandAdvancedSecurityOptions(m []interface{}) *opensearchservice.AdvancedS
config.Enabled = aws.Bool(advancedSecurityEnabled.(bool))

if advancedSecurityEnabled.(bool) {
if v, ok := group["anonymous_auth_enabled"].(bool); ok {
config.AnonymousAuthEnabled = aws.Bool(v)
}

if v, ok := group["internal_user_database_enabled"].(bool); ok {
config.InternalUserDatabaseEnabled = aws.Bool(v)
}
Expand Down Expand Up @@ -173,7 +177,12 @@ func flattenAdvancedSecurityOptions(advancedSecurityOptions *opensearchservice.A

m := map[string]interface{}{}
m["enabled"] = aws.BoolValue(advancedSecurityOptions.Enabled)
if aws.BoolValue(advancedSecurityOptions.Enabled) {

if aws.BoolValue(advancedSecurityOptions.Enabled) && advancedSecurityOptions.AnonymousAuthEnabled != nil {
m["anonymous_auth_enabled"] = aws.BoolValue(advancedSecurityOptions.AnonymousAuthEnabled)
}

if aws.BoolValue(advancedSecurityOptions.Enabled) && advancedSecurityOptions.InternalUserDatabaseEnabled != nil {
m["internal_user_database_enabled"] = aws.BoolValue(advancedSecurityOptions.InternalUserDatabaseEnabled)
}

Expand Down Expand Up @@ -279,19 +288,6 @@ func getMasterUserOptions(d *schema.ResourceData) []interface{} {
return []interface{}{}
}

func getUserDBEnabled(d *schema.ResourceData) bool {
if v, ok := d.GetOk("advanced_security_options"); ok {
options := v.([]interface{})
if len(options) > 0 && options[0] != nil {
m := options[0].(map[string]interface{})
if enabled, ok := m["internal_user_database_enabled"]; ok {
return enabled.(bool)
}
}
}
return false
}

func expandLogPublishingOptions(m *schema.Set) map[string]*opensearchservice.LogPublishingOption {
options := make(map[string]*opensearchservice.LogPublishingOption)

Expand Down
138 changes: 101 additions & 37 deletions internal/service/opensearch/domain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@ package opensearch_test
import (
"fmt"
"regexp"
"strings"
"testing"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/cognitoidentityprovider"
"github.com/aws/aws-sdk-go/service/elb"
"github.com/aws/aws-sdk-go/service/iam"
"github.com/aws/aws-sdk-go/service/opensearchservice"
sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
Expand Down Expand Up @@ -715,7 +713,51 @@ func TestAccOpenSearchDomain_AdvancedSecurityOptions_userDB(t *testing.T) {
Config: testAccDomainConfig_advancedSecurityOptionsUserDB(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckDomainExists(resourceName, &domain),
testAccCheckAdvancedSecurityOptions(true, true, &domain),
testAccCheckAdvancedSecurityOptions(true, true, false, &domain),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateId: rName,
ImportStateVerify: true,
// MasterUserOptions are not returned from DescribeDomainConfig
ImportStateVerifyIgnore: []string{
"advanced_security_options.0.internal_user_database_enabled",
"advanced_security_options.0.master_user_options",
},
},
},
})
}

func TestAccOpenSearchDomain_AdvancedSecurityOptions_anonymousAuth(t *testing.T) {
if testing.Short() {
t.Skip("skipping long-running test in short mode")
}

var domain opensearchservice.DomainStatus
rName := testAccRandomDomainName()
resourceName := "aws_opensearch_domain.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t); testAccPreCheckIAMServiceLinkedRole(t) },
ErrorCheck: acctest.ErrorCheck(t, opensearchservice.EndpointsID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckDomainDestroy,
Steps: []resource.TestStep{
{
Config: testAccDomainConfig_advancedSecurityOptionsAnonymousAuth(rName, false),
Check: resource.ComposeTestCheckFunc(
testAccCheckDomainExists(resourceName, &domain),
testAccCheckAdvancedSecurityOptions(false, true, true, &domain),
),
},
{
Config: testAccDomainConfig_advancedSecurityOptionsAnonymousAuth(rName, true),
Check: resource.ComposeTestCheckFunc(
testAccCheckDomainExists(resourceName, &domain),
testAccCheckAdvancedSecurityOptions(true, true, true, &domain),
),
},
{
Expand Down Expand Up @@ -752,7 +794,7 @@ func TestAccOpenSearchDomain_AdvancedSecurityOptions_iam(t *testing.T) {
Config: testAccDomainConfig_advancedSecurityOptionsIAM(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckDomainExists(resourceName, &domain),
testAccCheckAdvancedSecurityOptions(true, false, &domain),
testAccCheckAdvancedSecurityOptions(true, false, false, &domain),
),
},
{
Expand Down Expand Up @@ -789,7 +831,7 @@ func TestAccOpenSearchDomain_AdvancedSecurityOptions_disabled(t *testing.T) {
Config: testAccDomainConfig_advancedSecurityOptionsDisabled(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckDomainExists(resourceName, &domain),
testAccCheckAdvancedSecurityOptions(false, false, &domain),
testAccCheckAdvancedSecurityOptions(false, false, false, &domain),
),
},
{
Expand All @@ -799,8 +841,7 @@ func TestAccOpenSearchDomain_AdvancedSecurityOptions_disabled(t *testing.T) {
ImportStateVerify: true,
// MasterUserOptions are not returned from DescribeDomainConfig
ImportStateVerifyIgnore: []string{
"advanced_security_options.0.internal_user_database_enabled",
"advanced_security_options.0.master_user_options",
"advanced_security_options",
},
},
},
Expand Down Expand Up @@ -1684,7 +1725,7 @@ func testAccCheckNodeToNodeEncrypted(encrypted bool, status *opensearchservice.D
}
}

func testAccCheckAdvancedSecurityOptions(enabled bool, userDbEnabled bool, status *opensearchservice.DomainStatus) resource.TestCheckFunc {
func testAccCheckAdvancedSecurityOptions(enabled bool, userDbEnabled bool, anonymousAuthEnabled bool, status *opensearchservice.DomainStatus) resource.TestCheckFunc {
return func(s *terraform.State) error {
conf := status.AdvancedSecurityOptions

Expand All @@ -1706,6 +1747,16 @@ func testAccCheckAdvancedSecurityOptions(enabled bool, userDbEnabled bool, statu
}
}

if aws.BoolValue(conf.Enabled) {
if aws.BoolValue(conf.AnonymousAuthEnabled) != anonymousAuthEnabled {
return fmt.Errorf(
"AdvancedSecurityOptions.AnonymousAuthEnabled not set properly. Given: %t, Expected: %t",
aws.BoolValue(conf.AnonymousAuthEnabled),
anonymousAuthEnabled,
)
}
}

return nil
}
}
Expand Down Expand Up @@ -1815,35 +1866,7 @@ func testAccGetValidStartAtTime(t *testing.T, timeUntilStart string) string {
}

func testAccPreCheckIAMServiceLinkedRole(t *testing.T) {
conn := acctest.Provider.Meta().(*conns.AWSClient).IAMConn
dnsSuffix := acctest.Provider.Meta().(*conns.AWSClient).DNSSuffix

input := &iam.ListRolesInput{
PathPrefix: aws.String("/aws-service-role/opensearchservice."),
}

var role *iam.Role
err := conn.ListRolesPages(input, func(page *iam.ListRolesOutput, lastPage bool) bool {
for _, r := range page.Roles {
if strings.HasPrefix(aws.StringValue(r.Path), "/aws-service-role/opensearchservice.") {
role = r
}
}

return !lastPage
})

if acctest.PreCheckSkipError(err) {
t.Skipf("skipping acceptance testing: %s", err)
}

if err != nil {
t.Fatalf("unexpected PreCheck error: %s", err)
}

if role == nil {
t.Fatalf("missing IAM Service Linked Role (opensearchservice.%s), please create it in the AWS account and retry", dnsSuffix)
}
acctest.PreCheckIAMServiceLinkedRole(t, "/aws-service-role/opensearchservice")
}

func testAccPreCheckCognitoIdentityProvider(t *testing.T) {
Expand Down Expand Up @@ -2887,6 +2910,47 @@ resource "aws_opensearch_domain" "test" {
`, rName)
}

func testAccDomainConfig_advancedSecurityOptionsAnonymousAuth(rName string, enabled bool) string {
return fmt.Sprintf(`
resource "aws_opensearch_domain" "test" {
domain_name = %[1]q
engine_version = "Elasticsearch_7.1"
cluster_config {
instance_type = "r5.large.search"
}
advanced_security_options {
enabled = %[2]t
anonymous_auth_enabled = true
internal_user_database_enabled = true
master_user_options {
master_user_name = "testmasteruser"
master_user_password = "Barbarbarbar1!"
}
}
encrypt_at_rest {
enabled = true
}
domain_endpoint_options {
enforce_https = true
tls_security_policy = "Policy-Min-TLS-1-2-2019-07"
}
node_to_node_encryption {
enabled = true
}
ebs_options {
ebs_enabled = true
volume_size = 10
}
}
`, rName, enabled)
}

func testAccDomainConfig_advancedSecurityOptionsIAM(rName string) string {
return fmt.Sprintf(`
resource "aws_iam_user" "test" {
Expand Down
Loading

0 comments on commit aedfd9a

Please sign in to comment.