diff --git a/.changelog/19970.txt b/.changelog/19970.txt new file mode 100644 index 00000000000..ceff50805b9 --- /dev/null +++ b/.changelog/19970.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_fsx_windows_file_system: Add `audit_log_configuration` argument. +``` diff --git a/aws/resource_aws_fsx_windows_file_system.go b/aws/resource_aws_fsx_windows_file_system.go index a8d5bdca499..1dbf1f7dae0 100644 --- a/aws/resource_aws_fsx_windows_file_system.go +++ b/aws/resource_aws_fsx_windows_file_system.go @@ -4,9 +4,11 @@ import ( "fmt" "log" "regexp" + "strings" "time" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/fsx" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -45,6 +47,38 @@ func resourceAwsFsxWindowsFileSystem() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "audit_log_configuration": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "audit_log_destination": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validateArn, + StateFunc: fsxWindowsAuditLogStateFunc, + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + return strings.HasPrefix(old, fmt.Sprintf("%s:", new)) + }, + }, + "file_access_audit_log_level": { + Type: schema.TypeString, + Optional: true, + Default: fsx.WindowsAccessAuditLogLevelDisabled, + ValidateFunc: validation.StringInSlice(fsx.WindowsAccessAuditLogLevel_Values(), false), + }, + "file_share_access_audit_log_level": { + Type: schema.TypeString, + Optional: true, + Default: fsx.WindowsAccessAuditLogLevelDisabled, + ValidateFunc: validation.StringInSlice(fsx.WindowsAccessAuditLogLevel_Values(), false), + }, + }, + }, + }, "automatic_backup_retention_days": { Type: schema.TypeInt, Optional: true, @@ -177,15 +211,11 @@ func resourceAwsFsxWindowsFileSystem() *schema.Resource { ), }, "deployment_type": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Default: fsx.WindowsDeploymentTypeSingleAz1, - ValidateFunc: validation.StringInSlice([]string{ - fsx.WindowsDeploymentTypeMultiAz1, - fsx.WindowsDeploymentTypeSingleAz1, - fsx.WindowsDeploymentTypeSingleAz2, - }, false), + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: fsx.WindowsDeploymentTypeSingleAz1, + ValidateFunc: validation.StringInSlice(fsx.WindowsDeploymentType_Values(), false), }, "preferred_subnet_id": { Type: schema.TypeString, @@ -202,14 +232,11 @@ func resourceAwsFsxWindowsFileSystem() *schema.Resource { Computed: true, }, "storage_type": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Default: fsx.StorageTypeSsd, - ValidateFunc: validation.StringInSlice([]string{ - fsx.StorageTypeSsd, - fsx.StorageTypeHdd, - }, false), + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: fsx.StorageTypeSsd, + ValidateFunc: validation.StringInSlice(fsx.StorageType_Values(), false), }, }, @@ -262,6 +289,10 @@ func resourceAwsFsxWindowsFileSystemCreate(d *schema.ResourceData, meta interfac input.WindowsConfiguration.SelfManagedActiveDirectoryConfiguration = expandFsxSelfManagedActiveDirectoryConfigurationCreate(v.([]interface{})) } + if v, ok := d.GetOk("audit_log_configuration"); ok && len(v.([]interface{})) > 0 { + input.WindowsConfiguration.AuditLogConfiguration = expandFsxWindowsAuditLogCreateConfiguration(v.([]interface{})) + } + if len(tags) > 0 { input.Tags = tags.IgnoreAws().FsxTags() } @@ -276,7 +307,7 @@ func resourceAwsFsxWindowsFileSystemCreate(d *schema.ResourceData, meta interfac result, err := conn.CreateFileSystem(input) if err != nil { - return fmt.Errorf("Error creating FSx filesystem: %s", err) + return fmt.Errorf("Error creating FSx filesystem: %w", err) } d.SetId(aws.StringValue(result.FileSystem.FileSystemId)) @@ -284,7 +315,7 @@ func resourceAwsFsxWindowsFileSystemCreate(d *schema.ResourceData, meta interfac 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 fmt.Errorf("Error waiting for filesystem (%s) to become available: %w", d.Id(), err) } return resourceAwsFsxWindowsFileSystemRead(d, meta) @@ -297,48 +328,45 @@ func resourceAwsFsxWindowsFileSystemUpdate(d *schema.ResourceData, meta interfac o, n := d.GetChange("tags_all") if err := keyvaluetags.FsxUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { - return fmt.Errorf("error updating FSx Windows File System (%s) tags: %s", d.Get("arn").(string), err) + return fmt.Errorf("error updating FSx Windows File System (%s) tags: %w", d.Get("arn").(string), err) } } - requestUpdate := false - input := &fsx.UpdateFileSystemInput{ - ClientRequestToken: aws.String(resource.UniqueId()), - FileSystemId: aws.String(d.Id()), - WindowsConfiguration: &fsx.UpdateFileSystemWindowsConfiguration{}, - } + if d.HasChangeExcept("tags_all") { + 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("automatic_backup_retention_days") { + input.WindowsConfiguration.AutomaticBackupRetentionDays = aws.Int64(int64(d.Get("automatic_backup_retention_days").(int))) + } - if d.HasChange("throughput_capacity") { - input.WindowsConfiguration.ThroughputCapacity = aws.Int64(int64(d.Get("throughput_capacity").(int))) - requestUpdate = true - } + if d.HasChange("throughput_capacity") { + input.WindowsConfiguration.ThroughputCapacity = aws.Int64(int64(d.Get("throughput_capacity").(int))) + } - if d.HasChange("storage_capacity") { - input.StorageCapacity = aws.Int64(int64(d.Get("storage_capacity").(int))) - requestUpdate = true - } + if d.HasChange("storage_capacity") { + input.StorageCapacity = aws.Int64(int64(d.Get("storage_capacity").(int))) + } - 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("daily_automatic_backup_start_time") { + input.WindowsConfiguration.DailyAutomaticBackupStartTime = aws.String(d.Get("daily_automatic_backup_start_time").(string)) + } - if d.HasChange("self_managed_active_directory") { - input.WindowsConfiguration.SelfManagedActiveDirectoryConfiguration = expandFsxSelfManagedActiveDirectoryConfigurationUpdate(d.Get("self_managed_active_directory").([]interface{})) - requestUpdate = true - } + if d.HasChange("self_managed_active_directory") { + input.WindowsConfiguration.SelfManagedActiveDirectoryConfiguration = expandFsxSelfManagedActiveDirectoryConfigurationUpdate(d.Get("self_managed_active_directory").([]interface{})) + } - if d.HasChange("weekly_maintenance_start_time") { - input.WindowsConfiguration.WeeklyMaintenanceStartTime = aws.String(d.Get("weekly_maintenance_start_time").(string)) - requestUpdate = true - } + if d.HasChange("weekly_maintenance_start_time") { + input.WindowsConfiguration.WeeklyMaintenanceStartTime = aws.String(d.Get("weekly_maintenance_start_time").(string)) + } + + if d.HasChange("audit_log_configuration") { + input.WindowsConfiguration.AuditLogConfiguration = expandFsxWindowsAuditLogCreateConfiguration(d.Get("audit_log_configuration").([]interface{})) + } - if requestUpdate { _, err := conn.UpdateFileSystem(input) if err != nil { @@ -367,7 +395,7 @@ func resourceAwsFsxWindowsFileSystemRead(d *schema.ResourceData, meta interface{ } if err != nil { - return fmt.Errorf("Error reading FSx File System (%s): %s", d.Id(), err) + return fmt.Errorf("Error reading FSx File System (%s): %w", d.Id(), err) } if filesystem == nil { @@ -398,30 +426,34 @@ func resourceAwsFsxWindowsFileSystemRead(d *schema.ResourceData, meta interface{ d.Set("storage_type", filesystem.StorageType) if err := d.Set("network_interface_ids", aws.StringValueSlice(filesystem.NetworkInterfaceIds)); err != nil { - return fmt.Errorf("error setting network_interface_ids: %s", err) + return fmt.Errorf("error setting network_interface_ids: %w", 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) + return fmt.Errorf("error setting self_managed_active_directory: %w", err) + } + + if err := d.Set("audit_log_configuration", flattenFsxWindowsAuditLogConfiguration(filesystem.WindowsConfiguration.AuditLogConfiguration)); err != nil { + return fmt.Errorf("error setting audit_log_configuration: %w", 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) + return fmt.Errorf("error setting subnet_ids: %w", err) } tags := keyvaluetags.FsxKeyValueTags(filesystem.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig) //lintignore:AWSR002 if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { - return fmt.Errorf("error setting tags: %s", err) + return fmt.Errorf("error setting tags: %w", err) } if err := d.Set("tags_all", tags.Map()); err != nil { - return fmt.Errorf("error setting tags_all: %s", err) + return fmt.Errorf("error setting tags_all: %w", err) } d.Set("throughput_capacity", filesystem.WindowsConfiguration.ThroughputCapacity) @@ -449,13 +481,13 @@ func resourceAwsFsxWindowsFileSystemDelete(d *schema.ResourceData, meta interfac } if err != nil { - return fmt.Errorf("Error deleting FSx filesystem: %s", err) + return fmt.Errorf("Error deleting FSx filesystem: %w", 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 fmt.Errorf("Error waiting for filesystem (%s) to delete: %w", d.Id(), err) } return nil @@ -491,10 +523,18 @@ func expandFsxSelfManagedActiveDirectoryConfigurationUpdate(l []interface{}) *fs } data := l[0].(map[string]interface{}) - req := &fsx.SelfManagedActiveDirectoryConfigurationUpdates{ - DnsIps: expandStringSet(data["dns_ips"].(*schema.Set)), - Password: aws.String(data["password"].(string)), - UserName: aws.String(data["username"].(string)), + req := &fsx.SelfManagedActiveDirectoryConfigurationUpdates{} + + if v, ok := data["dns_ips"].(*schema.Set); ok && v.Len() > 0 { + req.DnsIps = expandStringSet(v) + } + + if v, ok := data["password"].(string); ok && v != "" { + req.Password = aws.String(v) + } + + if v, ok := data["username"].(string); ok && v != "" { + req.UserName = aws.String(v) } return req @@ -522,3 +562,53 @@ func flattenFsxSelfManagedActiveDirectoryConfiguration(d *schema.ResourceData, a return []map[string]interface{}{m} } + +func expandFsxWindowsAuditLogCreateConfiguration(l []interface{}) *fsx.WindowsAuditLogCreateConfiguration { + if len(l) == 0 || l[0] == nil { + return nil + } + + data := l[0].(map[string]interface{}) + req := &fsx.WindowsAuditLogCreateConfiguration{ + FileAccessAuditLogLevel: aws.String(data["file_access_audit_log_level"].(string)), + FileShareAccessAuditLogLevel: aws.String(data["file_share_access_audit_log_level"].(string)), + } + + if v, ok := data["audit_log_destination"].(string); ok && v != "" { + req.AuditLogDestination = aws.String(fsxWindowsAuditLogStateFunc(v)) + } + + return req +} + +func flattenFsxWindowsAuditLogConfiguration(adopts *fsx.WindowsAuditLogConfiguration) []map[string]interface{} { + if adopts == nil { + return []map[string]interface{}{} + } + + m := map[string]interface{}{ + "file_access_audit_log_level": aws.StringValue(adopts.FileAccessAuditLogLevel), + "file_share_access_audit_log_level": aws.StringValue(adopts.FileShareAccessAuditLogLevel), + } + + if adopts.AuditLogDestination != nil { + m["audit_log_destination"] = aws.StringValue(adopts.AuditLogDestination) + } + + return []map[string]interface{}{m} +} + +func fsxWindowsAuditLogStateFunc(v interface{}) string { + value := v.(string) + // API returns the specific log stream arn instead of provided log group + logArn, _ := arn.Parse(value) + if logArn.Service == "logs" { + parts := strings.SplitN(logArn.Resource, ":", 3) + if len(parts) == 3 { + return strings.TrimSuffix(value, fmt.Sprintf(":%s", parts[2])) + } else { + return value + } + } + return value +} diff --git a/aws/resource_aws_fsx_windows_file_system_test.go b/aws/resource_aws_fsx_windows_file_system_test.go index 94897cda2a7..20b4b679c8e 100644 --- a/aws/resource_aws_fsx_windows_file_system_test.go +++ b/aws/resource_aws_fsx_windows_file_system_test.go @@ -9,6 +9,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/fsx" multierror "github.com/hashicorp/go-multierror" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) @@ -24,7 +25,7 @@ func testSweepFSXWindowsFileSystems(region string) error { client, err := sharedClientForRegion(region) if err != nil { - return fmt.Errorf("error getting client: %s", err) + return fmt.Errorf("error getting client: %w", err) } conn := client.(*AWSClient).fsxconn @@ -102,6 +103,9 @@ func TestAccAWSFsxWindowsFileSystem_basic(t *testing.T) { resource.TestMatchResourceAttr(resourceName, "weekly_maintenance_start_time", regexp.MustCompile(`^\d:\d\d:\d\d$`)), resource.TestCheckResourceAttr(resourceName, "deployment_type", "SINGLE_AZ_1"), resource.TestCheckResourceAttr(resourceName, "storage_type", "SSD"), + resource.TestCheckResourceAttr(resourceName, "audit_log_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "audit_log_configuration.0.file_access_audit_log_level", "DISABLED"), + resource.TestCheckResourceAttr(resourceName, "audit_log_configuration.0.file_share_access_audit_log_level", "DISABLED"), ), }, { @@ -702,6 +706,50 @@ func TestAccAWSFsxWindowsFileSystem_WeeklyMaintenanceStartTime(t *testing.T) { }) } +func TestAccAWSFsxWindowsFileSystem_auditConfig(t *testing.T) { + var filesystem fsx.FileSystem + resourceName := "aws_fsx_windows_file_system.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(fsx.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, fsx.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckFsxWindowsFileSystemDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsFsxWindowsFileSystemConfigAuditConfig(rName, "SUCCESS_ONLY"), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem), + resource.TestCheckResourceAttr(resourceName, "audit_log_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "audit_log_configuration.0.file_access_audit_log_level", "SUCCESS_ONLY"), + resource.TestCheckResourceAttr(resourceName, "audit_log_configuration.0.file_share_access_audit_log_level", "SUCCESS_ONLY"), + resource.TestCheckResourceAttrSet(resourceName, "audit_log_configuration.0.audit_log_destination"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "security_group_ids", + "skip_final_backup", + }, + }, + { + Config: testAccAwsFsxWindowsFileSystemConfigAuditConfig(rName, "SUCCESS_AND_FAILURE"), + Check: resource.ComposeTestCheckFunc( + testAccCheckFsxWindowsFileSystemExists(resourceName, &filesystem), + resource.TestCheckResourceAttr(resourceName, "audit_log_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "audit_log_configuration.0.file_access_audit_log_level", "SUCCESS_AND_FAILURE"), + resource.TestCheckResourceAttr(resourceName, "audit_log_configuration.0.file_share_access_audit_log_level", "SUCCESS_AND_FAILURE"), + resource.TestCheckResourceAttrSet(resourceName, "audit_log_configuration.0.audit_log_destination"), + ), + }, + }, + }) +} + func testAccCheckFsxWindowsFileSystemExists(resourceName string, fs *fsx.FileSystem) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] @@ -1129,3 +1177,25 @@ resource "aws_fsx_windows_file_system" "test" { } `, weeklyMaintenanceStartTime) } + +func testAccAwsFsxWindowsFileSystemConfigAuditConfig(rName, status string) string { + return testAccAwsFsxWindowsFileSystemConfigBase() + fmt.Sprintf(` +resource aws_cloudwatch_log_group "test" { + name = "/aws/fsx/%[1]s" +} + +resource "aws_fsx_windows_file_system" "test" { + active_directory_id = aws_directory_service_directory.test.id + skip_final_backup = true + storage_capacity = 32 + subnet_ids = [aws_subnet.test1.id] + throughput_capacity = 32 + + audit_log_configuration { + audit_log_destination = aws_cloudwatch_log_group.test.arn + file_access_audit_log_level = %[2]q + file_share_access_audit_log_level = %[2]q + } +} +`, rName, status) +} diff --git a/website/docs/r/fsx_windows_file_system.html.markdown b/website/docs/r/fsx_windows_file_system.html.markdown index bc9a765eae6..4378b897041 100644 --- a/website/docs/r/fsx_windows_file_system.html.markdown +++ b/website/docs/r/fsx_windows_file_system.html.markdown @@ -67,6 +67,7 @@ The following arguments are supported: * `weekly_maintenance_start_time` - (Optional) The preferred start time (in `d:HH:MM` format) to perform weekly maintenance, in the UTC time zone. * `deployment_type` - (Optional) Specifies the file system deployment type, valid values are `MULTI_AZ_1`, `SINGLE_AZ_1` and `SINGLE_AZ_2`. Default value is `SINGLE_AZ_1`. * `preferred_subnet_id` - (Optional) Specifies the subnet in which you want the preferred file server to be located. Required for when deployment type is `MULTI_AZ_1`. +* `audit_log_configuration` - (Optional) The configuration that Amazon FSx for Windows File Server uses to audit and log user accesses of files, folders, and file shares on the Amazon FSx for Windows File Server file system. See below. * `storage_type` - (Optional) Specifies the storage type, Valid values are `SSD` and `HDD`. `HDD` is supported on `SINGLE_AZ_2` and `MULTI_AZ_1` Windows file system deployment types. Default value is `SSD`. ### self_managed_active_directory @@ -80,6 +81,12 @@ The following arguments are supported for `self_managed_active_directory` config * `file_system_administrators_group` - (Optional) The name of the domain group whose members are granted administrative privileges for the file system. Administrative privileges include taking ownership of files and folders, and setting audit controls (audit ACLs) on files and folders. The group that you specify must already exist in your domain. Defaults to `Domain Admins`. * `organizational_unit_distinguished_name` - (Optional) The fully qualified distinguished name of the organizational unit within your self-managed AD directory that the Windows File Server instance will join. For example, `OU=FSx,DC=yourdomain,DC=corp,DC=com`. Only accepts OU as the direct parent of the file system. If none is provided, the FSx file system is created in the default location of your self-managed AD directory. To learn more, see [RFC 2253](https://tools.ietf.org/html/rfc2253). +### audit_log_configuration + +* `audit_log_destination` - (Optional) The Amazon Resource Name (ARN) for the destination of the audit logs. The destination can be any Amazon CloudWatch Logs log group ARN or Amazon Kinesis Data Firehose delivery stream ARN. Can be specified when `file_access_audit_log_level` and `file_share_access_audit_log_level` are not set to `DISABLED`. The name of the Amazon CloudWatch Logs log group must begin with the `/aws/fsx` prefix. The name of the Amazon Kinesis Data Firehouse delivery stream must begin with the `aws-fsx` prefix. If you do not provide a destination in `audit_log_destionation`, Amazon FSx will create and use a log stream in the CloudWatch Logs /aws/fsx/windows log group. +* `file_access_audit_log_level` - (Optional) Sets which attempt type is logged by Amazon FSx for file and folder accesses. Valid values are `SUCCESS_ONLY`, `FAILURE_ONLY`, `SUCCESS_AND_FAILURE`, and `DISABLED`. Default value is `DISABLED`. +* `file_share_access_audit_log_level` - (Optional) Sets which attempt type is logged by Amazon FSx for file share accesses. Valid values are `SUCCESS_ONLY`, `FAILURE_ONLY`, `SUCCESS_AND_FAILURE`, and `DISABLED`. Default value is `DISABLED`. + ## Attributes Reference In addition to all arguments above, the following attributes are exported: