diff --git a/aws/internal/service/storagegateway/waiter/status.go b/aws/internal/service/storagegateway/waiter/status.go index dce23cba85e..2d344cfccc8 100644 --- a/aws/internal/service/storagegateway/waiter/status.go +++ b/aws/internal/service/storagegateway/waiter/status.go @@ -11,14 +11,55 @@ import ( ) const ( - StoredIscsiVolumeStatusNotFound = "NotFound" - StoredIscsiVolumeStatusUnknown = "Unknown" - NfsFileShareStatusNotFound = "NotFound" - NfsFileShareStatusUnknown = "Unknown" - SmbFileShareStatusNotFound = "NotFound" - SmbFileShareStatusUnknown = "Unknown" + StorageGatewayGatewayStatusConnected = "GatewayConnected" + StoredIscsiVolumeStatusNotFound = "NotFound" + StoredIscsiVolumeStatusUnknown = "Unknown" + NfsFileShareStatusNotFound = "NotFound" + NfsFileShareStatusUnknown = "Unknown" + SmbFileShareStatusNotFound = "NotFound" + SmbFileShareStatusUnknown = "Unknown" ) +func StorageGatewayGatewayStatus(conn *storagegateway.StorageGateway, gatewayARN string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + input := &storagegateway.DescribeGatewayInformationInput{ + GatewayARN: aws.String(gatewayARN), + } + + output, err := conn.DescribeGatewayInformation(input) + + if tfawserr.ErrMessageContains(err, storagegateway.ErrCodeInvalidGatewayRequestException, "The specified gateway is not connected") { + return output, storagegateway.ErrorCodeGatewayNotConnected, nil + } + + if err != nil { + return output, "", err + } + + return output, StorageGatewayGatewayStatusConnected, nil + } +} + +func StorageGatewayGatewayJoinDomainStatus(conn *storagegateway.StorageGateway, gatewayARN string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + input := &storagegateway.DescribeSMBSettingsInput{ + GatewayARN: aws.String(gatewayARN), + } + + output, err := conn.DescribeSMBSettings(input) + + if tfawserr.ErrMessageContains(err, storagegateway.ErrCodeInvalidGatewayRequestException, "The specified gateway is not connected") { + return output, storagegateway.ActiveDirectoryStatusUnknownError, nil + } + + if err != nil { + return output, storagegateway.ActiveDirectoryStatusUnknownError, err + } + + return output, aws.StringValue(output.ActiveDirectoryStatus), nil + } +} + // StoredIscsiVolumeStatus fetches the Volume and its Status func StoredIscsiVolumeStatus(conn *storagegateway.StorageGateway, volumeARN string) resource.StateRefreshFunc { return func() (interface{}, string, error) { diff --git a/aws/internal/service/storagegateway/waiter/waiter.go b/aws/internal/service/storagegateway/waiter/waiter.go index 6f1f1c4a77d..881ae65c914 100644 --- a/aws/internal/service/storagegateway/waiter/waiter.go +++ b/aws/internal/service/storagegateway/waiter/waiter.go @@ -8,13 +8,53 @@ import ( ) const ( - StoredIscsiVolumeAvailableTimeout = 5 * time.Minute - NfsFileShareAvailableDelay = 5 * time.Second - NfsFileShareDeletedDelay = 5 * time.Second - SmbFileShareAvailableDelay = 5 * time.Second - SmbFileShareDeletedDelay = 5 * time.Second + StorageGatewayGatewayConnectedMinTimeout = 10 * time.Second + StorageGatewayGatewayConnectedContinuousTargetOccurence = 6 + StorageGatewayGatewayJoinDomainJoinedTimeout = 5 * time.Minute + StoredIscsiVolumeAvailableTimeout = 5 * time.Minute + NfsFileShareAvailableDelay = 5 * time.Second + NfsFileShareDeletedDelay = 5 * time.Second + SmbFileShareAvailableDelay = 5 * time.Second + SmbFileShareDeletedDelay = 5 * time.Second ) +func StorageGatewayGatewayConnected(conn *storagegateway.StorageGateway, gatewayARN string, timeout time.Duration) (*storagegateway.DescribeGatewayInformationOutput, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{storagegateway.ErrorCodeGatewayNotConnected}, + Target: []string{StorageGatewayGatewayStatusConnected}, + Refresh: StorageGatewayGatewayStatus(conn, gatewayARN), + Timeout: timeout, + MinTimeout: StorageGatewayGatewayConnectedMinTimeout, + ContinuousTargetOccurence: StorageGatewayGatewayConnectedContinuousTargetOccurence, // Gateway activations can take a few seconds and can trigger a reboot of the Gateway + } + + outputRaw, err := stateConf.WaitForState() + + switch output := outputRaw.(type) { + case *storagegateway.DescribeGatewayInformationOutput: + return output, err + default: + return nil, err + } +} + +func StorageGatewayGatewayJoinDomainJoined(conn *storagegateway.StorageGateway, volumeARN string) (*storagegateway.DescribeSMBSettingsOutput, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{storagegateway.ActiveDirectoryStatusJoining}, + Target: []string{storagegateway.ActiveDirectoryStatusJoined}, + Refresh: StorageGatewayGatewayJoinDomainStatus(conn, volumeARN), + Timeout: StorageGatewayGatewayJoinDomainJoinedTimeout, + } + + outputRaw, err := stateConf.WaitForState() + + if output, ok := outputRaw.(*storagegateway.DescribeSMBSettingsOutput); ok { + return output, err + } + + return nil, err +} + // StoredIscsiVolumeAvailable waits for a StoredIscsiVolume to return Available func StoredIscsiVolumeAvailable(conn *storagegateway.StorageGateway, volumeARN string) (*storagegateway.DescribeStorediSCSIVolumesOutput, error) { stateConf := &resource.StateChangeConf{ diff --git a/aws/resource_aws_storagegateway_gateway.go b/aws/resource_aws_storagegateway_gateway.go index fa1dec3f5b5..54094ae29cb 100644 --- a/aws/resource_aws_storagegateway_gateway.go +++ b/aws/resource_aws_storagegateway_gateway.go @@ -6,6 +6,7 @@ import ( "log" "net" "net/http" + "regexp" "time" "github.com/aws/aws-sdk-go/aws" @@ -15,10 +16,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" -) - -const ( - StorageGatewayGatewayConnected = "GatewayConnected" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/storagegateway/waiter" ) func resourceAwsStorageGatewayGateway() *schema.Resource { @@ -36,7 +34,7 @@ func resourceAwsStorageGatewayGateway() *schema.Resource { State: schema.ImportStatePassthrough, }, Timeouts: &schema.ResourceTimeout{ - Create: schema.DefaultTimeout(10 * time.Minute), + Create: schema.DefaultTimeout(15 * time.Minute), }, Schema: map[string]*schema.Schema{ @@ -69,14 +67,20 @@ func resourceAwsStorageGatewayGateway() *schema.Resource { ConflictsWith: []string{"activation_key"}, }, "gateway_name": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.NoZeroValues, + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.All( + validation.StringMatch(regexp.MustCompile(`^[ -\.0-\[\]-~]*[!-\.0-\[\]-~][ -\.0-\[\]-~]*$`), ""), + validation.StringLenBetween(2, 255), + ), }, "gateway_timezone": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.NoZeroValues, + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.Any( + validation.StringMatch(regexp.MustCompile(`^GMT[+-][0-9]{1,2}:[0-9]{2}$`), ""), + validation.StringMatch(regexp.MustCompile(`^GMT$`), ""), + ), }, "gateway_type": { Type: schema.TypeString, @@ -97,6 +101,7 @@ func resourceAwsStorageGatewayGateway() *schema.Resource { ValidateFunc: validation.StringInSlice([]string{ "AWS-Gateway-VTL", "STK-L700", + "IBM-03584L32-0402", }, false), }, "smb_active_directory_settings": { @@ -108,15 +113,53 @@ func resourceAwsStorageGatewayGateway() *schema.Resource { "domain_name": { Type: schema.TypeString, Required: true, + ValidateFunc: validation.All( + validation.StringMatch(regexp.MustCompile(`^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$`), ""), + validation.StringLenBetween(1, 1024), + ), + }, + "timeout_in_seconds": { + Type: schema.TypeInt, + Optional: true, + Default: 20, + ValidateFunc: validation.IntBetween(0, 3600), }, "password": { Type: schema.TypeString, Required: true, Sensitive: true, + ValidateFunc: validation.All( + validation.StringMatch(regexp.MustCompile(`^[ -~]+$`), ""), + validation.StringLenBetween(1, 1024), + ), }, "username": { Type: schema.TypeString, Required: true, + ValidateFunc: validation.All( + validation.StringMatch(regexp.MustCompile(`^\w[\w\.\- ]*$`), ""), + validation.StringLenBetween(1, 1024), + ), + }, + "organizational_unit": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 1024), + }, + "domain_controllers": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.All( + validation.StringMatch(regexp.MustCompile(`^(([a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9\-]*[A-Za-z0-9])(:(\d+))?$`), ""), + validation.StringLenBetween(6, 1024), + ), + }, + }, + "active_directory_status": { + Type: schema.TypeString, + Computed: true, }, }, }, @@ -125,6 +168,10 @@ func resourceAwsStorageGatewayGateway() *schema.Resource { Type: schema.TypeString, Optional: true, Sensitive: true, + ValidateFunc: validation.All( + validation.StringMatch(regexp.MustCompile(`^[ -~]+$`), ""), + validation.StringLenBetween(6, 512), + ), }, "tape_drive_type": { Type: schema.TypeString, @@ -156,6 +203,30 @@ func resourceAwsStorageGatewayGateway() *schema.Resource { Optional: true, ValidateFunc: validation.IntAtLeast(51200), }, + "ec2_instance_id": { + Type: schema.TypeString, + Computed: true, + }, + "endpoint_type": { + Type: schema.TypeString, + Computed: true, + }, + "host_environment": { + Type: schema.TypeString, + Computed: true, + }, + "gateway_network_interface": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ipv4_address": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, }, } } @@ -264,25 +335,21 @@ func resourceAwsStorageGatewayGatewayCreate(d *schema.ResourceData, meta interfa d.SetId(aws.StringValue(output.GatewayARN)) - if _, err := WaitForStorageGatewayGatewayConnected(conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { - return fmt.Errorf("error waiting for Storage Gateway Gateway activation: %w", err) + if _, err = waiter.StorageGatewayGatewayConnected(conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { + return fmt.Errorf("error waiting for Storage Gateway Gateway (%q) to be Connected: %w", d.Id(), err) } if v, ok := d.GetOk("smb_active_directory_settings"); ok && len(v.([]interface{})) > 0 { - m := v.([]interface{})[0].(map[string]interface{}) - - input := &storagegateway.JoinDomainInput{ - DomainName: aws.String(m["domain_name"].(string)), - GatewayARN: aws.String(d.Id()), - Password: aws.String(m["password"].(string)), - UserName: aws.String(m["username"].(string)), - } - - log.Printf("[DEBUG] Storage Gateway Gateway %q joining Active Directory domain: %s", d.Id(), m["domain_name"].(string)) + input := expandStorageGatewayGatewayDomain(v.([]interface{}), d.Id()) + log.Printf("[DEBUG] Storage Gateway Gateway %q joining Active Directory domain: %s", d.Id(), aws.StringValue(input.DomainName)) _, err := conn.JoinDomain(input) if err != nil { return fmt.Errorf("error joining Active Directory domain: %w", err) } + + if _, err = waiter.StorageGatewayGatewayJoinDomainJoined(conn, d.Id()); err != nil { + return fmt.Errorf("error waiting for Storage Gateway Gateway (%q) to join domain (%s): %w", d.Id(), aws.StringValue(input.DomainName), err) + } } if v, ok := d.GetOk("smb_guest_password"); ok && v.(string) != "" { @@ -415,7 +482,8 @@ func resourceAwsStorageGatewayGatewayRead(d *schema.ResourceData, meta interface } } else { m := map[string]interface{}{ - "domain_name": aws.StringValue(smbSettingsOutput.DomainName), + "domain_name": aws.StringValue(smbSettingsOutput.DomainName), + "active_directory_status": aws.StringValue(smbSettingsOutput.ActiveDirectoryStatus), // The Storage Gateway API currently provides no way to read these values // "password": ..., // "username": ..., @@ -429,6 +497,15 @@ func resourceAwsStorageGatewayGatewayRead(d *schema.ResourceData, meta interface configM := v.([]interface{})[0].(map[string]interface{}) m["password"] = configM["password"] m["username"] = configM["username"] + m["timeout_in_seconds"] = configM["timeout_in_seconds"] + + if v, ok := configM["organizational_unit"]; ok { + m["organizational_unit"] = v + } + + if v, ok := configM["domain_controllers"]; ok { + m["domain_controllers"] = v + } } if err := d.Set("smb_active_directory_settings", []map[string]interface{}{m}); err != nil { return fmt.Errorf("error setting smb_active_directory_settings: %w", err) @@ -449,6 +526,13 @@ func resourceAwsStorageGatewayGatewayRead(d *schema.ResourceData, meta interface d.Set("tape_drive_type", d.Get("tape_drive_type").(string)) d.Set("cloudwatch_log_group_arn", output.CloudWatchLogGroupARN) d.Set("smb_security_strategy", smbSettingsOutput.SMBSecurityStrategy) + d.Set("ec2_instance_id", output.Ec2InstanceId) + d.Set("endpoint_type", output.EndpointType) + d.Set("host_environment", output.HostEnvironment) + + if err := d.Set("gateway_network_interface", flattenStorageGatewayGatewayNetworkInterfaces(output.GatewayNetworkInterfaces)); err != nil { + return fmt.Errorf("error setting gateway_network_interface: %w", err) + } bandwidthInput := &storagegateway.DescribeBandwidthRateLimitInput{ GatewayARN: aws.String(d.Id()), @@ -493,21 +577,16 @@ func resourceAwsStorageGatewayGatewayUpdate(d *schema.ResourceData, meta interfa } if d.HasChange("smb_active_directory_settings") { - l := d.Get("smb_active_directory_settings").([]interface{}) - m := l[0].(map[string]interface{}) - - input := &storagegateway.JoinDomainInput{ - DomainName: aws.String(m["domain_name"].(string)), - GatewayARN: aws.String(d.Id()), - Password: aws.String(m["password"].(string)), - UserName: aws.String(m["username"].(string)), - } - - log.Printf("[DEBUG] Storage Gateway Gateway %q joining Active Directory domain: %s", d.Id(), m["domain_name"].(string)) + input := expandStorageGatewayGatewayDomain(d.Get("smb_active_directory_settings").([]interface{}), d.Id()) + log.Printf("[DEBUG] Storage Gateway Gateway %q joining Active Directory domain: %s", d.Id(), aws.StringValue(input.DomainName)) _, err := conn.JoinDomain(input) if err != nil { return fmt.Errorf("error joining Active Directory domain: %w", err) } + + if _, err = waiter.StorageGatewayGatewayJoinDomainJoined(conn, d.Id()); err != nil { + return fmt.Errorf("error waiting for Storage Gateway Gateway (%q) to be Join domain (%s): %w", d.Id(), aws.StringValue(input.DomainName), err) + } } if d.HasChange("smb_guest_password") { @@ -608,53 +687,64 @@ func resourceAwsStorageGatewayGatewayDelete(d *schema.ResourceData, meta interfa return nil } -// The API returns multiple responses for a missing gateway -func isAWSErrStorageGatewayGatewayNotFound(err error) bool { - if isAWSErr(err, storagegateway.ErrCodeInvalidGatewayRequestException, "The specified gateway was not found.") { - return true +func expandStorageGatewayGatewayDomain(l []interface{}, gatewayArn string) *storagegateway.JoinDomainInput { + if l == nil || l[0] == nil { + return nil } - if isAWSErr(err, storagegateway.ErrorCodeGatewayNotFound, "") { - return true + + tfMap, ok := l[0].(map[string]interface{}) + if !ok { + return nil } - return false + + domain := &storagegateway.JoinDomainInput{ + DomainName: aws.String(tfMap["domain_name"].(string)), + GatewayARN: aws.String(gatewayArn), + Password: aws.String(tfMap["password"].(string)), + UserName: aws.String(tfMap["username"].(string)), + TimeoutInSeconds: aws.Int64(int64(tfMap["timeout_in_seconds"].(int))), + } + + if v, ok := tfMap["organizational_unit"].(string); ok && v != "" { + domain.OrganizationalUnit = aws.String(v) + } + + if v, ok := tfMap["domain_controllers"].(*schema.Set); ok && v.Len() > 0 { + domain.DomainControllers = expandStringSet(v) + } + + return domain } -func StorageGatewayGatewayConnectedStatus(conn *storagegateway.StorageGateway, gatewayARN string) resource.StateRefreshFunc { - return func() (interface{}, string, error) { - input := &storagegateway.DescribeGatewayInformationInput{ - GatewayARN: aws.String(gatewayARN), - } +func flattenStorageGatewayGatewayNetworkInterfaces(nis []*storagegateway.NetworkInterface) []interface{} { + if len(nis) == 0 { + return nil + } - output, err := conn.DescribeGatewayInformation(input) + var tfList []interface{} - if isAWSErr(err, storagegateway.ErrCodeInvalidGatewayRequestException, "The specified gateway is not connected") { - return output, storagegateway.ErrorCodeGatewayNotConnected, nil + for _, ni := range nis { + if ni == nil { + continue } - if err != nil { - return output, "", err + tfMap := map[string]interface{}{ + "ipv4_address": aws.StringValue(ni.Ipv4Address), } - return output, StorageGatewayGatewayConnected, nil + tfList = append(tfList, tfMap) } + + return tfList } -func WaitForStorageGatewayGatewayConnected(conn *storagegateway.StorageGateway, gatewayARN string, timeout time.Duration) (*storagegateway.DescribeGatewayInformationOutput, error) { - stateConf := &resource.StateChangeConf{ - Pending: []string{storagegateway.ErrorCodeGatewayNotConnected}, - Target: []string{StorageGatewayGatewayConnected}, - Refresh: StorageGatewayGatewayConnectedStatus(conn, gatewayARN), - Timeout: timeout, - MinTimeout: 10 * time.Second, - ContinuousTargetOccurence: 6, // Gateway activations can take a few seconds and can trigger a reboot of the Gateway +// The API returns multiple responses for a missing gateway +func isAWSErrStorageGatewayGatewayNotFound(err error) bool { + if isAWSErr(err, storagegateway.ErrCodeInvalidGatewayRequestException, "The specified gateway was not found.") { + return true } - - outputRaw, err := stateConf.WaitForState() - - switch output := outputRaw.(type) { - case *storagegateway.DescribeGatewayInformationOutput: - return output, err - default: - return nil, err + if isAWSErr(err, storagegateway.ErrorCodeGatewayNotFound, "") { + return true } + return false } diff --git a/aws/resource_aws_storagegateway_gateway_test.go b/aws/resource_aws_storagegateway_gateway_test.go index 30acb293a15..2cd33b782a5 100644 --- a/aws/resource_aws_storagegateway_gateway_test.go +++ b/aws/resource_aws_storagegateway_gateway_test.go @@ -86,6 +86,11 @@ func TestAccAWSStorageGatewayGateway_GatewayType_Cached(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "smb_guest_password", ""), resource.TestCheckResourceAttr(resourceName, "smb_security_strategy", ""), resource.TestCheckResourceAttr(resourceName, "tape_drive_type", ""), + resource.TestCheckResourceAttrPair(resourceName, "ec2_instance_id", "aws_instance.test", "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "STANDARD"), + resource.TestCheckResourceAttr(resourceName, "host_environment", "EC2"), + resource.TestCheckResourceAttr(resourceName, "gateway_network_interface.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "gateway_network_interface.0.ipv4_address", "aws_instance.test", "private_ip"), ), }, { @@ -121,6 +126,11 @@ func TestAccAWSStorageGatewayGateway_GatewayType_FileS3(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "smb_active_directory_settings.#", "0"), resource.TestCheckResourceAttr(resourceName, "smb_guest_password", ""), resource.TestCheckResourceAttr(resourceName, "tape_drive_type", ""), + resource.TestCheckResourceAttrPair(resourceName, "ec2_instance_id", "aws_instance.test", "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "STANDARD"), + resource.TestCheckResourceAttr(resourceName, "host_environment", "EC2"), + resource.TestCheckResourceAttr(resourceName, "gateway_network_interface.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "gateway_network_interface.0.ipv4_address", "aws_instance.test", "private_ip"), ), }, { @@ -156,6 +166,11 @@ func TestAccAWSStorageGatewayGateway_GatewayType_Stored(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "smb_active_directory_settings.#", "0"), resource.TestCheckResourceAttr(resourceName, "smb_guest_password", ""), resource.TestCheckResourceAttr(resourceName, "tape_drive_type", ""), + resource.TestCheckResourceAttrPair(resourceName, "ec2_instance_id", "aws_instance.test", "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "STANDARD"), + resource.TestCheckResourceAttr(resourceName, "host_environment", "EC2"), + resource.TestCheckResourceAttr(resourceName, "gateway_network_interface.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "gateway_network_interface.0.ipv4_address", "aws_instance.test", "private_ip"), ), }, { @@ -191,6 +206,9 @@ func TestAccAWSStorageGatewayGateway_GatewayType_Vtl(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "smb_active_directory_settings.#", "0"), resource.TestCheckResourceAttr(resourceName, "smb_guest_password", ""), resource.TestCheckResourceAttr(resourceName, "tape_drive_type", ""), + resource.TestCheckResourceAttrPair(resourceName, "ec2_instance_id", "aws_instance.test", "id"), + resource.TestCheckResourceAttr(resourceName, "endpoint_type", "STANDARD"), + resource.TestCheckResourceAttr(resourceName, "host_environment", "EC2"), ), }, { @@ -390,6 +408,37 @@ func TestAccAWSStorageGatewayGateway_SmbActiveDirectorySettings(t *testing.T) { testAccCheckAWSStorageGatewayGatewayExists(resourceName, &gateway), resource.TestCheckResourceAttr(resourceName, "smb_active_directory_settings.#", "1"), resource.TestCheckResourceAttr(resourceName, "smb_active_directory_settings.0.domain_name", "terraformtesting.com"), + resource.TestCheckResourceAttr(resourceName, "smb_active_directory_settings.0.username", "Administrator"), + resource.TestCheckResourceAttrSet(resourceName, "smb_active_directory_settings.0.active_directory_status"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"activation_key", "gateway_ip_address", "smb_active_directory_settings"}, + }, + }, + }) +} + +func TestAccAWSStorageGatewayGateway_SmbActiveDirectorySettings_timeout(t *testing.T) { + var gateway storagegateway.DescribeGatewayInformationOutput + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_storagegateway_gateway.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSStorageGatewayGatewayDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSStorageGatewayGatewayConfig_SmbActiveDirectorySettingsTimeout(rName, 50), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSStorageGatewayGatewayExists(resourceName, &gateway), + resource.TestCheckResourceAttr(resourceName, "smb_active_directory_settings.#", "1"), + resource.TestCheckResourceAttr(resourceName, "smb_active_directory_settings.0.domain_name", "terraformtesting.com"), + resource.TestCheckResourceAttr(resourceName, "smb_active_directory_settings.0.timeout_in_seconds", "50"), ), }, { @@ -675,7 +724,8 @@ func testAccCheckAWSStorageGatewayGatewayExists(resourceName string, gateway *st // testAccAWSStorageGateway_VPCBase provides a publicly accessible subnet // and security group, suitable for Storage Gateway EC2 instances of any type func testAccAWSStorageGateway_VPCBase(rName string) string { - return fmt.Sprintf(` + return composeConfig(testAccAvailableAZsNoOptInConfig(), + fmt.Sprintf(` resource "aws_vpc" "test" { cidr_block = "10.0.0.0/16" @@ -685,8 +735,9 @@ resource "aws_vpc" "test" { } resource "aws_subnet" "test" { - cidr_block = "10.0.0.0/24" - vpc_id = aws_vpc.test.id + cidr_block = "10.0.0.0/24" + vpc_id = aws_vpc.test.id + availability_zone = data.aws_availability_zones.available.names[0] tags = { Name = %[1]q @@ -728,7 +779,7 @@ resource "aws_security_group" "test" { Name = %[1]q } } -`, rName) +`, rName)) } func testAccAWSStorageGateway_FileGatewayBase(rName string) string { @@ -881,21 +932,13 @@ resource "aws_storagegateway_gateway" "test" { `, rName) } -func testAccAWSStorageGatewayGatewayConfig_SmbActiveDirectorySettings(rName string) string { +func testAccAWSStorageGatewayGatewayConfigSmbActiveDirectorySettingsBase(rName string) string { return composeConfig( // Reference: https://docs.aws.amazon.com/storagegateway/latest/userguide/Requirements.html testAccAvailableEc2InstanceTypeForAvailabilityZone("aws_subnet.test[0].availability_zone", "m5.xlarge", "m4.xlarge"), + testAccAvailableAZsNoOptInConfig(), fmt.Sprintf(` # Directory Service Directories must be deployed across multiple EC2 Availability Zones -data "aws_availability_zones" "available" { - state = "available" - - filter { - name = "opt-in-status" - values = ["opt-in-not-required"] - } -} - resource "aws_vpc" "test" { cidr_block = "10.0.0.0/16" @@ -999,7 +1042,13 @@ resource "aws_instance" "test" { Name = %[1]q } } +`, rName)) +} +func testAccAWSStorageGatewayGatewayConfig_SmbActiveDirectorySettings(rName string) string { + return composeConfig( + testAccAWSStorageGatewayGatewayConfigSmbActiveDirectorySettingsBase(rName), + fmt.Sprintf(` resource "aws_storagegateway_gateway" "test" { gateway_ip_address = aws_instance.test.public_ip gateway_name = %[1]q @@ -1015,6 +1064,26 @@ resource "aws_storagegateway_gateway" "test" { `, rName)) } +func testAccAWSStorageGatewayGatewayConfig_SmbActiveDirectorySettingsTimeout(rName string, timeout int) string { + return composeConfig( + testAccAWSStorageGatewayGatewayConfigSmbActiveDirectorySettingsBase(rName), + fmt.Sprintf(` +resource "aws_storagegateway_gateway" "test" { + gateway_ip_address = aws_instance.test.public_ip + gateway_name = %[1]q + gateway_timezone = "GMT" + gateway_type = "FILE_S3" + + smb_active_directory_settings { + domain_name = aws_directory_service_directory.test.name + password = aws_directory_service_directory.test.password + username = "Administrator" + timeout_in_seconds = %[2]d + } +} +`, rName, timeout)) +} + func testAccAWSStorageGatewayGatewayConfig_SmbGuestPassword(rName, smbGuestPassword string) string { return testAccAWSStorageGateway_FileGatewayBase(rName) + fmt.Sprintf(` resource "aws_storagegateway_gateway" "test" { diff --git a/website/docs/r/storagegateway_gateway.html.markdown b/website/docs/r/storagegateway_gateway.html.markdown index 41b6c4acfe9..0a1a1d2581a 100644 --- a/website/docs/r/storagegateway_gateway.html.markdown +++ b/website/docs/r/storagegateway_gateway.html.markdown @@ -75,7 +75,7 @@ The following arguments are supported: * `gateway_type` - (Optional) Type of the gateway. The default value is `STORED`. Valid values: `CACHED`, `FILE_S3`, `STORED`, `VTL`. * `gateway_vpc_endpoint` - (Optional) VPC endpoint address to be used when activating your gateway. This should be used when your instance is in a private subnet. Requires HTTP access from client computer running terraform. More info on what ports are required by your VPC Endpoint Security group in [Activating a Gateway in a Virtual Private Cloud](https://docs.aws.amazon.com/storagegateway/latest/userguide/gateway-private-link.html). * `cloudwatch_log_group_arn` - (Optional) The Amazon Resource Name (ARN) of the Amazon CloudWatch log group to use to monitor and log events in the gateway. -* `medium_changer_type` - (Optional) Type of medium changer to use for tape gateway. Terraform cannot detect drift of this argument. Valid values: `STK-L700`, `AWS-Gateway-VTL`. +* `medium_changer_type` - (Optional) Type of medium changer to use for tape gateway. Terraform cannot detect drift of this argument. Valid values: `STK-L700`, `AWS-Gateway-VTL`, `IBM-03584L32-0402`. * `smb_active_directory_settings` - (Optional) Nested argument with Active Directory domain join information for Server Message Block (SMB) file shares. Only valid for `FILE_S3` gateway type. Must be set before creating `ActiveDirectory` authentication SMB file shares. More details below. * `smb_guest_password` - (Optional) Guest password for Server Message Block (SMB) file shares. Only valid for `FILE_S3` gateway type. Must be set before creating `GuestAccess` authentication SMB file shares. Terraform can only detect drift of the existence of a guest password, not its actual value from the gateway. Terraform can however update the password with changing the argument. * `smb_security_strategy` - (Optional) Specifies the type of security strategy. Valid values are: `ClientSpecified`, `MandatorySigning`, and `MandatoryEncryption`. See [Setting a Security Level for Your Gateway](https://docs.aws.amazon.com/storagegateway/latest/userguide/managing-gateway-file.html#security-strategy) for more information. @@ -91,6 +91,11 @@ Information to join the gateway to an Active Directory domain for Server Message * `domain_name` - (Required) The name of the domain that you want the gateway to join. * `password` - (Required) The password of the user who has permission to add the gateway to the Active Directory domain. * `username` - (Required) The user name of user who has permission to add the gateway to the Active Directory domain. +* `timeout_in_seconds` - (Optional) Specifies the time in seconds, in which the JoinDomain operation must complete. The default is `20` seconds. +* `organizational_unit` - (Optional) The organizational unit (OU) is a container in an Active Directory that can hold users, groups, + computers, and other OUs and this parameter specifies the OU that the gateway will join within the AD domain. +* `domain_controllers` - (Optional) List of IPv4 addresses, NetBIOS names, or host names of your domain server. + If you need to specify the port number include it after the colon (“:”). For example, `mydc.mydomain.com:389`. ## Attributes Reference @@ -99,6 +104,14 @@ In addition to all arguments above, the following attributes are exported: * `id` - Amazon Resource Name (ARN) of the gateway. * `arn` - Amazon Resource Name (ARN) of the gateway. * `gateway_id` - Identifier of the gateway. +* `ec2_instance_id` - The ID of the Amazon EC2 instance that was used to launch the gateway. +* `endpoint_type` - The type of endpoint for your gateway. +* `host_environment` - The type of hypervisor environment used by the host. +* `gateway_network_interface` - An array that contains descriptions of the gateway network interfaces. See [Gateway Network Interface](#gateway-network-interface). + +### Gateway Network Interface + +* `ipv4_address` - The Internet Protocol version 4 (IPv4) address of the interface. ## Timeouts