diff --git a/.changelog/22988.txt b/.changelog/22988.txt new file mode 100644 index 00000000000..6fbf29acf39 --- /dev/null +++ b/.changelog/22988.txt @@ -0,0 +1,7 @@ +```release-note:new-resource +aws_opensearch_outbound_connection +``` + +```release-note:new-resource +aws_opensearch_inbound_connection_accepter +``` diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 0660eb3678a..e2eac1bcfb8 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -1818,9 +1818,11 @@ func New(_ context.Context) (*schema.Provider, error) { "aws_networkmanager_vpc_attachment": networkmanager.ResourceVPCAttachment(), "aws_networkmanager_site_to_site_vpn_attachment": networkmanager.ResourceSiteToSiteVPNAttachment(), - "aws_opensearch_domain": opensearch.ResourceDomain(), - "aws_opensearch_domain_policy": opensearch.ResourceDomainPolicy(), - "aws_opensearch_domain_saml_options": opensearch.ResourceDomainSAMLOptions(), + "aws_opensearch_domain": opensearch.ResourceDomain(), + "aws_opensearch_domain_policy": opensearch.ResourceDomainPolicy(), + "aws_opensearch_domain_saml_options": opensearch.ResourceDomainSAMLOptions(), + "aws_opensearch_outbound_connection": opensearch.ResourceOutboundConnection(), + "aws_opensearch_inbound_connection_accepter": opensearch.ResourceInboundConnectionAccepter(), "aws_opsworks_application": opsworks.ResourceApplication(), "aws_opsworks_custom_layer": opsworks.ResourceCustomLayer(), diff --git a/internal/service/opensearch/inbound_connection_accepter.go b/internal/service/opensearch/inbound_connection_accepter.go new file mode 100644 index 00000000000..c81f1431850 --- /dev/null +++ b/internal/service/opensearch/inbound_connection_accepter.go @@ -0,0 +1,184 @@ +package opensearch + +import ( + "context" + "fmt" + "log" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/opensearchservice" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-provider-aws/internal/conns" +) + +func ResourceInboundConnectionAccepter() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceInboundConnectionAccepterCreate, + ReadContext: resourceInboundConnectionRead, + DeleteContext: resourceInboundConnectionDelete, + Importer: &schema.ResourceImporter{ + State: func(d *schema.ResourceData, m interface{}) (result []*schema.ResourceData, err error) { + d.Set("connection_id", d.Id()) + + return []*schema.ResourceData{d}, nil + }, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(1 * time.Minute), + Delete: schema.DefaultTimeout(1 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "connection_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "connection_status": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceInboundConnectionAccepterCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).OpenSearchConn + + // Create the Inbound Connection + acceptOpts := &opensearchservice.AcceptInboundConnectionInput{ + ConnectionId: aws.String(d.Get("connection_id").(string)), + } + + log.Printf("[DEBUG] Inbound Connection Accept options: %#v", acceptOpts) + + resp, err := conn.AcceptInboundConnectionWithContext(ctx, acceptOpts) + if err != nil { + return diag.FromErr(fmt.Errorf("Error accepting Inbound Connection: %s", err)) + } + + // Get the ID and store it + d.SetId(aws.StringValue(resp.Connection.ConnectionId)) + log.Printf("[INFO] Inbound Connection ID: %s", d.Id()) + + err = inboundConnectionWaitUntilActive(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)) + if err != nil { + return diag.FromErr(fmt.Errorf("Error waiting for Inbound Connection to become active: %s", err)) + } + + return resourceInboundConnectionRead(ctx, d, meta) +} + +func resourceInboundConnectionRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).OpenSearchConn + + ccscRaw, statusCode, err := inboundConnectionRefreshState(ctx, conn, d.Id())() + + if err != nil { + return diag.FromErr(fmt.Errorf("Error reading Inbound Connection: %s", err)) + } + + ccsc := ccscRaw.(*opensearchservice.InboundConnection) + log.Printf("[DEBUG] Inbound Connection response: %#v", ccsc) + + d.Set("connection_id", ccsc.ConnectionId) + d.Set("connection_status", statusCode) + return nil +} + +func resourceInboundConnectionDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).OpenSearchConn + + req := &opensearchservice.DeleteInboundConnectionInput{ + ConnectionId: aws.String(d.Id()), + } + + _, err := conn.DeleteInboundConnectionWithContext(ctx, req) + + if tfawserr.ErrCodeEquals(err, "ResourceNotFoundException") { + return nil + } + + if err != nil { + return diag.FromErr(fmt.Errorf("Error deleting Inbound Connection (%s): %s", d.Id(), err)) + } + + if err := waitForInboundConnectionDeletion(ctx, conn, d.Id(), d.Timeout(schema.TimeoutDelete)); err != nil { + return diag.FromErr(fmt.Errorf("Error waiting for VPC Peering Connection (%s) to be deleted: %s", d.Id(), err)) + } + + return nil +} + +func inboundConnectionRefreshState(ctx context.Context, conn *opensearchservice.OpenSearchService, id string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + resp, err := conn.DescribeInboundConnectionsWithContext(ctx, &opensearchservice.DescribeInboundConnectionsInput{ + Filters: []*opensearchservice.Filter{ + { + Name: aws.String("connection-id"), + Values: []*string{aws.String(id)}, + }, + }, + }) + if err != nil { + return nil, "", err + } + + if resp == nil || resp.Connections == nil || + len(resp.Connections) == 0 || resp.Connections[0] == nil { + // Sometimes AWS just has consistency issues and doesn't see + // our connection yet. Return an empty state. + return nil, "", nil + } + ccsc := resp.Connections[0] + if ccsc.ConnectionStatus == nil { + // Sometimes AWS just has consistency issues and doesn't see + // our connection yet. Return an empty state. + return nil, "", nil + } + statusCode := aws.StringValue(ccsc.ConnectionStatus.StatusCode) + + return ccsc, statusCode, nil + } +} + +func inboundConnectionWaitUntilActive(ctx context.Context, conn *opensearchservice.OpenSearchService, id string, timeout time.Duration) error { + log.Printf("[DEBUG] Waiting for Inbound Connection (%s) to become available.", id) + stateConf := &resource.StateChangeConf{ + Pending: []string{ + opensearchservice.InboundConnectionStatusCodeProvisioning, + opensearchservice.InboundConnectionStatusCodeApproved, + }, + Target: []string{ + opensearchservice.InboundConnectionStatusCodeActive, + }, + Refresh: inboundConnectionRefreshState(ctx, conn, id), + Timeout: timeout, + } + if _, err := stateConf.WaitForState(); err != nil { + return fmt.Errorf("Error waiting for Inbound Connection (%s) to become available: %s", id, err) + } + return nil +} + +func waitForInboundConnectionDeletion(ctx context.Context, conn *opensearchservice.OpenSearchService, id string, timeout time.Duration) error { + stateConf := &resource.StateChangeConf{ + Pending: []string{ + opensearchservice.InboundConnectionStatusCodeDeleting, + }, + Target: []string{ + opensearchservice.InboundConnectionStatusCodeDeleted, + }, + Refresh: inboundConnectionRefreshState(ctx, conn, id), + Timeout: timeout, + } + + _, err := stateConf.WaitForState() + + return err +} diff --git a/internal/service/opensearch/inbound_connection_accepter_test.go b/internal/service/opensearch/inbound_connection_accepter_test.go new file mode 100644 index 00000000000..474150e8e13 --- /dev/null +++ b/internal/service/opensearch/inbound_connection_accepter_test.go @@ -0,0 +1,168 @@ +package opensearch_test + +import ( + "fmt" + "testing" + + "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" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + tfopensearch "github.com/hashicorp/terraform-provider-aws/internal/service/opensearch" +) + +func TestAccOpenSearchInboundConnectionAccepter_basic(t *testing.T) { + var domain opensearchservice.DomainStatus + ri := sdkacctest.RandString(10) + name := fmt.Sprintf("tf-test-%s", ri) + resourceName := "aws_opensearch_inbound_connection_accepter.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, opensearchservice.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckDomainDestroy, + Steps: []resource.TestStep{ + { + Config: testAccInboundConnectionAccepterConfig(name), + Check: resource.ComposeTestCheckFunc( + testAccCheckDomainExists("aws_opensearch_domain.domain_1", &domain), + testAccCheckDomainExists("aws_opensearch_domain.domain_2", &domain), + resource.TestCheckResourceAttr(resourceName, "connection_status", "ACTIVE"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccOpenSearchInboundConnectionAccepter_disappears(t *testing.T) { + var domain opensearchservice.DomainStatus + ri := sdkacctest.RandString(10) + name := fmt.Sprintf("tf-test-%s", ri) + resourceName := "aws_opensearch_inbound_connection_accepter.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, opensearchservice.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckDomainDestroy, + Steps: []resource.TestStep{ + { + Config: testAccInboundConnectionAccepterConfig(name), + Check: resource.ComposeTestCheckFunc( + testAccCheckDomainExists("aws_opensearch_domain.domain_1", &domain), + testAccCheckDomainExists("aws_opensearch_domain.domain_2", &domain), + acctest.CheckResourceDisappears(acctest.Provider, tfopensearch.ResourceInboundConnectionAccepter(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccInboundConnectionAccepterConfig(name string) string { + // Satisfy the pw requirements + pw := fmt.Sprintf("Aa1-%s", sdkacctest.RandString(10)) + return fmt.Sprintf(` +resource "aws_opensearch_domain" "domain_1" { + domain_name = "%s-1" + engine_version = "OpenSearch_1.1" + + cluster_config { + instance_type = "t3.small.search" # supported in both aws and aws-us-gov + } + + ebs_options { + ebs_enabled = true + volume_size = 10 + } + + node_to_node_encryption { + enabled = true + } + + advanced_security_options { + enabled = true + internal_user_database_enabled = true + + master_user_options { + master_user_name = "test" + master_user_password = "%s" + } + } + + encrypt_at_rest { + enabled = true + } + + domain_endpoint_options { + enforce_https = true + tls_security_policy = "Policy-Min-TLS-1-2-2019-07" + } +} + +resource "aws_opensearch_domain" "domain_2" { + domain_name = "%s-2" + engine_version = "OpenSearch_1.1" + + cluster_config { + instance_type = "t3.small.search" # supported in both aws and aws-us-gov + } + + ebs_options { + ebs_enabled = true + volume_size = 10 + } + + node_to_node_encryption { + enabled = true + } + + advanced_security_options { + enabled = true + internal_user_database_enabled = true + + master_user_options { + master_user_name = "test" + master_user_password = "%s" + } + } + + encrypt_at_rest { + enabled = true + } + + domain_endpoint_options { + enforce_https = true + tls_security_policy = "Policy-Min-TLS-1-2-2019-07" + } +} + +data "aws_caller_identity" "current" {} +data "aws_region" "current" {} + +resource "aws_opensearch_outbound_connection" "test" { + connection_alias = "%s" + local_domain_info { + owner_id = data.aws_caller_identity.current.account_id + region = data.aws_region.current.name + domain_name = aws_opensearch_domain.domain_1.domain_name + } + + remote_domain_info { + owner_id = data.aws_caller_identity.current.account_id + region = data.aws_region.current.name + domain_name = aws_opensearch_domain.domain_2.domain_name + } +} + +resource "aws_opensearch_inbound_connection_accepter" "test" { + connection_id = aws_opensearch_outbound_connection.test.id +} +`, name, pw, name, pw, name) +} diff --git a/internal/service/opensearch/outbound_connection.go b/internal/service/opensearch/outbound_connection.go new file mode 100644 index 00000000000..9d85ecb1cde --- /dev/null +++ b/internal/service/opensearch/outbound_connection.go @@ -0,0 +1,262 @@ +package opensearch + +import ( + "context" + "errors" + "fmt" + "log" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/opensearchservice" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-provider-aws/internal/conns" +) + +func ResourceOutboundConnection() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceOutboundConnectionCreate, + ReadContext: resourceOutboundConnectionRead, + DeleteContext: resourceOutboundConnectionDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(1 * time.Minute), + Delete: schema.DefaultTimeout(1 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "connection_alias": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "local_domain_info": outboundConnectionDomainInfoSchema(), + "remote_domain_info": outboundConnectionDomainInfoSchema(), + "connection_status": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceOutboundConnectionCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).OpenSearchConn + + // Create the Outbound Connection + createOpts := &opensearchservice.CreateOutboundConnectionInput{ + ConnectionAlias: aws.String(d.Get("connection_alias").(string)), + LocalDomainInfo: expandOutboundConnectionDomainInfo(d.Get("local_domain_info").([]interface{})), + RemoteDomainInfo: expandOutboundConnectionDomainInfo(d.Get("remote_domain_info").([]interface{})), + } + + log.Printf("[DEBUG] Outbound Connection Create options: %#v", createOpts) + + resp, err := conn.CreateOutboundConnectionWithContext(ctx, createOpts) + if err != nil { + return diag.FromErr(fmt.Errorf("Error creating Outbound Connection: %s", err)) + } + + // Get the ID and store it + d.SetId(aws.StringValue(resp.ConnectionId)) + log.Printf("[INFO] Outbound Connection ID: %s", d.Id()) + + err = outboundConnectionWaitUntilAvailable(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)) + if err != nil { + return diag.FromErr(fmt.Errorf("Error waiting for Outbound Connection to become available: %s", err)) + } + + return resourceOutboundConnectionRead(ctx, d, meta) +} + +func resourceOutboundConnectionRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).OpenSearchConn + + ccscRaw, statusCode, err := outboundConnectionRefreshState(ctx, conn, d.Id())() + + if err != nil { + return diag.FromErr(fmt.Errorf("Error reading Outbound Connection: %s", err)) + } + + ccsc := ccscRaw.(*opensearchservice.OutboundConnection) + log.Printf("[DEBUG] Outbound Connection response: %#v", ccsc) + + if !d.IsNewResource() && statusCode == opensearchservice.OutboundConnectionStatusCodeDeleted { + log.Printf("[INFO] Outbound Connection (%s) deleted, removing from state", d.Id()) + d.SetId("") + return nil + } + + d.Set("connection_alias", ccsc.ConnectionAlias) + d.Set("remote_domain_info", flattenOutboundConnectionDomainInfo(ccsc.RemoteDomainInfo)) + d.Set("local_domain_info", flattenOutboundConnectionDomainInfo(ccsc.LocalDomainInfo)) + d.Set("connection_status", statusCode) + + return nil +} + +func resourceOutboundConnectionDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).OpenSearchConn + + req := &opensearchservice.DeleteOutboundConnectionInput{ + ConnectionId: aws.String(d.Id()), + } + + _, err := conn.DeleteOutboundConnectionWithContext(ctx, req) + + if tfawserr.ErrCodeEquals(err, "ResourceNotFoundException") { + return nil + } + + if err != nil { + return diag.FromErr(fmt.Errorf("Error deleting Outbound Connection (%s): %s", d.Id(), err)) + } + + if err := waitForOutboundConnectionDeletion(ctx, conn, d.Id(), d.Timeout(schema.TimeoutDelete)); err != nil { + return diag.FromErr(fmt.Errorf("Error waiting for VPC Peering Connection (%s) to be deleted: %s", d.Id(), err)) + } + + return nil +} + +func outboundConnectionRefreshState(ctx context.Context, conn *opensearchservice.OpenSearchService, id string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + resp, err := conn.DescribeOutboundConnectionsWithContext(ctx, &opensearchservice.DescribeOutboundConnectionsInput{ + Filters: []*opensearchservice.Filter{ + { + Name: aws.String("connection-id"), + Values: []*string{aws.String(id)}, + }, + }, + }) + if err != nil { + return nil, "", err + } + + if resp == nil || resp.Connections == nil || + len(resp.Connections) == 0 || resp.Connections[0] == nil { + // Sometimes AWS just has consistency issues and doesn't see + // our connection yet. Return an empty state. + return nil, "", nil + } + ccsc := resp.Connections[0] + if ccsc.ConnectionStatus == nil { + // Sometimes AWS just has consistency issues and doesn't see + // our connection yet. Return an empty state. + return nil, "", nil + } + statusCode := aws.StringValue(ccsc.ConnectionStatus.StatusCode) + + // A Outbound Connection can exist in a failed state, + // thus we short circuit before the time out would occur. + if statusCode == opensearchservice.OutboundConnectionStatusCodeValidationFailed { + return nil, statusCode, errors.New(aws.StringValue(ccsc.ConnectionStatus.Message)) + } + + return ccsc, statusCode, nil + } +} + +func outboundConnectionWaitUntilAvailable(ctx context.Context, conn *opensearchservice.OpenSearchService, id string, timeout time.Duration) error { + log.Printf("[DEBUG] Waiting for Outbound Connection (%s) to become available.", id) + stateConf := &resource.StateChangeConf{ + Pending: []string{ + opensearchservice.OutboundConnectionStatusCodeValidating, + opensearchservice.OutboundConnectionStatusCodeProvisioning, + }, + Target: []string{ + opensearchservice.OutboundConnectionStatusCodePendingAcceptance, + opensearchservice.OutboundConnectionStatusCodeActive, + opensearchservice.OutboundConnectionStatusCodeApproved, + opensearchservice.OutboundConnectionStatusCodeRejected, + opensearchservice.OutboundConnectionStatusCodeValidationFailed, + }, + Refresh: outboundConnectionRefreshState(ctx, conn, id), + Timeout: timeout, + } + if _, err := stateConf.WaitForState(); err != nil { + return fmt.Errorf("Error waiting for Outbound Connection (%s) to become available: %s", id, err) + } + return nil +} + +func waitForOutboundConnectionDeletion(ctx context.Context, conn *opensearchservice.OpenSearchService, id string, timeout time.Duration) error { + stateConf := &resource.StateChangeConf{ + Pending: []string{ + opensearchservice.OutboundConnectionStatusCodeActive, + opensearchservice.OutboundConnectionStatusCodePendingAcceptance, + opensearchservice.OutboundConnectionStatusCodeDeleting, + opensearchservice.OutboundConnectionStatusCodeRejecting, + }, + Target: []string{ + opensearchservice.OutboundConnectionStatusCodeDeleted, + }, + Refresh: outboundConnectionRefreshState(ctx, conn, id), + Timeout: timeout, + } + + _, err := stateConf.WaitForState() + + return err +} + +func outboundConnectionDomainInfoSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Required: true, + ForceNew: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "owner_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "domain_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "region": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + }, + } +} + +func expandOutboundConnectionDomainInfo(vOptions []interface{}) *opensearchservice.DomainInformationContainer { + if len(vOptions) == 0 || vOptions[0] == nil { + return nil + } + + mOptions := vOptions[0].(map[string]interface{}) + + return &opensearchservice.DomainInformationContainer{ + AWSDomainInformation: &opensearchservice.AWSDomainInformation{ + DomainName: aws.String(mOptions["domain_name"].(string)), + OwnerId: aws.String(mOptions["owner_id"].(string)), + Region: aws.String(mOptions["region"].(string)), + }, + } +} + +func flattenOutboundConnectionDomainInfo(domainInfo *opensearchservice.DomainInformationContainer) []interface{} { + if domainInfo == nil || domainInfo.AWSDomainInformation == nil { + return nil + } + return []interface{}{map[string]interface{}{ + "owner_id": aws.StringValue(domainInfo.AWSDomainInformation.OwnerId), + "domain_name": aws.StringValue(domainInfo.AWSDomainInformation.DomainName), + "region": aws.StringValue(domainInfo.AWSDomainInformation.Region), + }} +} diff --git a/internal/service/opensearch/outbound_connection_test.go b/internal/service/opensearch/outbound_connection_test.go new file mode 100644 index 00000000000..9559f652e2b --- /dev/null +++ b/internal/service/opensearch/outbound_connection_test.go @@ -0,0 +1,164 @@ +package opensearch_test + +import ( + "fmt" + "testing" + + "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" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + tfopensearch "github.com/hashicorp/terraform-provider-aws/internal/service/opensearch" +) + +func TestAccOpenSearchOutboundConnection_basic(t *testing.T) { + var domain opensearchservice.DomainStatus + ri := sdkacctest.RandString(10) + name := fmt.Sprintf("tf-test-%s", ri) + resourceName := "aws_opensearch_outbound_connection.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, opensearchservice.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckDomainDestroy, + Steps: []resource.TestStep{ + { + Config: testAccOutboundConnectionConfig(name), + Check: resource.ComposeTestCheckFunc( + testAccCheckDomainExists("aws_opensearch_domain.domain_1", &domain), + testAccCheckDomainExists("aws_opensearch_domain.domain_2", &domain), + resource.TestCheckResourceAttr(resourceName, "connection_status", "PENDING_ACCEPTANCE"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccOpenSearchOutboundConnection_disappears(t *testing.T) { + var domain opensearchservice.DomainStatus + ri := sdkacctest.RandString(10) + name := fmt.Sprintf("tf-test-%s", ri) + resourceName := "aws_opensearch_outbound_connection.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, opensearchservice.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckDomainDestroy, + Steps: []resource.TestStep{ + { + Config: testAccOutboundConnectionConfig(name), + Check: resource.ComposeTestCheckFunc( + testAccCheckDomainExists("aws_opensearch_domain.domain_1", &domain), + testAccCheckDomainExists("aws_opensearch_domain.domain_2", &domain), + acctest.CheckResourceDisappears(acctest.Provider, tfopensearch.ResourceOutboundConnection(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccOutboundConnectionConfig(name string) string { + // Satisfy the pw requirements + pw := fmt.Sprintf("Aa1-%s", sdkacctest.RandString(10)) + return fmt.Sprintf(` +resource "aws_opensearch_domain" "domain_1" { + domain_name = "%s-1" + engine_version = "OpenSearch_1.1" + + cluster_config { + instance_type = "t3.small.search" # supported in both aws and aws-us-gov + } + + ebs_options { + ebs_enabled = true + volume_size = 10 + } + + node_to_node_encryption { + enabled = true + } + + advanced_security_options { + enabled = true + internal_user_database_enabled = true + + master_user_options { + master_user_name = "test" + master_user_password = "%s" + } + } + + encrypt_at_rest { + enabled = true + } + + domain_endpoint_options { + enforce_https = true + tls_security_policy = "Policy-Min-TLS-1-2-2019-07" + } +} + +resource "aws_opensearch_domain" "domain_2" { + domain_name = "%s-2" + engine_version = "OpenSearch_1.1" + + cluster_config { + instance_type = "t3.small.search" # supported in both aws and aws-us-gov + } + + ebs_options { + ebs_enabled = true + volume_size = 10 + } + + node_to_node_encryption { + enabled = true + } + + advanced_security_options { + enabled = true + internal_user_database_enabled = true + + master_user_options { + master_user_name = "test" + master_user_password = "%s" + } + } + + encrypt_at_rest { + enabled = true + } + + domain_endpoint_options { + enforce_https = true + tls_security_policy = "Policy-Min-TLS-1-2-2019-07" + } +} + +data "aws_caller_identity" "current" {} +data "aws_region" "current" {} + +resource "aws_opensearch_outbound_connection" "test" { + connection_alias = "%s" + local_domain_info { + owner_id = data.aws_caller_identity.current.account_id + region = data.aws_region.current.name + domain_name = aws_opensearch_domain.domain_1.domain_name + } + + remote_domain_info { + owner_id = data.aws_caller_identity.current.account_id + region = data.aws_region.current.name + domain_name = aws_opensearch_domain.domain_2.domain_name + } +} +`, name, pw, name, pw, name) +} diff --git a/website/docs/r/opensearch_inbound_connection_accepter.html.markdown b/website/docs/r/opensearch_inbound_connection_accepter.html.markdown new file mode 100644 index 00000000000..04b72ae840e --- /dev/null +++ b/website/docs/r/opensearch_inbound_connection_accepter.html.markdown @@ -0,0 +1,60 @@ +--- +subcategory: "OpenSearch" +layout: "aws" +page_title: "AWS: aws_opensearch_inbound_connection_accepter" +description: |- + Terraform resource for managing an AWS OpenSearch Inbound Connection Accepter. +--- + +# Resource: aws_opensearch_inbound_connection_accepter + +Manages an [AWS Opensearch Inbound Connection Accepter](https://docs.aws.amazon.com/opensearch-service/latest/APIReference/API_AcceptInboundConnection.html). If connecting domains from different AWS accounts, ensure that the accepter is configured to use the AWS account where the _remote_ opensearch domain exists. + +## Example Usage + +### Basic Usage + +```terraform +data "aws_caller_identity" "current" {} +data "aws_region" "current" {} + +resource "aws_opensearch_outbound_connection" "foo" { + connection_alias = "outbound_connection" + local_domain_info { + owner_id = data.aws_caller_identity.current.account_id + region = data.aws_region.current.name + domain_name = aws_opensearch_domain.local_domain.domain_name + } + + remote_domain_info { + owner_id = data.aws_caller_identity.current.account_id + region = data.aws_region.current.name + domain_name = aws_opensearch_domain.remote_domain.domain_name + } +} + +resource "aws_opensearch_inbound_connection_accepter" "foo" { + connection_id = aws_opensearch_outbound_connection.foo.id +} +``` + +## Argument Reference + +The following arguments are supported: + +* `connection_id` - (Required, Forces new resource) Specifies the ID of the connection to accept. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The Id of the connection to accept. +* `connection_status` - Status of the connection request. + +## Import + +AWS Opensearch Inbound Connection Accepters can be imported by using the Inbound Connection ID, e.g., + +``` +$ terraform import aws_opensearch_inbound_connection_accepter.foo connection-id +``` diff --git a/website/docs/r/opensearch_outbound_connection.html.markdown b/website/docs/r/opensearch_outbound_connection.html.markdown new file mode 100644 index 00000000000..dc4dda898da --- /dev/null +++ b/website/docs/r/opensearch_outbound_connection.html.markdown @@ -0,0 +1,70 @@ +--- +subcategory: "OpenSearch" +layout: "aws" +page_title: "AWS: aws_opensearch_outbound_connection" +description: |- + Terraform resource for managing an AWS OpenSearch Outbound Connection. +--- + +# Resource: aws_opensearch_outbound_connection + +Manages an AWS Opensearch Outbound Connection. + +## Example Usage + +### Basic Usage + +```terraform +data "aws_caller_identity" "current" {} +data "aws_region" "current" {} + +resource "aws_opensearch_outbound_connection" "foo" { + connection_alias = "outbound_connection" + local_domain_info { + owner_id = data.aws_caller_identity.current.account_id + region = data.aws_region.current.name + domain_name = aws_opensearch_domain.local_domain.domain_name + } + + remote_domain_info { + owner_id = data.aws_caller_identity.current.account_id + region = data.aws_region.current.name + domain_name = aws_opensearch_domain.remote_domain.domain_name + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `connection_alias` - (Required, Forces new resource) Specifies the connection alias that will be used by the customer for this connection. +* `local_domain_info` - (Required, Forces new resource) Configuration block for the local Opensearch domain. +* `remote_domain_info` - (Required, Forces new resource) Configuration block for the remote Opensearch domain. + +### local_domain_info + +* `owner_id` - (Required, Forces new resource) The Account ID of the owner of the local domain. +* `domain_name` - (Required, Forces new resource) The name of the local domain. +* `region` - (Required, Forces new resource) The region of the local domain. + +### remote_domain_info + +* `owner_id` - (Required, Forces new resource) The Account ID of the owner of the remote domain. +* `domain_name` - (Required, Forces new resource) The name of the remote domain. +* `region` - (Required, Forces new resource) The region of the remote domain. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The Id of the connection. +* `connection_status` - Status of the connection request. + +## Import + +AWS Opensearch Outbound Connections can be imported by using the Outbound Connection ID, e.g., + +``` +$ terraform import aws_opensearch_outbound_connection.foo connection-id +```