diff --git a/aws/fsx.go b/aws/fsx.go new file mode 100644 index 000000000000..f8c5450b1c99 --- /dev/null +++ b/aws/fsx.go @@ -0,0 +1,77 @@ +package aws + +import ( + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/fsx" + "github.com/hashicorp/terraform/helper/resource" +) + +func describeFsxFileSystem(conn *fsx.FSx, id string) (*fsx.FileSystem, error) { + input := &fsx.DescribeFileSystemsInput{ + FileSystemIds: []*string{aws.String(id)}, + } + var filesystem *fsx.FileSystem + + err := conn.DescribeFileSystemsPages(input, func(page *fsx.DescribeFileSystemsOutput, lastPage bool) bool { + for _, fs := range page.FileSystems { + if aws.StringValue(fs.FileSystemId) == id { + filesystem = fs + return false + } + } + + return !lastPage + }) + + return filesystem, err +} + +func refreshFsxFileSystemLifecycle(conn *fsx.FSx, id string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + filesystem, err := describeFsxFileSystem(conn, id) + + if isAWSErr(err, fsx.ErrCodeFileSystemNotFound, "") { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + if filesystem == nil { + return nil, "", nil + } + + return filesystem, aws.StringValue(filesystem.Lifecycle), nil + } +} + +func waitForFsxFileSystemCreation(conn *fsx.FSx, id string, timeout time.Duration) error { + stateConf := &resource.StateChangeConf{ + Pending: []string{fsx.FileSystemLifecycleCreating}, + Target: []string{fsx.FileSystemLifecycleAvailable}, + Refresh: refreshFsxFileSystemLifecycle(conn, id), + Timeout: timeout, + Delay: 30 * time.Second, + } + + _, err := stateConf.WaitForState() + + return err +} + +func waitForFsxFileSystemDeletion(conn *fsx.FSx, id string, timeout time.Duration) error { + stateConf := &resource.StateChangeConf{ + Pending: []string{fsx.FileSystemLifecycleAvailable, fsx.FileSystemLifecycleDeleting}, + Target: []string{}, + Refresh: refreshFsxFileSystemLifecycle(conn, id), + Timeout: timeout, + Delay: 30 * time.Second, + } + + _, err := stateConf.WaitForState() + + return err +} diff --git a/aws/provider.go b/aws/provider.go index a68aabd43f26..a3597a77c9ac 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -496,7 +496,8 @@ func Provider() terraform.ResourceProvider { "aws_emr_instance_group": resourceAwsEMRInstanceGroup(), "aws_emr_security_configuration": resourceAwsEMRSecurityConfiguration(), "aws_flow_log": resourceAwsFlowLog(), - "aws_fsx_file_system": resourceAwsFsxFileSystem(), + "aws_fsx_lustre_file_system": resourceAwsFsxLustreFileSystem(), + "aws_fsx_windows_file_system": resourceAwsFsxWindowsFileSystem(), "aws_fms_admin_account": resourceAwsFmsAdminAccount(), "aws_gamelift_alias": resourceAwsGameliftAlias(), "aws_gamelift_build": resourceAwsGameliftBuild(), diff --git a/aws/resource_aws_fsx_file_system.go b/aws/resource_aws_fsx_file_system.go deleted file mode 100644 index de469c45dba4..000000000000 --- a/aws/resource_aws_fsx_file_system.go +++ /dev/null @@ -1,649 +0,0 @@ -package aws - -import ( - "fmt" - "log" - "time" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/fsx" - - "github.com/hashicorp/terraform/helper/resource" - "github.com/hashicorp/terraform/helper/schema" - "github.com/hashicorp/terraform/helper/validation" -) - -func resourceAwsFsxFileSystem() *schema.Resource { - return &schema.Resource{ - Create: resourceAwsFsxFileSystemCreate, - Read: resourceAwsFsxFileSystemRead, - Update: resourceAwsFsxFileSystemUpdate, - Delete: resourceAwsFsxFileSystemDelete, - Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, - }, - - Schema: map[string]*schema.Schema{ - "type": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{ - fsx.FileSystemTypeLustre, - fsx.FileSystemTypeWindows, - }, false), - }, - "capacity": { - Type: schema.TypeInt, - Required: true, - ForceNew: true, - ValidateFunc: validation.IntAtLeast(300), - }, - "kms_key_id": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - ConflictsWith: []string{"lustre_configuration"}, - }, - "subnet_ids": { - Type: schema.TypeSet, - Required: true, - ForceNew: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - "security_group_ids": { - Type: schema.TypeSet, - Optional: true, - ForceNew: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - "timeout": { - Type: schema.TypeInt, - Optional: true, - Default: 120, - ValidateFunc: validation.IntAtLeast(60), - }, - "lustre_configuration": { - Type: schema.TypeList, - Optional: true, - Computed: true, - MaxItems: 1, - ConflictsWith: []string{"windows_configuration"}, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "import_path": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - }, - "export_path": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, - "chunk_size": { - Type: schema.TypeInt, - Optional: true, - ForceNew: true, - }, - "weekly_maintenance_start_time": { - Type: schema.TypeString, - Optional: true, - Computed: true, - }, - }, - }, - }, - "windows_configuration": { - Type: schema.TypeList, - Optional: true, - Computed: true, - MaxItems: 1, - ConflictsWith: []string{"lustre_configuration"}, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "active_directory_id": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - }, - "self_managed_active_directory": { - Type: schema.TypeList, - Optional: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "username": { - Type: schema.TypeString, - Required: true, - }, - "password": { - Type: schema.TypeString, - Required: true, - }, - "dns_ips": { - Type: schema.TypeSet, - Required: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - "domain_name": { - Type: schema.TypeString, - Required: true, - }, - "administrators_group": { - Type: schema.TypeString, - Optional: true, - }, - "ou_distinguished_name": { - Type: schema.TypeString, - Optional: true, - }, - }, - }, - }, - "backup_retention": { - Type: schema.TypeInt, - Optional: true, - Default: 7, - ValidateFunc: validation.IntBetween(0, 35), - }, - "copy_tags_to_backups": { - Type: schema.TypeBool, - Optional: true, - Default: false, - ForceNew: true, - }, - "daily_backup_start_time": { - Type: schema.TypeString, - Optional: true, - Computed: true, - }, - "throughput_capacity": { - Type: schema.TypeInt, - Required: true, - ForceNew: true, - }, - "weekly_maintenance_start_time": { - Type: schema.TypeString, - Optional: true, - Computed: true, - }, - }, - }, - }, - "arn": { - Type: schema.TypeString, - Computed: true, - }, - "dns_name": { - Type: schema.TypeString, - Computed: true, - }, - "tags": tagsSchema(), - }, - } -} - -func resourceAwsFsxFileSystemCreate(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*AWSClient).fsxconn - - request := &fsx.CreateFileSystemInput{ - FileSystemType: aws.String(d.Get("type").(string)), - StorageCapacity: aws.Int64(int64(d.Get("capacity").(int))), - SubnetIds: expandStringList(d.Get("subnet_ids").(*schema.Set).List()), - } - - if _, ok := d.GetOk("kms_key_id"); ok { - request.KmsKeyId = aws.String(d.Get("kms_key_id").(string)) - } - - if _, ok := d.GetOk("security_group_ids"); ok { - request.SecurityGroupIds = expandStringList(d.Get("security_group_ids").(*schema.Set).List()) - } - - if _, ok := d.GetOk("lustre_configuration"); ok { - request.LustreConfiguration = expandFsxLustreConfigurationCreate(d.Get("lustre_configuration").([]interface{})) - } - - if _, ok := d.GetOk("windows_configuration"); ok { - request.WindowsConfiguration = expandFsxWindowsConfigurationCreate(d.Get("windows_configuration").([]interface{})) - } - - if value, ok := d.GetOk("tags"); ok { - request.Tags = tagsFromMapFSX(value.(map[string]interface{})) - } - - log.Printf("[DEBUG] FSx Filesystem create opts: %s", request) - result, err := conn.CreateFileSystem(request) - if err != nil { - return fmt.Errorf("Error creating FSx filesystem: %s", err) - } - - d.SetId(*result.FileSystem.FileSystemId) - - log.Println("[DEBUG] Waiting for filesystem to become available") - - stateConf := &resource.StateChangeConf{ - Pending: []string{fsx.FileSystemLifecycleCreating}, - Target: []string{fsx.FileSystemLifecycleAvailable}, - Refresh: fsxStateRefreshFunc(conn, d.Id()), - Timeout: time.Duration(d.Get("timeout").(int)) * time.Minute, - Delay: 30 * time.Second, - MinTimeout: 15 * time.Second, - } - - _, err = stateConf.WaitForState() - if err != nil { - return fmt.Errorf( - "Error waiting for filesystem (%s) to become available: %s", - *result.FileSystem.FileSystemId, err) - } - - return resourceAwsFsxFileSystemRead(d, meta) -} - -func resourceAwsFsxFileSystemUpdate(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*AWSClient).fsxconn - - if d.HasChange("tags") { - if err := setTagsFSX(conn, d); err != nil { - return fmt.Errorf("Error updating tags for FSx filesystem: %s", err) - } - } - - requestUpdate := false - params := &fsx.UpdateFileSystemInput{ - FileSystemId: aws.String(d.Id()), - } - - if d.HasChange("lustre_configuration") { - params.LustreConfiguration = expandFsxLustreConfigurationUpdate(d.Get("lustre_configuration").([]interface{})) - requestUpdate = true - } - - if d.HasChange("windows_configuration") { - params.WindowsConfiguration = expandFsxWindowsConfigurationUpdate(d.Get("windows_configuration").([]interface{})) - requestUpdate = true - } - - if requestUpdate { - _, err := conn.UpdateFileSystem(params) - if err != nil { - return fmt.Errorf("error updating FSX File System (%s): %s", d.Id(), err) - } - } - - return resourceAwsFsxFileSystemRead(d, meta) -} - -func resourceAwsFsxFileSystemRead(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*AWSClient).fsxconn - - filesystem, err := describeFsxFileSystem(conn, d.Id()) - - if isAWSErr(err, fsx.ErrCodeFileSystemNotFound, "") { - log.Printf("[WARN] FSx File System (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil - } - - if err != nil { - return fmt.Errorf("Error reading FSx File System (%s): %s", d.Id(), err) - } - - if filesystem == nil { - log.Printf("[WARN] FSx File System (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil - } - - d.Set("type", filesystem.FileSystemType) - d.Set("capacity", filesystem.StorageCapacity) - d.Set("arn", filesystem.ResourceARN) - d.Set("dns_name", filesystem.DNSName) - d.Set("kms_key_id", filesystem.KmsKeyId) - - d.Set("tags", tagsToMapFSX(filesystem.Tags)) - - err = d.Set("lustre_configuration", flattenLustreOptsConfig(filesystem.LustreConfiguration)) - if err != nil { - return err - } - - err = d.Set("windows_configuration", flattenWindowsOptsConfig(filesystem.WindowsConfiguration)) - if err != nil { - return err - } - - err = d.Set("subnet_ids", aws.StringValueSlice(filesystem.SubnetIds)) - if err != nil { - return err - } - - err = d.Set("security_group_ids", expandStringList(d.Get("security_group_ids").(*schema.Set).List())) - if err != nil { - return err - } - - return nil -} - -func resourceAwsFsxFileSystemDelete(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*AWSClient).fsxconn - - request := &fsx.DeleteFileSystemInput{ - FileSystemId: aws.String(d.Id()), - } - - _, err := conn.DeleteFileSystem(request) - if err != nil { - return fmt.Errorf("Error deleting FSx filesystem: %s", err) - } - - log.Println("[DEBUG] Waiting for filesystem to delete") - - stateConf := &resource.StateChangeConf{ - Pending: []string{fsx.FileSystemLifecycleAvailable, fsx.FileSystemLifecycleDeleting}, - Target: []string{}, - Refresh: fsxStateRefreshFunc(conn, d.Id()), - Timeout: time.Duration(d.Get("timeout").(int)) * time.Minute, - Delay: 30 * time.Second, - MinTimeout: 15 * time.Second, - } - - _, err = stateConf.WaitForState() - if err != nil { - return fmt.Errorf("Error waiting for filesystem (%s) to delete: %s", d.Id(), err) - } - - return nil -} - -func describeFsxFileSystem(conn *fsx.FSx, id string) (*fsx.FileSystem, error) { - input := &fsx.DescribeFileSystemsInput{ - FileSystemIds: []*string{aws.String(id)}, - } - var filesystem *fsx.FileSystem - - err := conn.DescribeFileSystemsPages(input, func(page *fsx.DescribeFileSystemsOutput, lastPage bool) bool { - for _, fs := range page.FileSystems { - if aws.StringValue(fs.FileSystemId) == id { - filesystem = fs - break - } - } - - return !lastPage - }) - - return filesystem, err -} - -func fsxStateRefreshFunc(conn *fsx.FSx, id string) resource.StateRefreshFunc { - return func() (interface{}, string, error) { - resp, err := conn.DescribeFileSystems(&fsx.DescribeFileSystemsInput{ - FileSystemIds: []*string{aws.String(id)}, - }) - - if resp == nil { - return nil, "", nil - } - - if isAWSErr(err, fsx.ErrCodeFileSystemNotFound, "") { - return nil, "", nil - } - - if err != nil { - return nil, "", err - } - - filesystem := resp.FileSystems[0] - return filesystem, *filesystem.Lifecycle, nil - } -} - -func expandFsxLustreConfigurationCreate(l []interface{}) *fsx.CreateFileSystemLustreConfiguration { - if len(l) == 0 || l[0] == nil { - return nil - } - - data := l[0].(map[string]interface{}) - req := &fsx.CreateFileSystemLustreConfiguration{} - - if data["import_path"].(string) != "" { - req.ImportPath = aws.String(data["import_path"].(string)) - } - - if data["export_path"].(string) != "" { - req.ExportPath = aws.String(data["export_path"].(string)) - } - - if data["chunk_size"] != nil { - req.ImportedFileChunkSize = aws.Int64(int64(data["chunk_size"].(int))) - } - - if data["weekly_maintenance_start_time"].(string) != "" { - req.WeeklyMaintenanceStartTime = aws.String(data["weekly_maintenance_start_time"].(string)) - } - - return req -} - -func expandFsxLustreConfigurationUpdate(l []interface{}) *fsx.UpdateFileSystemLustreConfiguration { - if len(l) == 0 || l[0] == nil { - return nil - } - - data := l[0].(map[string]interface{}) - req := &fsx.UpdateFileSystemLustreConfiguration{} - - if data["weekly_maintenance_start_time"].(string) != "" { - req.WeeklyMaintenanceStartTime = aws.String(data["weekly_maintenance_start_time"].(string)) - } - - return req -} - -func expandFsxWindowsConfigurationCreate(l []interface{}) *fsx.CreateFileSystemWindowsConfiguration { - if len(l) == 0 || l[0] == nil { - return nil - } - - data := l[0].(map[string]interface{}) - req := &fsx.CreateFileSystemWindowsConfiguration{ - ThroughputCapacity: aws.Int64(int64(data["throughput_capacity"].(int))), - } - - if data["active_directory_id"].(string) != "" { - req.ActiveDirectoryId = aws.String(data["active_directory_id"].(string)) - } - - if data["self_managed_active_directory"] != nil { - req.SelfManagedActiveDirectoryConfiguration = expandSelfManagedAdOptsCreate(data["self_managed_active_directory"].([]interface{})) - } - - if data["backup_retention"] != nil { - req.AutomaticBackupRetentionDays = aws.Int64(int64(data["backup_retention"].(int))) - } - - if data["copy_tags_to_backups"] != nil { - req.CopyTagsToBackups = aws.Bool(data["copy_tags_to_backups"].(bool)) - } - - if data["daily_backup_start_time"].(string) != "" { - req.DailyAutomaticBackupStartTime = aws.String(data["daily_backup_start_time"].(string)) - } - - if data["weekly_maintenance_start_time"].(string) != "" { - req.WeeklyMaintenanceStartTime = aws.String(data["weekly_maintenance_start_time"].(string)) - } - - return req -} - -func expandFsxWindowsConfigurationUpdate(l []interface{}) *fsx.UpdateFileSystemWindowsConfiguration { - if len(l) == 0 || l[0] == nil { - return nil - } - - data := l[0].(map[string]interface{}) - req := &fsx.UpdateFileSystemWindowsConfiguration{} - - if data["backup_retention"] != nil { - req.AutomaticBackupRetentionDays = aws.Int64(int64(data["backup_retention"].(int))) - } - - if data["daily_backup_start_time"].(string) != "" { - req.DailyAutomaticBackupStartTime = aws.String(data["daily_backup_start_time"].(string)) - } - - if data["weekly_maintenance_start_time"].(string) != "" { - req.WeeklyMaintenanceStartTime = aws.String(data["weekly_maintenance_start_time"].(string)) - } - - if data["self_managed_active_directory"] != nil { - req.SelfManagedActiveDirectoryConfiguration = expandSelfManagedAdOptsUpdate(data["self_managed_active_directory"].([]interface{})) - } - - return req -} - -func expandSelfManagedAdOptsCreate(l []interface{}) *fsx.SelfManagedActiveDirectoryConfiguration { - if len(l) == 0 || l[0] == nil { - return nil - } - - data := l[0].(map[string]interface{}) - req := &fsx.SelfManagedActiveDirectoryConfiguration{} - - if d, ok := data["dns_ips"]; ok { - req.DnsIps = expandStringList(d.([]interface{})) - } - - if data["domain_name"].(string) != "" { - req.DomainName = aws.String(data["domain_name"].(string)) - } - - if data["administrators_group"].(string) != "" { - req.FileSystemAdministratorsGroup = aws.String(data["administrators_group"].(string)) - } - - if data["ou_distinguished_name"].(string) != "" { - req.OrganizationalUnitDistinguishedName = aws.String(data["ou_distinguished_name"].(string)) - } - - if data["password"].(string) != "" { - req.Password = aws.String(data["password"].(string)) - } - - if data["username"].(string) != "" { - req.UserName = aws.String(data["username"].(string)) - } - - return req -} - -func expandSelfManagedAdOptsUpdate(l []interface{}) *fsx.SelfManagedActiveDirectoryConfigurationUpdates { - if len(l) == 0 || l[0] == nil { - return nil - } - - data := l[0].(map[string]interface{}) - req := &fsx.SelfManagedActiveDirectoryConfigurationUpdates{} - - if d, ok := data["dns_ips"]; ok { - req.DnsIps = expandStringList(d.([]interface{})) - } - - if data["password"].(string) != "" { - req.Password = aws.String(data["password"].(string)) - } - - if data["username"].(string) != "" { - req.UserName = aws.String(data["username"].(string)) - } - - return req -} - -func flattenLustreOptsConfig(lopts *fsx.LustreFileSystemConfiguration) []map[string]interface{} { - if lopts == nil { - return []map[string]interface{}{} - } - - m := map[string]interface{}{} - - if lopts.DataRepositoryConfiguration != nil && *lopts.DataRepositoryConfiguration.ImportPath != "" { - m["import_path"] = aws.StringValue(lopts.DataRepositoryConfiguration.ImportPath) - } - if lopts.DataRepositoryConfiguration != nil && *lopts.DataRepositoryConfiguration.ExportPath != "" { - m["export_path"] = aws.StringValue(lopts.DataRepositoryConfiguration.ExportPath) - } - if lopts.DataRepositoryConfiguration != nil && *lopts.DataRepositoryConfiguration.ImportedFileChunkSize != 0 { - m["chunk_size"] = aws.Int64Value(lopts.DataRepositoryConfiguration.ImportedFileChunkSize) - } - if lopts.WeeklyMaintenanceStartTime != nil { - m["weekly_maintenance_start_time"] = aws.StringValue(lopts.WeeklyMaintenanceStartTime) - } - - return []map[string]interface{}{m} -} - -func flattenWindowsOptsConfig(wopts *fsx.WindowsFileSystemConfiguration) []map[string]interface{} { - if wopts == nil { - return []map[string]interface{}{} - } - - m := map[string]interface{}{} - - if wopts.ActiveDirectoryId != nil { - m["active_directory_id"] = aws.StringValue(wopts.ActiveDirectoryId) - } - if wopts.AutomaticBackupRetentionDays != nil { - m["backup_retention"] = aws.Int64Value(wopts.AutomaticBackupRetentionDays) - } - if wopts.CopyTagsToBackups != nil { - m["copy_tags_to_backups"] = aws.BoolValue(wopts.CopyTagsToBackups) - } - if wopts.DailyAutomaticBackupStartTime != nil { - m["daily_backup_start_time"] = aws.StringValue(wopts.DailyAutomaticBackupStartTime) - } - if wopts.ThroughputCapacity != nil { - m["throughput_capacity"] = aws.Int64Value(wopts.ThroughputCapacity) - } - if wopts.WeeklyMaintenanceStartTime != nil { - m["weekly_maintenance_start_time"] = aws.StringValue(wopts.WeeklyMaintenanceStartTime) - } - if wopts.SelfManagedActiveDirectoryConfiguration != nil { - m["self_managed_active_directory"] = flattenSelfManagedAdOptsConfig(wopts.SelfManagedActiveDirectoryConfiguration) - } - - return []map[string]interface{}{m} -} - -func flattenSelfManagedAdOptsConfig(adopts *fsx.SelfManagedActiveDirectoryAttributes) []map[string]interface{} { - if adopts == nil { - return []map[string]interface{}{} - } - - m := map[string]interface{}{} - - if adopts.UserName != nil { - m["username"] = aws.StringValue(adopts.UserName) - } - if adopts.DnsIps != nil { - m["dns_ips"] = aws.StringValueSlice(adopts.DnsIps) - } - if adopts.DomainName != nil { - m["domain_name"] = aws.StringValue(adopts.DomainName) - } - if adopts.FileSystemAdministratorsGroup != nil { - m["administrators_group"] = aws.StringValue(adopts.FileSystemAdministratorsGroup) - } - if adopts.OrganizationalUnitDistinguishedName != nil { - m["ou_distinguished_name"] = aws.StringValue(adopts.OrganizationalUnitDistinguishedName) - } - - return []map[string]interface{}{m} -} diff --git a/aws/resource_aws_fsx_file_system_test.go b/aws/resource_aws_fsx_file_system_test.go deleted file mode 100644 index 5c6cd0b29556..000000000000 --- a/aws/resource_aws_fsx_file_system_test.go +++ /dev/null @@ -1,367 +0,0 @@ -package aws - -import ( - "fmt" - "testing" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/fsx" - "github.com/hashicorp/terraform/helper/resource" - "github.com/hashicorp/terraform/terraform" -) - -func TestAccAWSFsxFileSystem_lustreBasic(t *testing.T) { - var v fsx.FileSystem - resourceName := "aws_fsx_file_system.test" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - IDRefreshName: resourceName, - Providers: testAccProviders, - CheckDestroy: testAccCheckFsxFileSystemDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAwsFsxFileSystemLustreBasic(), - Check: resource.ComposeTestCheckFunc( - testAccCheckFileSystemExists(resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "type", "LUSTRE"), - resource.TestCheckResourceAttr(resourceName, "capacity", "3600"), - resource.TestCheckResourceAttr(resourceName, "subnet_ids.#", "1"), - resource.TestCheckResourceAttr(resourceName, "security_group_ids.#", "1"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"timeout", "security_group_ids"}, - }, - }, - }) -} - -func TestAccAWSFsxFileSystem_lustreConfig(t *testing.T) { - var v fsx.FileSystem - resourceName := "aws_fsx_file_system.test" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckFsxFileSystemDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAwsFsxFileSystemLustreConfigOpts(), - Check: resource.ComposeTestCheckFunc( - testAccCheckFileSystemExists(resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "lustre_configuration.#", "1"), - resource.TestCheckResourceAttr(resourceName, "lustre_configuration.0.import_path", "s3://nasanex"), - resource.TestCheckResourceAttr(resourceName, "lustre_configuration.0.chunk_size", "2048"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"timeout", "security_group_ids"}, - }, - }, - }) -} - -func TestAccAWSFsxFileSystem_lustreUpdate(t *testing.T) { - var v fsx.FileSystem - resourceName := "aws_fsx_file_system.test" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckFsxFileSystemDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAwsFsxFileSystemLustreConfigOpts(), - Check: resource.ComposeTestCheckFunc( - testAccCheckFileSystemExists(resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "lustre_configuration.#", "1"), - resource.TestCheckResourceAttrSet(resourceName, "lustre_configuration.0.weekly_maintenance_start_time"), - ), - }, - { - Config: testAccAwsFsxFileSystemLustreUpdateOpts(), - Check: resource.ComposeTestCheckFunc( - testAccCheckFileSystemExists(resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "lustre_configuration.#", "1"), - resource.TestCheckResourceAttr(resourceName, "lustre_configuration.0.weekly_maintenance_start_time", "5:05:50"), - ), - }, - }, - }) -} - -func TestAccAWSFsxFileSystem_windowsConfig(t *testing.T) { - var v fsx.FileSystem - resourceName := "aws_fsx_file_system.test" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckFsxFileSystemDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAwsFsxFileSystemWindowsConfigOpts(), - Check: resource.ComposeTestCheckFunc( - testAccCheckFileSystemExists(resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "windows_configuration.#", "1"), - resource.TestCheckResourceAttr(resourceName, "windows_configuration.0.backup_retention", "3"), - resource.TestCheckResourceAttr(resourceName, "windows_configuration.0.copy_tags_to_backups", "true"), - resource.TestCheckResourceAttr(resourceName, "windows_configuration.0.throughput_capacity", "1024"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"timeout", "security_group_ids"}, - }, - }, - }) -} - -func TestAccAWSFsxFileSystem_windowsUpdate(t *testing.T) { - var v fsx.FileSystem - resourceName := "aws_fsx_file_system.test" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckFsxFileSystemDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAwsFsxFileSystemWindowsConfigOpts(), - Check: resource.ComposeTestCheckFunc( - testAccCheckFileSystemExists(resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "windows_configuration.#", "1"), - resource.TestCheckResourceAttr(resourceName, "windows_configuration.0.backup_retention", "3"), - ), - }, - { - Config: testAccAwsFsxFileSystemWindowsUpdateOpts(), - Check: resource.ComposeTestCheckFunc( - testAccCheckFileSystemExists(resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "windows_configuration.#", "1"), - resource.TestCheckResourceAttr(resourceName, "windows_configuration.0.backup_retention", "30"), - ), - }, - }, - }) -} - -func testAccCheckFileSystemExists(n string, v *fsx.FileSystem) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[n] - if !ok { - return fmt.Errorf("Not found: %s", n) - } - - if rs.Primary.ID == "" { - return fmt.Errorf("No ID is set") - } - - conn := testAccProvider.Meta().(*AWSClient).fsxconn - - request := &fsx.DescribeFileSystemsInput{ - FileSystemIds: []*string{aws.String(rs.Primary.ID)}, - } - - response, err := conn.DescribeFileSystems(request) - if err == nil { - if response.FileSystems != nil && len(response.FileSystems) > 0 { - *v = *response.FileSystems[0] - return nil - } - } - return fmt.Errorf("Error finding FSx filesystem %s", rs.Primary.ID) - } -} - -func testAccCheckFsxFileSystemDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*AWSClient).fsxconn - - for _, rs := range s.RootModule().Resources { - if rs.Type != "aws_fsx_file_system" { - continue - } - - filesystem, err := describeFsxFileSystem(conn, rs.Primary.ID) - - if isAWSErr(err, fsx.ErrCodeFileSystemNotFound, "") { - continue - } - - if err != nil { - return err - } - - if filesystem != nil { - return fmt.Errorf("FSx File System (%s) still exists", rs.Primary.ID) - } - } - return nil -} - -const testAccAwsFsxFileSystemBaseConfig = ` -data "aws_availability_zones" "available" { - state = "available" -} - -resource "aws_vpc" "test" { - cidr_block = "10.0.0.0/16" -} - -resource "aws_subnet" "test1" { - vpc_id = "${aws_vpc.test.id}" - cidr_block = "10.0.1.0/24" - availability_zone = "${data.aws_availability_zones.available.names[0]}" -} - -resource "aws_subnet" "test2" { - vpc_id = "${aws_vpc.test.id}" - cidr_block = "10.0.2.0/24" - availability_zone = "${data.aws_availability_zones.available.names[1]}" -} - -resource "aws_security_group" "test1" { - description = "security group for FSx testing" - vpc_id = "${aws_vpc.test.id}" - - ingress { - from_port = 988 - to_port = 988 - protocol = "tcp" - cidr_blocks = ["10.0.0.0/16"] - } - - ingress { - from_port = 135 - to_port = 135 - protocol = "tcp" - cidr_blocks = ["10.0.0.0/16"] - } - - ingress { - from_port = 445 - to_port = 445 - protocol = "tcp" - cidr_blocks = ["10.0.0.0/16"] - } - - ingress { - from_port = 55555 - to_port = 55555 - protocol = "tcp" - cidr_blocks = ["10.0.0.0/16"] - } - - egress { - from_port = 0 - to_port = 0 - protocol = "-1" - cidr_blocks = ["0.0.0.0/0"] - } -} -` - -const testAccAwsFsxFileSystemBaseWindowsConfig = ` -resource "aws_kms_key" "test" { - description = "FSx KMS Testing key" - deletion_window_in_days = 7 -} - -resource "aws_directory_service_directory" "test" { - name = "corp.notexample.com" - password = "SuperSecretPassw0rd" - type = "MicrosoftAD" - - vpc_settings { - vpc_id = "${aws_vpc.test.id}" - subnet_ids = ["${aws_subnet.test1.id}", "${aws_subnet.test2.id}"] - } -} -` - -func testAccAwsFsxFileSystemLustreBasic() string { - return testAccAwsFsxFileSystemBaseConfig + fmt.Sprintf(` -resource "aws_fsx_file_system" "test" { - type = "LUSTRE" - capacity = 3600 - subnet_ids = ["${aws_subnet.test1.id}"] - security_group_ids = ["${aws_security_group.test1.id}"] -} -`) -} - -func testAccAwsFsxFileSystemLustreConfigOpts() string { - return testAccAwsFsxFileSystemBaseConfig + fmt.Sprintf(` -resource "aws_fsx_file_system" "test" { - type = "LUSTRE" - capacity = 3600 - subnet_ids = ["${aws_subnet.test1.id}"] - - lustre_configuration { - import_path = "s3://nasanex" - chunk_size = 2048 - } -} -`) -} - -func testAccAwsFsxFileSystemLustreUpdateOpts() string { - return testAccAwsFsxFileSystemBaseConfig + fmt.Sprintf(` -resource "aws_fsx_file_system" "test" { - type = "LUSTRE" - capacity = 3600 - subnet_ids = ["${aws_subnet.test1.id}"] - - lustre_configuration { - import_path = "s3://nasanex" - chunk_size = 2048 - weekly_maintenance_start_time = "5:05:50" - } -} -`) -} - -func testAccAwsFsxFileSystemWindowsConfigOpts() string { - return testAccAwsFsxFileSystemBaseConfig + testAccAwsFsxFileSystemBaseWindowsConfig + fmt.Sprintf(` -resource "aws_fsx_file_system" "test" { - type = "WINDOWS" - capacity = 300 - kms_key_id = "${aws_kms_key.test.arn}" - subnet_ids = ["${aws_subnet.test1.id}"] - - windows_configuration { - active_directory_id = "${aws_directory_service_directory.test.id}" - backup_retention = 3 - copy_tags_to_backups = true - throughput_capacity = 1024 - } -} -`) -} - -func testAccAwsFsxFileSystemWindowsUpdateOpts() string { - return testAccAwsFsxFileSystemBaseConfig + testAccAwsFsxFileSystemBaseWindowsConfig + fmt.Sprintf(` -resource "aws_fsx_file_system" "test" { - type = "WINDOWS" - capacity = 300 - kms_key_id = "${aws_kms_key.test.arn}" - subnet_ids = ["${aws_subnet.test1.id}"] - - windows_configuration { - active_directory_id = "${aws_directory_service_directory.test.id}" - backup_retention = 30 - copy_tags_to_backups = true - throughput_capacity = 1024 - } -} -`) -} diff --git a/aws/resource_aws_fsx_lustre_file_system.go b/aws/resource_aws_fsx_lustre_file_system.go new file mode 100644 index 000000000000..f9ac456c0f16 --- /dev/null +++ b/aws/resource_aws_fsx_lustre_file_system.go @@ -0,0 +1,296 @@ +package aws + +import ( + "fmt" + "log" + "regexp" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/fsx" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" +) + +func resourceAwsFsxLustreFileSystem() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsFsxLustreFileSystemCreate, + Read: resourceAwsFsxLustreFileSystemRead, + Update: resourceAwsFsxLustreFileSystemUpdate, + Delete: resourceAwsFsxLustreFileSystemDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(30 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "dns_name": { + Type: schema.TypeString, + Computed: true, + }, + "export_path": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validation.All( + validation.StringLenBetween(3, 900), + validation.StringMatch(regexp.MustCompile(`^s3://`), "must begin with s3://"), + ), + }, + "import_path": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.All( + validation.StringLenBetween(3, 900), + validation.StringMatch(regexp.MustCompile(`^s3://`), "must begin with s3://"), + ), + }, + "imported_file_chunk_size": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validation.IntBetween(1, 512000), + }, + "network_interface_ids": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "owner_id": { + Type: schema.TypeString, + Computed: true, + }, + "security_group_ids": { + Type: schema.TypeSet, + Optional: true, + ForceNew: true, + MaxItems: 50, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "storage_capacity": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + ValidateFunc: validation.IntAtLeast(3600), + }, + "subnet_ids": { + Type: schema.TypeSet, + Required: true, + ForceNew: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "tags": tagsSchema(), + "vpc_id": { + Type: schema.TypeString, + Computed: true, + }, + "weekly_maintenance_start_time": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.All( + validation.StringLenBetween(7, 7), + validation.StringMatch(regexp.MustCompile(`^[1-7]:([01]\d|2[0-3]):?([0-5]\d)$`), "must be in the format d:HH:MM"), + ), + }, + }, + } +} + +func resourceAwsFsxLustreFileSystemCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).fsxconn + + input := &fsx.CreateFileSystemInput{ + ClientRequestToken: aws.String(resource.UniqueId()), + FileSystemType: aws.String(fsx.FileSystemTypeLustre), + StorageCapacity: aws.Int64(int64(d.Get("storage_capacity").(int))), + SubnetIds: expandStringSet(d.Get("subnet_ids").(*schema.Set)), + } + + if v, ok := d.GetOk("export_path"); ok { + if input.LustreConfiguration == nil { + input.LustreConfiguration = &fsx.CreateFileSystemLustreConfiguration{} + } + + input.LustreConfiguration.ExportPath = aws.String(v.(string)) + } + + if v, ok := d.GetOk("import_path"); ok { + if input.LustreConfiguration == nil { + input.LustreConfiguration = &fsx.CreateFileSystemLustreConfiguration{} + } + + input.LustreConfiguration.ImportPath = aws.String(v.(string)) + } + + if v, ok := d.GetOk("imported_file_chunk_size"); ok { + if input.LustreConfiguration == nil { + input.LustreConfiguration = &fsx.CreateFileSystemLustreConfiguration{} + } + + input.LustreConfiguration.ImportedFileChunkSize = aws.Int64(int64(v.(int))) + } + + if v, ok := d.GetOk("security_group_ids"); ok { + input.SecurityGroupIds = expandStringSet(v.(*schema.Set)) + } + + if v, ok := d.GetOk("tags"); ok { + input.Tags = tagsFromMapFSX(v.(map[string]interface{})) + } + + if v, ok := d.GetOk("weekly_maintenance_start_time"); ok { + if input.LustreConfiguration == nil { + input.LustreConfiguration = &fsx.CreateFileSystemLustreConfiguration{} + } + + input.LustreConfiguration.WeeklyMaintenanceStartTime = aws.String(v.(string)) + } + + result, err := conn.CreateFileSystem(input) + if err != nil { + return fmt.Errorf("Error creating FSx filesystem: %s", err) + } + + d.SetId(*result.FileSystem.FileSystemId) + + log.Println("[DEBUG] Waiting for filesystem to become available") + + if err := waitForFsxFileSystemCreation(conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { + return fmt.Errorf("Error waiting for filesystem (%s) to become available: %s", d.Id(), err) + } + + return resourceAwsFsxLustreFileSystemRead(d, meta) +} + +func resourceAwsFsxLustreFileSystemUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).fsxconn + + if d.HasChange("tags") { + if err := setTagsFSX(conn, d); err != nil { + return fmt.Errorf("Error updating tags for FSx filesystem: %s", err) + } + } + + requestUpdate := false + input := &fsx.UpdateFileSystemInput{ + ClientRequestToken: aws.String(resource.UniqueId()), + FileSystemId: aws.String(d.Id()), + LustreConfiguration: &fsx.UpdateFileSystemLustreConfiguration{}, + } + + if d.HasChange("weekly_maintenance_start_time") { + input.LustreConfiguration.WeeklyMaintenanceStartTime = aws.String(d.Get("weekly_maintenance_start_time").(string)) + requestUpdate = true + } + + if requestUpdate { + _, err := conn.UpdateFileSystem(input) + if err != nil { + return fmt.Errorf("error updating FSX File System (%s): %s", d.Id(), err) + } + } + + return resourceAwsFsxLustreFileSystemRead(d, meta) +} + +func resourceAwsFsxLustreFileSystemRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).fsxconn + + filesystem, err := describeFsxFileSystem(conn, d.Id()) + + if isAWSErr(err, fsx.ErrCodeFileSystemNotFound, "") { + log.Printf("[WARN] FSx File System (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("Error reading FSx File System (%s): %s", d.Id(), err) + } + + if filesystem == nil { + log.Printf("[WARN] FSx File System (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if filesystem.WindowsConfiguration != nil { + return fmt.Errorf("expected FSx Lustre File System, found FSx Windows File System: %s", d.Id()) + } + + if filesystem.LustreConfiguration == nil { + return fmt.Errorf("error describing FSx Lustre File System (%s): empty Lustre configuration", d.Id()) + } + + if filesystem.LustreConfiguration.DataRepositoryConfiguration == nil { + // Initialize an empty structure to simplify d.Set() handling + filesystem.LustreConfiguration.DataRepositoryConfiguration = &fsx.DataRepositoryConfiguration{} + } + + d.Set("arn", filesystem.ResourceARN) + d.Set("dns_name", filesystem.DNSName) + d.Set("export_path", filesystem.LustreConfiguration.DataRepositoryConfiguration.ExportPath) + d.Set("import_path", filesystem.LustreConfiguration.DataRepositoryConfiguration.ImportPath) + d.Set("imported_file_chunk_size", filesystem.LustreConfiguration.DataRepositoryConfiguration.ImportedFileChunkSize) + + if err := d.Set("network_interface_ids", aws.StringValueSlice(filesystem.NetworkInterfaceIds)); err != nil { + return fmt.Errorf("error setting network_interface_ids: %s", err) + } + + d.Set("owner_id", filesystem.OwnerId) + d.Set("storage_capacity", filesystem.StorageCapacity) + + if err := d.Set("subnet_ids", aws.StringValueSlice(filesystem.SubnetIds)); err != nil { + return fmt.Errorf("error setting subnet_ids: %s", err) + } + + if err := d.Set("tags", tagsToMapFSX(filesystem.Tags)); err != nil { + return fmt.Errorf("error setting tags: %s", err) + } + + d.Set("vpc_id", filesystem.VpcId) + d.Set("weekly_maintenance_start_time", filesystem.LustreConfiguration.WeeklyMaintenanceStartTime) + + return nil +} + +func resourceAwsFsxLustreFileSystemDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).fsxconn + + request := &fsx.DeleteFileSystemInput{ + FileSystemId: aws.String(d.Id()), + } + + _, err := conn.DeleteFileSystem(request) + + if isAWSErr(err, fsx.ErrCodeFileSystemNotFound, "") { + return nil + } + + if err != nil { + return fmt.Errorf("Error deleting FSx filesystem: %s", err) + } + + log.Println("[DEBUG] Waiting for filesystem to delete") + + if err := waitForFsxFileSystemDeletion(conn, d.Id(), d.Timeout(schema.TimeoutDelete)); err != nil { + return fmt.Errorf("Error waiting for filesystem (%s) to delete: %s", d.Id(), err) + } + + return nil +} diff --git a/aws/resource_aws_fsx_lustre_file_system_test.go b/aws/resource_aws_fsx_lustre_file_system_test.go new file mode 100644 index 000000000000..94c6f17f204c --- /dev/null +++ b/aws/resource_aws_fsx_lustre_file_system_test.go @@ -0,0 +1,611 @@ +package aws + +import ( + "fmt" + "regexp" + "testing" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/fsx" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSFsxLustreFileSystem_basic(t *testing.T) { + var filesystem fsx.FileSystem + resourceName := "aws_fsx_lustre_file_system.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckFsxLustreFileSystemDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsFsxLustreFileSystemConfigSubnetIds1(), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxLustreFileSystemExists(resourceName, &filesystem), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "fsx", regexp.MustCompile(`file-system/fs-.+`)), + resource.TestMatchResourceAttr(resourceName, "dns_name", regexp.MustCompile(`fs-.+\.fsx\.`)), + resource.TestCheckResourceAttr(resourceName, "export_path", ""), + resource.TestCheckResourceAttr(resourceName, "import_path", ""), + resource.TestCheckResourceAttr(resourceName, "imported_file_chunk_size", "0"), + resource.TestCheckResourceAttr(resourceName, "network_interface_ids.#", "2"), + testAccCheckResourceAttrAccountID(resourceName, "owner_id"), + resource.TestCheckResourceAttr(resourceName, "storage_capacity", "3600"), + resource.TestCheckResourceAttr(resourceName, "subnet_ids.#", "1"), + resource.TestCheckResourceAttr(resourceName, "security_group_ids.#", "0"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestMatchResourceAttr(resourceName, "vpc_id", regexp.MustCompile(`^vpc-.+`)), + resource.TestMatchResourceAttr(resourceName, "weekly_maintenance_start_time", regexp.MustCompile(`^\d:\d\d:\d\d$`)), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"security_group_ids"}, + }, + }, + }) +} + +func TestAccAWSFsxLustreFileSystem_disappears(t *testing.T) { + var filesystem fsx.FileSystem + resourceName := "aws_fsx_lustre_file_system.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckFsxLustreFileSystemDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsFsxLustreFileSystemConfigSubnetIds1(), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxLustreFileSystemExists(resourceName, &filesystem), + testAccCheckFsxLustreFileSystemDisappears(&filesystem), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAWSFsxLustreFileSystem_ExportPath(t *testing.T) { + var filesystem1, filesystem2 fsx.FileSystem + resourceName := "aws_fsx_lustre_file_system.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckFsxLustreFileSystemDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsFsxLustreFileSystemConfigExportPath(rName, ""), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxLustreFileSystemExists(resourceName, &filesystem1), + resource.TestCheckResourceAttr(resourceName, "export_path", fmt.Sprintf("s3://%s", rName)), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"security_group_ids"}, + }, + { + Config: testAccAwsFsxLustreFileSystemConfigExportPath(rName, "/prefix/"), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxLustreFileSystemExists(resourceName, &filesystem2), + testAccCheckFsxLustreFileSystemRecreated(&filesystem1, &filesystem2), + resource.TestCheckResourceAttr(resourceName, "export_path", fmt.Sprintf("s3://%s/prefix/", rName)), + ), + }, + }, + }) +} + +func TestAccAWSFsxLustreFileSystem_ImportPath(t *testing.T) { + var filesystem1, filesystem2 fsx.FileSystem + resourceName := "aws_fsx_lustre_file_system.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckFsxLustreFileSystemDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsFsxLustreFileSystemConfigImportPath(rName, ""), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxLustreFileSystemExists(resourceName, &filesystem1), + resource.TestCheckResourceAttr(resourceName, "import_path", fmt.Sprintf("s3://%s", rName)), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"security_group_ids"}, + }, + { + Config: testAccAwsFsxLustreFileSystemConfigImportPath(rName, "/prefix/"), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxLustreFileSystemExists(resourceName, &filesystem2), + testAccCheckFsxLustreFileSystemRecreated(&filesystem1, &filesystem2), + resource.TestCheckResourceAttr(resourceName, "import_path", fmt.Sprintf("s3://%s/prefix/", rName)), + ), + }, + }, + }) +} + +func TestAccAWSFsxLustreFileSystem_ImportedFileChunkSize(t *testing.T) { + var filesystem1, filesystem2 fsx.FileSystem + resourceName := "aws_fsx_lustre_file_system.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckFsxLustreFileSystemDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsFsxLustreFileSystemConfigImportedFileChunkSize(rName, 2048), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxLustreFileSystemExists(resourceName, &filesystem1), + resource.TestCheckResourceAttr(resourceName, "imported_file_chunk_size", "2048"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"security_group_ids"}, + }, + { + Config: testAccAwsFsxLustreFileSystemConfigImportedFileChunkSize(rName, 4096), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxLustreFileSystemExists(resourceName, &filesystem2), + testAccCheckFsxLustreFileSystemRecreated(&filesystem1, &filesystem2), + resource.TestCheckResourceAttr(resourceName, "imported_file_chunk_size", "4096"), + ), + }, + }, + }) +} + +func TestAccAWSFsxLustreFileSystem_SecurityGroupIds(t *testing.T) { + var filesystem1, filesystem2 fsx.FileSystem + resourceName := "aws_fsx_lustre_file_system.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckFsxLustreFileSystemDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsFsxLustreFileSystemConfigSecurityGroupIds1(), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxLustreFileSystemExists(resourceName, &filesystem1), + resource.TestCheckResourceAttr(resourceName, "security_group_ids.#", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"security_group_ids"}, + }, + { + Config: testAccAwsFsxLustreFileSystemConfigSecurityGroupIds2(), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxLustreFileSystemExists(resourceName, &filesystem2), + testAccCheckFsxLustreFileSystemRecreated(&filesystem1, &filesystem2), + resource.TestCheckResourceAttr(resourceName, "security_group_ids.#", "2"), + ), + }, + }, + }) +} + +func TestAccAWSFsxLustreFileSystem_StorageCapacity(t *testing.T) { + var filesystem1, filesystem2 fsx.FileSystem + resourceName := "aws_fsx_lustre_file_system.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckFsxLustreFileSystemDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsFsxLustreFileSystemConfigStorageCapacity(7200), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxLustreFileSystemExists(resourceName, &filesystem1), + resource.TestCheckResourceAttr(resourceName, "storage_capacity", "7200"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"security_group_ids"}, + }, + { + Config: testAccAwsFsxLustreFileSystemConfigStorageCapacity(3600), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxLustreFileSystemExists(resourceName, &filesystem2), + testAccCheckFsxLustreFileSystemRecreated(&filesystem1, &filesystem2), + resource.TestCheckResourceAttr(resourceName, "storage_capacity", "3600"), + ), + }, + }, + }) +} + +func TestAccAWSFsxLustreFileSystem_Tags(t *testing.T) { + var filesystem1, filesystem2, filesystem3 fsx.FileSystem + resourceName := "aws_fsx_lustre_file_system.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckFsxLustreFileSystemDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsFsxLustreFileSystemConfigTags1("key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxLustreFileSystemExists(resourceName, &filesystem1), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"security_group_ids"}, + }, + { + Config: testAccAwsFsxLustreFileSystemConfigTags2("key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxLustreFileSystemExists(resourceName, &filesystem2), + testAccCheckFsxLustreFileSystemNotRecreated(&filesystem1, &filesystem2), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccAwsFsxLustreFileSystemConfigTags1("key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxLustreFileSystemExists(resourceName, &filesystem3), + testAccCheckFsxLustreFileSystemNotRecreated(&filesystem2, &filesystem3), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + }, + }) +} + +func TestAccAWSFsxLustreFileSystem_WeeklyMaintenanceStartTime(t *testing.T) { + var filesystem1, filesystem2 fsx.FileSystem + resourceName := "aws_fsx_lustre_file_system.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckFsxLustreFileSystemDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsFsxLustreFileSystemConfigWeeklyMaintenanceStartTime("1:01:01"), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxLustreFileSystemExists(resourceName, &filesystem1), + resource.TestCheckResourceAttr(resourceName, "weekly_maintenance_start_time", "1:01:01"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"security_group_ids"}, + }, + { + Config: testAccAwsFsxLustreFileSystemConfigWeeklyMaintenanceStartTime("2:02:02"), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxLustreFileSystemExists(resourceName, &filesystem2), + testAccCheckFsxLustreFileSystemNotRecreated(&filesystem1, &filesystem2), + resource.TestCheckResourceAttr(resourceName, "weekly_maintenance_start_time", "2:02:02"), + ), + }, + }, + }) +} + +func testAccCheckFsxLustreFileSystemExists(resourceName string, fs *fsx.FileSystem) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + conn := testAccProvider.Meta().(*AWSClient).fsxconn + + filesystem, err := describeFsxFileSystem(conn, rs.Primary.ID) + + if err != nil { + return err + } + + if filesystem == nil { + return fmt.Errorf("FSx File System (%s) not found", rs.Primary.ID) + } + + *fs = *filesystem + + return nil + } +} + +func testAccCheckFsxLustreFileSystemDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).fsxconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_fsx_lustre_file_system" { + continue + } + + filesystem, err := describeFsxFileSystem(conn, rs.Primary.ID) + + if isAWSErr(err, fsx.ErrCodeFileSystemNotFound, "") { + continue + } + + if err != nil { + return err + } + + if filesystem != nil { + return fmt.Errorf("FSx File System (%s) still exists", rs.Primary.ID) + } + } + return nil +} + +func testAccCheckFsxLustreFileSystemDisappears(filesystem *fsx.FileSystem) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).fsxconn + + input := &fsx.DeleteFileSystemInput{ + FileSystemId: filesystem.FileSystemId, + } + + _, err := conn.DeleteFileSystem(input) + + if err != nil { + return err + } + + return waitForFsxFileSystemDeletion(conn, aws.StringValue(filesystem.FileSystemId), 30*time.Minute) + } +} + +func testAccCheckFsxLustreFileSystemNotRecreated(i, j *fsx.FileSystem) resource.TestCheckFunc { + return func(s *terraform.State) error { + if aws.StringValue(i.FileSystemId) != aws.StringValue(j.FileSystemId) { + return fmt.Errorf("FSx File System (%s) recreated", aws.StringValue(i.FileSystemId)) + } + + return nil + } +} + +func testAccCheckFsxLustreFileSystemRecreated(i, j *fsx.FileSystem) resource.TestCheckFunc { + return func(s *terraform.State) error { + if aws.StringValue(i.FileSystemId) == aws.StringValue(j.FileSystemId) { + return fmt.Errorf("FSx File System (%s) not recreated", aws.StringValue(i.FileSystemId)) + } + + return nil + } +} + +func testAccAwsFsxLustreFileSystemConfigBase() string { + return fmt.Sprintf(` +data "aws_availability_zones" "available" { + state = "available" +} + +resource "aws_vpc" "test" { + cidr_block = "10.0.0.0/16" +} + +resource "aws_subnet" "test1" { + vpc_id = "${aws_vpc.test.id}" + cidr_block = "10.0.1.0/24" + availability_zone = "${data.aws_availability_zones.available.names[0]}" +} +`) +} + +func testAccAwsFsxLustreFileSystemConfigExportPath(rName, exportPrefix string) string { + return testAccAwsFsxLustreFileSystemConfigBase() + fmt.Sprintf(` +resource "aws_s3_bucket" "test" { + acl = "private" + bucket = %[1]q +} + +resource "aws_fsx_lustre_file_system" "test" { + export_path = "s3://${aws_s3_bucket.test.bucket}%[2]s" + import_path = "s3://${aws_s3_bucket.test.bucket}" + storage_capacity = 3600 + subnet_ids = ["${aws_subnet.test1.id}"] +} +`, rName, exportPrefix) +} + +func testAccAwsFsxLustreFileSystemConfigImportPath(rName, importPrefix string) string { + return testAccAwsFsxLustreFileSystemConfigBase() + fmt.Sprintf(` +resource "aws_s3_bucket" "test" { + acl = "private" + bucket = %[1]q +} + +resource "aws_fsx_lustre_file_system" "test" { + import_path = "s3://${aws_s3_bucket.test.bucket}%[2]s" + storage_capacity = 3600 + subnet_ids = ["${aws_subnet.test1.id}"] +} +`, rName, importPrefix) +} + +func testAccAwsFsxLustreFileSystemConfigImportedFileChunkSize(rName string, importedFileChunkSize int) string { + return testAccAwsFsxLustreFileSystemConfigBase() + fmt.Sprintf(` +resource "aws_s3_bucket" "test" { + acl = "private" + bucket = %[1]q +} + +resource "aws_fsx_lustre_file_system" "test" { + import_path = "s3://${aws_s3_bucket.test.bucket}" + imported_file_chunk_size = %[2]d + storage_capacity = 3600 + subnet_ids = ["${aws_subnet.test1.id}"] +} +`, rName, importedFileChunkSize) +} + +func testAccAwsFsxLustreFileSystemConfigSecurityGroupIds1() string { + return testAccAwsFsxLustreFileSystemConfigBase() + fmt.Sprintf(` +resource "aws_security_group" "test1" { + description = "security group for FSx testing" + vpc_id = "${aws_vpc.test.id}" + + ingress { + cidr_blocks = ["${aws_vpc.test.cidr_block}"] + from_port = 0 + protocol = -1 + to_port = 0 + } + + egress { + cidr_blocks = ["0.0.0.0/0"] + from_port = 0 + protocol = "-1" + to_port = 0 + } +} + +resource "aws_fsx_lustre_file_system" "test" { + security_group_ids = ["${aws_security_group.test1.id}"] + storage_capacity = 3600 + subnet_ids = ["${aws_subnet.test1.id}"] +} +`) +} + +func testAccAwsFsxLustreFileSystemConfigSecurityGroupIds2() string { + return testAccAwsFsxLustreFileSystemConfigBase() + fmt.Sprintf(` +resource "aws_security_group" "test1" { + description = "security group for FSx testing" + vpc_id = "${aws_vpc.test.id}" + + ingress { + cidr_blocks = ["${aws_vpc.test.cidr_block}"] + from_port = 0 + protocol = -1 + to_port = 0 + } + + egress { + cidr_blocks = ["0.0.0.0/0"] + from_port = 0 + protocol = "-1" + to_port = 0 + } +} + +resource "aws_security_group" "test2" { + description = "security group for FSx testing" + vpc_id = "${aws_vpc.test.id}" + + ingress { + cidr_blocks = ["${aws_vpc.test.cidr_block}"] + from_port = 0 + protocol = -1 + to_port = 0 + } + + egress { + cidr_blocks = ["0.0.0.0/0"] + from_port = 0 + protocol = "-1" + to_port = 0 + } +} + +resource "aws_fsx_lustre_file_system" "test" { + security_group_ids = ["${aws_security_group.test1.id}", "${aws_security_group.test2.id}"] + storage_capacity = 3600 + subnet_ids = ["${aws_subnet.test1.id}"] +} +`) +} + +func testAccAwsFsxLustreFileSystemConfigStorageCapacity(storageCapacity int) string { + return testAccAwsFsxLustreFileSystemConfigBase() + fmt.Sprintf(` +resource "aws_fsx_lustre_file_system" "test" { + storage_capacity = %[1]d + subnet_ids = ["${aws_subnet.test1.id}"] +} +`, storageCapacity) +} + +func testAccAwsFsxLustreFileSystemConfigSubnetIds1() string { + return testAccAwsFsxLustreFileSystemConfigBase() + fmt.Sprintf(` +resource "aws_fsx_lustre_file_system" "test" { + storage_capacity = 3600 + subnet_ids = ["${aws_subnet.test1.id}"] +} +`) +} + +func testAccAwsFsxLustreFileSystemConfigTags1(tagKey1, tagValue1 string) string { + return testAccAwsFsxLustreFileSystemConfigBase() + fmt.Sprintf(` +resource "aws_fsx_lustre_file_system" "test" { + storage_capacity = 3600 + subnet_ids = ["${aws_subnet.test1.id}"] + + tags = { + %[1]q = %[2]q + } +} +`, tagKey1, tagValue1) +} + +func testAccAwsFsxLustreFileSystemConfigTags2(tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return testAccAwsFsxLustreFileSystemConfigBase() + fmt.Sprintf(` +resource "aws_fsx_lustre_file_system" "test" { + storage_capacity = 3600 + subnet_ids = ["${aws_subnet.test1.id}"] + + tags = { + %[1]q = %[2]q + %[3]q = %[4]q + } +} +`, tagKey1, tagValue1, tagKey2, tagValue2) +} + +func testAccAwsFsxLustreFileSystemConfigWeeklyMaintenanceStartTime(weeklyMaintenanceStartTime string) string { + return testAccAwsFsxLustreFileSystemConfigBase() + fmt.Sprintf(` +resource "aws_fsx_lustre_file_system" "test" { + storage_capacity = 3600 + subnet_ids = ["${aws_subnet.test1.id}"] + weekly_maintenance_start_time = %[1]q +} +`, weeklyMaintenanceStartTime) +} diff --git a/aws/resource_aws_fsx_windows_file_system.go b/aws/resource_aws_fsx_windows_file_system.go new file mode 100644 index 000000000000..b29112a1d9f4 --- /dev/null +++ b/aws/resource_aws_fsx_windows_file_system.go @@ -0,0 +1,439 @@ +package aws + +import ( + "fmt" + "log" + "regexp" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/fsx" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" +) + +func resourceAwsFsxWindowsFileSystem() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsFsxWindowsFileSystemCreate, + Read: resourceAwsFsxWindowsFileSystemRead, + Update: resourceAwsFsxWindowsFileSystemUpdate, + Delete: resourceAwsFsxWindowsFileSystemDelete, + Importer: &schema.ResourceImporter{ + State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + d.Set("skip_final_backup", false) + + return []*schema.ResourceData{d}, nil + }, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(30 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "active_directory_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ConflictsWith: []string{"self_managed_active_directory"}, + }, + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "automatic_backup_retention_days": { + Type: schema.TypeInt, + Optional: true, + Default: 7, + ValidateFunc: validation.IntBetween(0, 35), + }, + "copy_tags_to_backups": { + Type: schema.TypeBool, + Optional: true, + Default: false, + ForceNew: true, + }, + "daily_automatic_backup_start_time": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.All( + validation.StringLenBetween(5, 5), + validation.StringMatch(regexp.MustCompile(`^([01]\d|2[0-3]):?([0-5]\d)$`), "must be in the format HH:MM"), + ), + }, + "dns_name": { + Type: schema.TypeString, + Computed: true, + }, + "kms_key_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validateArn, + }, + "network_interface_ids": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "owner_id": { + Type: schema.TypeString, + Computed: true, + }, + "security_group_ids": { + Type: schema.TypeSet, + Optional: true, + ForceNew: true, + MaxItems: 50, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "self_managed_active_directory": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + ConflictsWith: []string{"active_directory_id"}, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "dns_ips": { + Type: schema.TypeSet, + Required: true, + MinItems: 1, + MaxItems: 2, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "domain_name": { + Type: schema.TypeString, + Required: true, + }, + "file_system_administrators_group": { + Type: schema.TypeString, + Optional: true, + Default: "Domain Admins", + ValidateFunc: validation.StringLenBetween(1, 256), + }, + "organizational_unit_distinguished_name": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringLenBetween(1, 2000), + }, + "password": { + Type: schema.TypeString, + Required: true, + Sensitive: true, + ValidateFunc: validation.StringLenBetween(1, 256), + }, + "username": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 256), + }, + }, + }, + }, + "skip_final_backup": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "storage_capacity": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + ValidateFunc: validation.IntBetween(300, 65536), + }, + "subnet_ids": { + Type: schema.TypeSet, + Required: true, + ForceNew: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "tags": tagsSchema(), + "throughput_capacity": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + ValidateFunc: validation.IntBetween(8, 2048), + }, + "vpc_id": { + Type: schema.TypeString, + Computed: true, + }, + "weekly_maintenance_start_time": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.All( + validation.StringLenBetween(7, 7), + validation.StringMatch(regexp.MustCompile(`^[1-7]:([01]\d|2[0-3]):?([0-5]\d)$`), "must be in the format d:HH:MM"), + ), + }, + }, + } +} + +func resourceAwsFsxWindowsFileSystemCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).fsxconn + + input := &fsx.CreateFileSystemInput{ + ClientRequestToken: aws.String(resource.UniqueId()), + FileSystemType: aws.String(fsx.FileSystemTypeWindows), + StorageCapacity: aws.Int64(int64(d.Get("storage_capacity").(int))), + SubnetIds: expandStringSet(d.Get("subnet_ids").(*schema.Set)), + WindowsConfiguration: &fsx.CreateFileSystemWindowsConfiguration{ + AutomaticBackupRetentionDays: aws.Int64(int64(d.Get("automatic_backup_retention_days").(int))), + CopyTagsToBackups: aws.Bool(d.Get("copy_tags_to_backups").(bool)), + ThroughputCapacity: aws.Int64(int64(d.Get("throughput_capacity").(int))), + }, + } + + if v, ok := d.GetOk("active_directory_id"); ok { + input.WindowsConfiguration.ActiveDirectoryId = aws.String(v.(string)) + } + + if v, ok := d.GetOk("daily_automatic_backup_start_time"); ok { + input.WindowsConfiguration.DailyAutomaticBackupStartTime = aws.String(v.(string)) + } + + if v, ok := d.GetOk("kms_key_id"); ok { + input.KmsKeyId = aws.String(v.(string)) + } + + if v, ok := d.GetOk("security_group_ids"); ok { + input.SecurityGroupIds = expandStringSet(v.(*schema.Set)) + } + + if v, ok := d.GetOk("self_managed_active_directory"); ok { + input.WindowsConfiguration.SelfManagedActiveDirectoryConfiguration = expandFsxSelfManagedActiveDirectoryConfigurationCreate(v.([]interface{})) + } + + if v, ok := d.GetOk("tags"); ok { + input.Tags = tagsFromMapFSX(v.(map[string]interface{})) + } + + if v, ok := d.GetOk("weekly_maintenance_start_time"); ok { + input.WindowsConfiguration.WeeklyMaintenanceStartTime = aws.String(v.(string)) + } + + result, err := conn.CreateFileSystem(input) + if err != nil { + return fmt.Errorf("Error creating FSx filesystem: %s", err) + } + + d.SetId(*result.FileSystem.FileSystemId) + + log.Println("[DEBUG] Waiting for filesystem to become available") + + if err := waitForFsxFileSystemCreation(conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { + return fmt.Errorf("Error waiting for filesystem (%s) to become available: %s", d.Id(), err) + } + + return resourceAwsFsxWindowsFileSystemRead(d, meta) +} + +func resourceAwsFsxWindowsFileSystemUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).fsxconn + + if d.HasChange("tags") { + if err := setTagsFSX(conn, d); err != nil { + return fmt.Errorf("Error updating tags for FSx filesystem: %s", err) + } + } + + requestUpdate := false + input := &fsx.UpdateFileSystemInput{ + ClientRequestToken: aws.String(resource.UniqueId()), + FileSystemId: aws.String(d.Id()), + WindowsConfiguration: &fsx.UpdateFileSystemWindowsConfiguration{}, + } + + if d.HasChange("automatic_backup_retention_days") { + input.WindowsConfiguration.AutomaticBackupRetentionDays = aws.Int64(int64(d.Get("automatic_backup_retention_days").(int))) + requestUpdate = true + } + + if d.HasChange("daily_automatic_backup_start_time") { + input.WindowsConfiguration.DailyAutomaticBackupStartTime = aws.String(d.Get("daily_automatic_backup_start_time").(string)) + requestUpdate = true + } + + if d.HasChange("self_managed_active_directory") { + input.WindowsConfiguration.SelfManagedActiveDirectoryConfiguration = expandFsxSelfManagedActiveDirectoryConfigurationUpdate(d.Get("self_managed_active_directory").([]interface{})) + requestUpdate = true + } + + if d.HasChange("weekly_maintenance_start_time") { + input.WindowsConfiguration.WeeklyMaintenanceStartTime = aws.String(d.Get("weekly_maintenance_start_time").(string)) + requestUpdate = true + } + + if requestUpdate { + _, err := conn.UpdateFileSystem(input) + if err != nil { + return fmt.Errorf("error updating FSX File System (%s): %s", d.Id(), err) + } + } + + return resourceAwsFsxWindowsFileSystemRead(d, meta) +} + +func resourceAwsFsxWindowsFileSystemRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).fsxconn + + filesystem, err := describeFsxFileSystem(conn, d.Id()) + + if isAWSErr(err, fsx.ErrCodeFileSystemNotFound, "") { + log.Printf("[WARN] FSx File System (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("Error reading FSx File System (%s): %s", d.Id(), err) + } + + if filesystem == nil { + log.Printf("[WARN] FSx File System (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if filesystem.LustreConfiguration != nil { + return fmt.Errorf("expected FSx Windows File System, found FSx Lustre File System: %s", d.Id()) + } + + if filesystem.WindowsConfiguration == nil { + return fmt.Errorf("error describing FSx Windows File System (%s): empty Windows configuration", d.Id()) + } + + d.Set("active_directory_id", filesystem.WindowsConfiguration.ActiveDirectoryId) + d.Set("arn", filesystem.ResourceARN) + d.Set("automatic_backup_retention_days", filesystem.WindowsConfiguration.AutomaticBackupRetentionDays) + d.Set("copy_tags_to_backups", filesystem.WindowsConfiguration.CopyTagsToBackups) + d.Set("daily_automatic_backup_start_time", filesystem.WindowsConfiguration.DailyAutomaticBackupStartTime) + d.Set("dns_name", filesystem.DNSName) + d.Set("kms_key_id", filesystem.KmsKeyId) + + if err := d.Set("network_interface_ids", aws.StringValueSlice(filesystem.NetworkInterfaceIds)); err != nil { + return fmt.Errorf("error setting network_interface_ids: %s", err) + } + + d.Set("owner_id", filesystem.OwnerId) + + if err := d.Set("self_managed_active_directory", flattenFsxSelfManagedActiveDirectoryConfiguration(d, filesystem.WindowsConfiguration.SelfManagedActiveDirectoryConfiguration)); err != nil { + return fmt.Errorf("error setting self_managed_active_directory: %s", err) + } + + d.Set("storage_capacity", filesystem.StorageCapacity) + + if err := d.Set("subnet_ids", aws.StringValueSlice(filesystem.SubnetIds)); err != nil { + return fmt.Errorf("error setting subnet_ids: %s", err) + } + + if err := d.Set("tags", tagsToMapFSX(filesystem.Tags)); err != nil { + return fmt.Errorf("error setting tags: %s", err) + } + + d.Set("throughput_capacity", filesystem.WindowsConfiguration.ThroughputCapacity) + d.Set("vpc_id", filesystem.VpcId) + d.Set("weekly_maintenance_start_time", filesystem.WindowsConfiguration.WeeklyMaintenanceStartTime) + + return nil +} + +func resourceAwsFsxWindowsFileSystemDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).fsxconn + + input := &fsx.DeleteFileSystemInput{ + ClientRequestToken: aws.String(resource.UniqueId()), + FileSystemId: aws.String(d.Id()), + WindowsConfiguration: &fsx.DeleteFileSystemWindowsConfiguration{ + SkipFinalBackup: aws.Bool(d.Get("skip_final_backup").(bool)), + }, + } + + _, err := conn.DeleteFileSystem(input) + + if isAWSErr(err, fsx.ErrCodeFileSystemNotFound, "") { + return nil + } + + if err != nil { + return fmt.Errorf("Error deleting FSx filesystem: %s", err) + } + + log.Println("[DEBUG] Waiting for filesystem to delete") + + if err := waitForFsxFileSystemDeletion(conn, d.Id(), d.Timeout(schema.TimeoutDelete)); err != nil { + return fmt.Errorf("Error waiting for filesystem (%s) to delete: %s", d.Id(), err) + } + + return nil +} + +func expandFsxSelfManagedActiveDirectoryConfigurationCreate(l []interface{}) *fsx.SelfManagedActiveDirectoryConfiguration { + if len(l) == 0 || l[0] == nil { + return nil + } + + data := l[0].(map[string]interface{}) + req := &fsx.SelfManagedActiveDirectoryConfiguration{ + DomainName: aws.String(data["domain_name"].(string)), + DnsIps: expandStringSet(data["dns_ips"].(*schema.Set)), + Password: aws.String(data["password"].(string)), + UserName: aws.String(data["username"].(string)), + } + + if v, ok := data["file_system_administrators_group"]; ok && v.(string) != "" { + req.FileSystemAdministratorsGroup = aws.String(v.(string)) + } + + if v, ok := data["organizational_unit_distinguished_name"]; ok && v.(string) != "" { + req.OrganizationalUnitDistinguishedName = aws.String(v.(string)) + } + + return req +} + +func expandFsxSelfManagedActiveDirectoryConfigurationUpdate(l []interface{}) *fsx.SelfManagedActiveDirectoryConfigurationUpdates { + if len(l) == 0 || l[0] == nil { + return nil + } + + data := l[0].(map[string]interface{}) + req := &fsx.SelfManagedActiveDirectoryConfigurationUpdates{ + DnsIps: expandStringList(data["dns_ips"].([]interface{})), + Password: aws.String(data["password"].(string)), + UserName: aws.String(data["username"].(string)), + } + + return req +} + +func flattenFsxSelfManagedActiveDirectoryConfiguration(d *schema.ResourceData, adopts *fsx.SelfManagedActiveDirectoryAttributes) []map[string]interface{} { + if adopts == nil { + return []map[string]interface{}{} + } + + // Since we are in a configuration block and the FSx API does not return + // the password, we need to set the value if we can or Terraform will + // show a difference for the argument from empty string to the value. + // This is not a pattern that should be used normally. + // See also: flattenEmrKerberosAttributes + + m := map[string]interface{}{ + "dns_ips": aws.StringValueSlice(adopts.DnsIps), + "domain_name": aws.StringValue(adopts.DomainName), + "file_system_administrators_group": aws.StringValue(adopts.FileSystemAdministratorsGroup), + "organizational_unit_distinguished_name": aws.StringValue(adopts.OrganizationalUnitDistinguishedName), + "password": d.Get("self_managed_active_directory.0.password").(string), + "username": aws.StringValue(adopts.UserName), + } + + return []map[string]interface{}{m} +} diff --git a/aws/resource_aws_fsx_windows_file_system_test.go b/aws/resource_aws_fsx_windows_file_system_test.go new file mode 100644 index 000000000000..01e0a1733c27 --- /dev/null +++ b/aws/resource_aws_fsx_windows_file_system_test.go @@ -0,0 +1,846 @@ +package aws + +import ( + "fmt" + "regexp" + "testing" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/fsx" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSFsxWindowsFileSystem_basic(t *testing.T) { + var filesystem fsx.FileSystem + resourceName := "aws_fsx_windows_file_system.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckFsxWindowsFileSystemDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsFsxWindowsFileSystemConfigSubnetIds1(), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "fsx", regexp.MustCompile(`file-system/fs-.+`)), + resource.TestCheckResourceAttr(resourceName, "automatic_backup_retention_days", "7"), + resource.TestCheckResourceAttr(resourceName, "copy_tags_to_backups", "false"), + resource.TestMatchResourceAttr(resourceName, "daily_automatic_backup_start_time", regexp.MustCompile(`^\d\d:\d\d$`)), + resource.TestMatchResourceAttr(resourceName, "dns_name", regexp.MustCompile(`fs-.+\..+`)), + resource.TestMatchResourceAttr(resourceName, "kms_key_id", regexp.MustCompile(`^arn:`)), + resource.TestCheckResourceAttr(resourceName, "network_interface_ids.#", "1"), + testAccCheckResourceAttrAccountID(resourceName, "owner_id"), + resource.TestCheckResourceAttr(resourceName, "security_group_ids.#", "0"), + resource.TestCheckResourceAttr(resourceName, "self_managed_active_directory.#", "0"), + resource.TestCheckResourceAttr(resourceName, "skip_final_backup", "true"), + resource.TestCheckResourceAttr(resourceName, "storage_capacity", "300"), + resource.TestCheckResourceAttr(resourceName, "subnet_ids.#", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "throughput_capacity", "8"), + resource.TestMatchResourceAttr(resourceName, "vpc_id", regexp.MustCompile(`^vpc-.+`)), + resource.TestMatchResourceAttr(resourceName, "weekly_maintenance_start_time", regexp.MustCompile(`^\d:\d\d:\d\d$`)), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "security_group_ids", + "skip_final_backup", + }, + }, + }, + }) +} + +func TestAccAWSFsxWindowsFileSystem_disappears(t *testing.T) { + var filesystem fsx.FileSystem + resourceName := "aws_fsx_windows_file_system.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckFsxWindowsFileSystemDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsFsxWindowsFileSystemConfigSubnetIds1(), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem), + testAccCheckFsxWindowsFileSystemDisappears(&filesystem), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAWSFsxWindowsFileSystem_AutomaticBackupRetentionDays(t *testing.T) { + var filesystem1, filesystem2, filesystem3 fsx.FileSystem + resourceName := "aws_fsx_windows_file_system.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckFsxWindowsFileSystemDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsFsxWindowsFileSystemConfigAutomaticBackupRetentionDays(35), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem1), + resource.TestCheckResourceAttr(resourceName, "automatic_backup_retention_days", "35"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "security_group_ids", + "skip_final_backup", + }, + }, + { + Config: testAccAwsFsxWindowsFileSystemConfigAutomaticBackupRetentionDays(0), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem2), + testAccCheckFsxWindowsFileSystemNotRecreated(&filesystem1, &filesystem2), + resource.TestCheckResourceAttr(resourceName, "automatic_backup_retention_days", "0"), + ), + }, + { + Config: testAccAwsFsxWindowsFileSystemConfigAutomaticBackupRetentionDays(14), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem3), + testAccCheckFsxWindowsFileSystemNotRecreated(&filesystem2, &filesystem3), + resource.TestCheckResourceAttr(resourceName, "automatic_backup_retention_days", "14"), + ), + }, + }, + }) +} + +func TestAccAWSFsxWindowsFileSystem_CopyTagsToBackups(t *testing.T) { + var filesystem1, filesystem2 fsx.FileSystem + resourceName := "aws_fsx_windows_file_system.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckFsxWindowsFileSystemDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsFsxWindowsFileSystemConfigCopyTagsToBackups(true), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem1), + resource.TestCheckResourceAttr(resourceName, "copy_tags_to_backups", "true"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "security_group_ids", + "skip_final_backup", + }, + }, + { + Config: testAccAwsFsxWindowsFileSystemConfigCopyTagsToBackups(false), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem2), + testAccCheckFsxWindowsFileSystemRecreated(&filesystem1, &filesystem2), + resource.TestCheckResourceAttr(resourceName, "copy_tags_to_backups", "false"), + ), + }, + }, + }) +} + +func TestAccAWSFsxWindowsFileSystem_DailyAutomaticBackupStartTime(t *testing.T) { + var filesystem1, filesystem2 fsx.FileSystem + resourceName := "aws_fsx_windows_file_system.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckFsxWindowsFileSystemDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsFsxWindowsFileSystemConfigDailyAutomaticBackupStartTime("01:01"), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem1), + resource.TestCheckResourceAttr(resourceName, "daily_automatic_backup_start_time", "01:01"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "security_group_ids", + "skip_final_backup", + }, + }, + { + Config: testAccAwsFsxWindowsFileSystemConfigDailyAutomaticBackupStartTime("02:02"), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem2), + testAccCheckFsxWindowsFileSystemNotRecreated(&filesystem1, &filesystem2), + resource.TestCheckResourceAttr(resourceName, "daily_automatic_backup_start_time", "02:02"), + ), + }, + }, + }) +} + +func TestAccAWSFsxWindowsFileSystem_KmsKeyId(t *testing.T) { + var filesystem1, filesystem2 fsx.FileSystem + kmsKeyResourceName1 := "aws_kms_key.test1" + kmsKeyResourceName2 := "aws_kms_key.test2" + resourceName := "aws_fsx_windows_file_system.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckFsxWindowsFileSystemDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsFsxWindowsFileSystemConfigKmsKeyId1(), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem1), + resource.TestCheckResourceAttrPair(resourceName, "kms_key_id", kmsKeyResourceName1, "arn"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "security_group_ids", + "skip_final_backup", + }, + }, + { + Config: testAccAwsFsxWindowsFileSystemConfigKmsKeyId2(), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem2), + testAccCheckFsxWindowsFileSystemRecreated(&filesystem1, &filesystem2), + resource.TestCheckResourceAttrPair(resourceName, "kms_key_id", kmsKeyResourceName2, "arn"), + ), + }, + }, + }) +} + +func TestAccAWSFsxWindowsFileSystem_SecurityGroupIds(t *testing.T) { + var filesystem1, filesystem2 fsx.FileSystem + resourceName := "aws_fsx_windows_file_system.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckFsxWindowsFileSystemDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsFsxWindowsFileSystemConfigSecurityGroupIds1(), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem1), + resource.TestCheckResourceAttr(resourceName, "security_group_ids.#", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "security_group_ids", + "skip_final_backup", + }, + }, + { + Config: testAccAwsFsxWindowsFileSystemConfigSecurityGroupIds2(), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem2), + testAccCheckFsxWindowsFileSystemRecreated(&filesystem1, &filesystem2), + resource.TestCheckResourceAttr(resourceName, "security_group_ids.#", "2"), + ), + }, + }, + }) +} + +func TestAccAWSFsxWindowsFileSystem_SelfManagedActiveDirectory(t *testing.T) { + var filesystem fsx.FileSystem + resourceName := "aws_fsx_windows_file_system.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckFsxWindowsFileSystemDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsFsxWindowsFileSystemConfigSelfManagedActiveDirectory(), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem), + resource.TestCheckResourceAttr(resourceName, "self_managed_active_directory.#", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "security_group_ids", + "self_managed_active_directory", + "skip_final_backup", + }, + }, + }, + }) +} + +func TestAccAWSFsxWindowsFileSystem_StorageCapacity(t *testing.T) { + var filesystem1, filesystem2 fsx.FileSystem + resourceName := "aws_fsx_windows_file_system.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckFsxWindowsFileSystemDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsFsxWindowsFileSystemConfigStorageCapacity(301), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem1), + resource.TestCheckResourceAttr(resourceName, "storage_capacity", "301"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "security_group_ids", + "skip_final_backup", + }, + }, + { + Config: testAccAwsFsxWindowsFileSystemConfigStorageCapacity(302), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem2), + testAccCheckFsxWindowsFileSystemRecreated(&filesystem1, &filesystem2), + resource.TestCheckResourceAttr(resourceName, "storage_capacity", "302"), + ), + }, + }, + }) +} + +func TestAccAWSFsxWindowsFileSystem_Tags(t *testing.T) { + var filesystem1, filesystem2, filesystem3 fsx.FileSystem + resourceName := "aws_fsx_windows_file_system.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckFsxWindowsFileSystemDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsFsxWindowsFileSystemConfigTags1("key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem1), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "security_group_ids", + "skip_final_backup", + }, + }, + { + Config: testAccAwsFsxWindowsFileSystemConfigTags2("key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem2), + testAccCheckFsxWindowsFileSystemNotRecreated(&filesystem1, &filesystem2), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccAwsFsxWindowsFileSystemConfigTags1("key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem3), + testAccCheckFsxWindowsFileSystemNotRecreated(&filesystem2, &filesystem3), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + }, + }) +} + +func TestAccAWSFsxWindowsFileSystem_ThroughputCapacity(t *testing.T) { + var filesystem1, filesystem2 fsx.FileSystem + resourceName := "aws_fsx_windows_file_system.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckFsxWindowsFileSystemDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsFsxWindowsFileSystemConfigThroughputCapacity(16), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem1), + resource.TestCheckResourceAttr(resourceName, "throughput_capacity", "16"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "security_group_ids", + "skip_final_backup", + }, + }, + { + Config: testAccAwsFsxWindowsFileSystemConfigThroughputCapacity(32), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem2), + testAccCheckFsxWindowsFileSystemRecreated(&filesystem1, &filesystem2), + resource.TestCheckResourceAttr(resourceName, "throughput_capacity", "32"), + ), + }, + }, + }) +} + +func TestAccAWSFsxWindowsFileSystem_WeeklyMaintenanceStartTime(t *testing.T) { + var filesystem1, filesystem2 fsx.FileSystem + resourceName := "aws_fsx_windows_file_system.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckFsxWindowsFileSystemDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsFsxWindowsFileSystemConfigWeeklyMaintenanceStartTime("1:01:01"), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem1), + resource.TestCheckResourceAttr(resourceName, "weekly_maintenance_start_time", "1:01:01"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "security_group_ids", + "skip_final_backup", + }, + }, + { + Config: testAccAwsFsxWindowsFileSystemConfigWeeklyMaintenanceStartTime("2:02:02"), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem2), + testAccCheckFsxWindowsFileSystemNotRecreated(&filesystem1, &filesystem2), + resource.TestCheckResourceAttr(resourceName, "weekly_maintenance_start_time", "2:02:02"), + ), + }, + }, + }) +} + +func testAccCheckFsxWindowsFileSystemExists(resourceName string, fs *fsx.FileSystem) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + conn := testAccProvider.Meta().(*AWSClient).fsxconn + + filesystem, err := describeFsxFileSystem(conn, rs.Primary.ID) + + if err != nil { + return err + } + + if filesystem == nil { + return fmt.Errorf("FSx File System (%s) not found", rs.Primary.ID) + } + + *fs = *filesystem + + return nil + } +} + +func testAccCheckFsxWindowsFileSystemDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).fsxconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_fsx_windows_file_system" { + continue + } + + filesystem, err := describeFsxFileSystem(conn, rs.Primary.ID) + + if isAWSErr(err, fsx.ErrCodeFileSystemNotFound, "") { + continue + } + + if err != nil { + return err + } + + if filesystem != nil { + return fmt.Errorf("FSx File System (%s) still exists", rs.Primary.ID) + } + } + return nil +} + +func testAccCheckFsxWindowsFileSystemDisappears(filesystem *fsx.FileSystem) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).fsxconn + + input := &fsx.DeleteFileSystemInput{ + FileSystemId: filesystem.FileSystemId, + } + + _, err := conn.DeleteFileSystem(input) + + if err != nil { + return err + } + + return waitForFsxFileSystemDeletion(conn, aws.StringValue(filesystem.FileSystemId), 30*time.Minute) + } +} + +func testAccCheckFsxWindowsFileSystemNotRecreated(i, j *fsx.FileSystem) resource.TestCheckFunc { + return func(s *terraform.State) error { + if aws.StringValue(i.FileSystemId) != aws.StringValue(j.FileSystemId) { + return fmt.Errorf("FSx File System (%s) recreated", aws.StringValue(i.FileSystemId)) + } + + return nil + } +} + +func testAccCheckFsxWindowsFileSystemRecreated(i, j *fsx.FileSystem) resource.TestCheckFunc { + return func(s *terraform.State) error { + if aws.StringValue(i.FileSystemId) == aws.StringValue(j.FileSystemId) { + return fmt.Errorf("FSx File System (%s) not recreated", aws.StringValue(i.FileSystemId)) + } + + return nil + } +} + +func testAccAwsFsxWindowsFileSystemConfigBase() string { + return fmt.Sprintf(` +data "aws_availability_zones" "available" { + state = "available" +} + +resource "aws_vpc" "test" { + cidr_block = "10.0.0.0/16" +} + +resource "aws_subnet" "test1" { + vpc_id = "${aws_vpc.test.id}" + cidr_block = "10.0.1.0/24" + availability_zone = "${data.aws_availability_zones.available.names[0]}" +} + +resource "aws_subnet" "test2" { + vpc_id = "${aws_vpc.test.id}" + cidr_block = "10.0.2.0/24" + availability_zone = "${data.aws_availability_zones.available.names[1]}" +} + +resource "aws_directory_service_directory" "test" { + edition = "Standard" + name = "corp.notexample.com" + password = "SuperSecretPassw0rd" + type = "MicrosoftAD" + + vpc_settings { + subnet_ids = ["${aws_subnet.test1.id}", "${aws_subnet.test2.id}"] + vpc_id = "${aws_vpc.test.id}" + } +} +`) +} + +func testAccAwsFsxWindowsFileSystemConfigAutomaticBackupRetentionDays(automaticBackupRetentionDays int) string { + return testAccAwsFsxWindowsFileSystemConfigBase() + fmt.Sprintf(` +resource "aws_fsx_windows_file_system" "test" { + active_directory_id = "${aws_directory_service_directory.test.id}" + automatic_backup_retention_days = %[1]d + skip_final_backup = true + storage_capacity = 300 + subnet_ids = ["${aws_subnet.test1.id}"] + throughput_capacity = 8 +} +`, automaticBackupRetentionDays) +} + +func testAccAwsFsxWindowsFileSystemConfigCopyTagsToBackups(copyTagsToBackups bool) string { + return testAccAwsFsxWindowsFileSystemConfigBase() + fmt.Sprintf(` +resource "aws_fsx_windows_file_system" "test" { + active_directory_id = "${aws_directory_service_directory.test.id}" + copy_tags_to_backups = %[1]t + skip_final_backup = true + storage_capacity = 300 + subnet_ids = ["${aws_subnet.test1.id}"] + throughput_capacity = 8 +} +`, copyTagsToBackups) +} + +func testAccAwsFsxWindowsFileSystemConfigDailyAutomaticBackupStartTime(dailyAutomaticBackupStartTime string) string { + return testAccAwsFsxWindowsFileSystemConfigBase() + fmt.Sprintf(` +resource "aws_fsx_windows_file_system" "test" { + active_directory_id = "${aws_directory_service_directory.test.id}" + daily_automatic_backup_start_time = %[1]q + skip_final_backup = true + storage_capacity = 300 + subnet_ids = ["${aws_subnet.test1.id}"] + throughput_capacity = 8 +} +`, dailyAutomaticBackupStartTime) +} + +func testAccAwsFsxWindowsFileSystemConfigKmsKeyId1() string { + return testAccAwsFsxWindowsFileSystemConfigBase() + fmt.Sprintf(` +resource "aws_kms_key" "test1" { + description = "FSx KMS Testing key" + deletion_window_in_days = 7 +} + +resource "aws_fsx_windows_file_system" "test" { + active_directory_id = "${aws_directory_service_directory.test.id}" + kms_key_id = "${aws_kms_key.test1.arn}" + skip_final_backup = true + storage_capacity = 300 + subnet_ids = ["${aws_subnet.test1.id}"] + throughput_capacity = 8 +} +`) +} + +func testAccAwsFsxWindowsFileSystemConfigKmsKeyId2() string { + return testAccAwsFsxWindowsFileSystemConfigBase() + fmt.Sprintf(` +resource "aws_kms_key" "test2" { + description = "FSx KMS Testing key" + deletion_window_in_days = 7 +} + +resource "aws_fsx_windows_file_system" "test" { + active_directory_id = "${aws_directory_service_directory.test.id}" + kms_key_id = "${aws_kms_key.test2.arn}" + skip_final_backup = true + storage_capacity = 300 + subnet_ids = ["${aws_subnet.test1.id}"] + throughput_capacity = 8 +} +`) +} + +func testAccAwsFsxWindowsFileSystemConfigSecurityGroupIds1() string { + return testAccAwsFsxWindowsFileSystemConfigBase() + fmt.Sprintf(` +resource "aws_security_group" "test1" { + description = "security group for FSx testing" + vpc_id = "${aws_vpc.test.id}" + + ingress { + cidr_blocks = ["${aws_vpc.test.cidr_block}"] + from_port = 0 + protocol = -1 + to_port = 0 + } + + egress { + cidr_blocks = ["0.0.0.0/0"] + from_port = 0 + protocol = "-1" + to_port = 0 + } +} + +resource "aws_fsx_windows_file_system" "test" { + active_directory_id = "${aws_directory_service_directory.test.id}" + security_group_ids = ["${aws_security_group.test1.id}"] + skip_final_backup = true + storage_capacity = 300 + subnet_ids = ["${aws_subnet.test1.id}"] + throughput_capacity = 8 +} +`) +} + +func testAccAwsFsxWindowsFileSystemConfigSecurityGroupIds2() string { + return testAccAwsFsxWindowsFileSystemConfigBase() + fmt.Sprintf(` +resource "aws_security_group" "test1" { + description = "security group for FSx testing" + vpc_id = "${aws_vpc.test.id}" + + ingress { + cidr_blocks = ["${aws_vpc.test.cidr_block}"] + from_port = 0 + protocol = -1 + to_port = 0 + } + + egress { + cidr_blocks = ["0.0.0.0/0"] + from_port = 0 + protocol = "-1" + to_port = 0 + } +} + +resource "aws_security_group" "test2" { + description = "security group for FSx testing" + vpc_id = "${aws_vpc.test.id}" + + ingress { + cidr_blocks = ["${aws_vpc.test.cidr_block}"] + from_port = 0 + protocol = -1 + to_port = 0 + } + + egress { + cidr_blocks = ["0.0.0.0/0"] + from_port = 0 + protocol = "-1" + to_port = 0 + } +} + +resource "aws_fsx_windows_file_system" "test" { + active_directory_id = "${aws_directory_service_directory.test.id}" + security_group_ids = ["${aws_security_group.test1.id}", "${aws_security_group.test2.id}"] + skip_final_backup = true + storage_capacity = 300 + subnet_ids = ["${aws_subnet.test1.id}"] + throughput_capacity = 8 +} +`) +} + +func testAccAwsFsxWindowsFileSystemConfigSelfManagedActiveDirectory() string { + return testAccAwsFsxWindowsFileSystemConfigBase() + fmt.Sprintf(` +resource "aws_fsx_windows_file_system" "test" { + skip_final_backup = true + storage_capacity = 300 + subnet_ids = ["${aws_subnet.test1.id}"] + throughput_capacity = 8 + + self_managed_active_directory { + dns_ips = aws_directory_service_directory.test.dns_ip_addresses + domain_name = aws_directory_service_directory.test.name + password = aws_directory_service_directory.test.password + username = "Admin" + } +} +`) +} + +func testAccAwsFsxWindowsFileSystemConfigStorageCapacity(storageCapacity int) string { + return testAccAwsFsxWindowsFileSystemConfigBase() + fmt.Sprintf(` +resource "aws_fsx_windows_file_system" "test" { + active_directory_id = "${aws_directory_service_directory.test.id}" + skip_final_backup = true + storage_capacity = %[1]d + subnet_ids = ["${aws_subnet.test1.id}"] + throughput_capacity = 8 +} +`, storageCapacity) +} + +func testAccAwsFsxWindowsFileSystemConfigSubnetIds1() string { + return testAccAwsFsxWindowsFileSystemConfigBase() + fmt.Sprintf(` +resource "aws_fsx_windows_file_system" "test" { + active_directory_id = "${aws_directory_service_directory.test.id}" + skip_final_backup = true + storage_capacity = 300 + subnet_ids = ["${aws_subnet.test1.id}"] + throughput_capacity = 8 +} +`) +} + +func testAccAwsFsxWindowsFileSystemConfigTags1(tagKey1, tagValue1 string) string { + return testAccAwsFsxWindowsFileSystemConfigBase() + fmt.Sprintf(` +resource "aws_fsx_windows_file_system" "test" { + active_directory_id = "${aws_directory_service_directory.test.id}" + skip_final_backup = true + storage_capacity = 300 + subnet_ids = ["${aws_subnet.test1.id}"] + throughput_capacity = 8 + + tags = { + %[1]q = %[2]q + } +} +`, tagKey1, tagValue1) +} + +func testAccAwsFsxWindowsFileSystemConfigTags2(tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return testAccAwsFsxWindowsFileSystemConfigBase() + fmt.Sprintf(` +resource "aws_fsx_windows_file_system" "test" { + active_directory_id = "${aws_directory_service_directory.test.id}" + skip_final_backup = true + storage_capacity = 300 + subnet_ids = ["${aws_subnet.test1.id}"] + throughput_capacity = 8 + + tags = { + %[1]q = %[2]q + %[3]q = %[4]q + } +} +`, tagKey1, tagValue1, tagKey2, tagValue2) +} + +func testAccAwsFsxWindowsFileSystemConfigThroughputCapacity(throughputCapacity int) string { + return testAccAwsFsxWindowsFileSystemConfigBase() + fmt.Sprintf(` +resource "aws_fsx_windows_file_system" "test" { + active_directory_id = "${aws_directory_service_directory.test.id}" + skip_final_backup = true + storage_capacity = 300 + subnet_ids = ["${aws_subnet.test1.id}"] + throughput_capacity = %[1]d +} +`, throughputCapacity) +} + +func testAccAwsFsxWindowsFileSystemConfigWeeklyMaintenanceStartTime(weeklyMaintenanceStartTime string) string { + return testAccAwsFsxWindowsFileSystemConfigBase() + fmt.Sprintf(` +resource "aws_fsx_windows_file_system" "test" { + active_directory_id = "${aws_directory_service_directory.test.id}" + skip_final_backup = true + storage_capacity = 300 + subnet_ids = ["${aws_subnet.test1.id}"] + throughput_capacity = 8 + weekly_maintenance_start_time = %[1]q +} +`, weeklyMaintenanceStartTime) +} diff --git a/aws/tagsFSX.go b/aws/tagsFSX.go index f2eed55a87ba..213b84653df5 100644 --- a/aws/tagsFSX.go +++ b/aws/tagsFSX.go @@ -63,10 +63,13 @@ func diffTagsFSX(oldTags, newTags []*fsx.Tag) ([]*fsx.Tag, []*fsx.Tag) { // Build the list of what to remove var remove []*fsx.Tag for _, t := range oldTags { - old, ok := create[*t.Key] - if !ok || old != *t.Value { + old, ok := create[aws.StringValue(t.Key)] + if !ok || old != aws.StringValue(t.Value) { // Delete it! remove = append(remove, t) + } else if ok { + // already present so remove from new + delete(create, aws.StringValue(t.Key)) } } diff --git a/aws/tagsFSX_test.go b/aws/tagsFSX_test.go index 5bf1c309b6e3..84cacd1c84c7 100644 --- a/aws/tagsFSX_test.go +++ b/aws/tagsFSX_test.go @@ -44,6 +44,39 @@ func TestDiffFSXTags(t *testing.T) { "foo": "bar", }, }, + + // Overlap + { + Old: map[string]interface{}{ + "foo": "bar", + "hello": "world", + }, + New: map[string]interface{}{ + "foo": "baz", + "hello": "world", + }, + Create: map[string]string{ + "foo": "baz", + }, + Remove: map[string]string{ + "foo": "bar", + }, + }, + + // Remove + { + Old: map[string]interface{}{ + "foo": "bar", + "bar": "baz", + }, + New: map[string]interface{}{ + "foo": "bar", + }, + Create: map[string]string{}, + Remove: map[string]string{ + "bar": "baz", + }, + }, } for i, tc := range cases { diff --git a/website/aws.erb b/website/aws.erb index e5201269ae64..ed8d10ca4e95 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -1478,10 +1478,18 @@
  • - FSx Resources -