diff --git a/.changelog/23077.txt b/.changelog/23077.txt new file mode 100644 index 00000000000..7a1206a93e4 --- /dev/null +++ b/.changelog/23077.txt @@ -0,0 +1,7 @@ +```release-note:note +provider: The `assume_role.duration_seconds` argument has been deprecated. All configurations using `assume_role.duration_seconds` should be updated to use the new `assume_role.duration` argument instead. +``` + +```release-note:enhancement +provider: Add `duration` argument to the `assume_role` configuration block +``` diff --git a/internal/provider/provider.go b/internal/provider/provider.go index a242f5d2df6..c12f6794084 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -1936,11 +1936,20 @@ func assumeRoleSchema() *schema.Schema { MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ + "duration": { + Type: schema.TypeString, + Optional: true, + Description: "The duration, between 15 minutes and 12 hours, of the role session. Valid time units are ns, us (or µs), ms, s, h, or m.", + ValidateFunc: ValidAssumeRoleDuration, + ConflictsWith: []string{"assume_role.0.duration_seconds"}, + }, "duration_seconds": { - Type: schema.TypeInt, - Optional: true, - Description: "The duration, in seconds, of the role session.", - ValidateFunc: validation.IntBetween(900, 43200), + Type: schema.TypeInt, + Optional: true, + Deprecated: "Use assume_role.0.duration instead", + Description: "The duration, in seconds, of the role session.", + ValidateFunc: validation.IntBetween(900, 43200), + ConflictsWith: []string{"assume_role.0.duration"}, }, "external_id": { Type: schema.TypeString, @@ -2022,6 +2031,11 @@ func endpointsSchema() *schema.Schema { func expandAssumeRole(m map[string]interface{}) *awsbase.AssumeRole { assumeRole := awsbase.AssumeRole{} + if v, ok := m["duration"].(string); ok && v != "" { + duration, _ := time.ParseDuration(v) + assumeRole.Duration = duration + } + if v, ok := m["duration_seconds"].(int); ok && v != 0 { assumeRole.Duration = time.Duration(v) * time.Second } diff --git a/internal/provider/validate.go b/internal/provider/validate.go new file mode 100644 index 00000000000..6bb96f68f10 --- /dev/null +++ b/internal/provider/validate.go @@ -0,0 +1,23 @@ +package provider + +import ( + "fmt" + "time" +) + +// ValidAssumeRoleDuration validates a string can be parsed as a valid time.Duration +// and is within a minimum of 15 minutes and maximum of 12 hours +func ValidAssumeRoleDuration(v interface{}, k string) (ws []string, errors []error) { + duration, err := time.ParseDuration(v.(string)) + + if err != nil { + errors = append(errors, fmt.Errorf("%q cannot be parsed as a duration: %w", k, err)) + return + } + + if duration.Minutes() < 15 || duration.Hours() > 12 { + errors = append(errors, fmt.Errorf("duration %q must be between 15 minutes (15m) and 12 hours (12h), inclusive", k)) + } + + return +} diff --git a/internal/provider/validate_test.go b/internal/provider/validate_test.go new file mode 100644 index 00000000000..03a21b22f8d --- /dev/null +++ b/internal/provider/validate_test.go @@ -0,0 +1,68 @@ +package provider + +import ( + "regexp" + "testing" +) + +func TestValidAssumeRoleDuration(t *testing.T) { + testCases := []struct { + val interface{} + expectedErr *regexp.Regexp + }{ + { + val: "", + expectedErr: regexp.MustCompile(`cannot be parsed as a duration`), + }, + { + val: "1", + expectedErr: regexp.MustCompile(`cannot be parsed as a duration`), + }, + { + val: "10m", + expectedErr: regexp.MustCompile(`must be between 15 minutes \(15m\) and 12 hours \(12h\)`), + }, + { + val: "12h30m", + expectedErr: regexp.MustCompile(`must be between 15 minutes \(15m\) and 12 hours \(12h\)`), + }, + { + + val: "15m", + }, + { + val: "1h10m10s", + }, + { + + val: "12h", + }, + } + + matchErr := func(errs []error, r *regexp.Regexp) bool { + // err must match one provided + for _, err := range errs { + if r.MatchString(err.Error()) { + return true + } + } + + return false + } + + for i, tc := range testCases { + _, errs := ValidAssumeRoleDuration(tc.val, "test_property") + + if len(errs) == 0 && tc.expectedErr == nil { + continue + } + + if len(errs) != 0 && tc.expectedErr == nil { + t.Fatalf("expected test case %d to produce no errors, got %v", i, errs) + } + + if !matchErr(errs, tc.expectedErr) { + t.Fatalf("expected test case %d to produce error matching \"%s\", got %v", i, tc.expectedErr, errs) + } + } +} diff --git a/website/docs/guides/version-4-upgrade.html.md b/website/docs/guides/version-4-upgrade.html.md index 71ecfc9dce7..717bc7bf627 100644 --- a/website/docs/guides/version-4-upgrade.html.md +++ b/website/docs/guides/version-4-upgrade.html.md @@ -99,6 +99,7 @@ provider "aws" { Version 4.0.0 adds these new provider arguments: +* `assume_role.duration` - The assume role duration represented as a string e.g. `"1h"` or `"1h30s"`. Replaces `assume_role.duration_seconds`, which has been deprecated in Terraform AWS Provider v4.0.0 and support will be removed in a future version. * `ec2_metadata_service_endpoint` - Address of the EC2 metadata service (IMDS) endpoint to use. Can also be set with the `AWS_EC2_METADATA_SERVICE_ENDPOINT` environment variable. * `ec2_metadata_service_endpoint_mode` - Mode to use in communicating with the metadata service. Valid values are `IPv4` and `IPv6`. Can also be set with the `AWS_EC2_METADATA_SERVICE_ENDPOINT_MODE` environment variable. * `s3_use_path_style` - Replaces `s3_force_path_style`, which has been deprecated in Terraform AWS Provider v4.0.0 and support will be removed in a future version. diff --git a/website/docs/index.html.markdown b/website/docs/index.html.markdown index 8d3995ae943..2bc19bbb0ae 100644 --- a/website/docs/index.html.markdown +++ b/website/docs/index.html.markdown @@ -335,7 +335,8 @@ In addition to [generic `provider` arguments](https://www.terraform.io/docs/conf The `assume_role` configuration block supports the following optional arguments: -* `duration_seconds` - (Optional) Number of seconds to restrict the assume role session duration. You can provide a value from 900 seconds (15 minutes) up to the maximum session duration setting for the role. +* `duration` - (Optional, Conflicts with `duration_seconds`) Duration of the assume role session. You can provide a value from 15 minutes up to the maximum session duration setting for the role. Represented by a string such as `1h`, `2h45m`, or `30m15s`. +* `duration_seconds` - (Optional, **Deprecated** use `duration` instead) Number of seconds to restrict the assume role session duration. You can provide a value from 900 seconds (15 minutes) up to the maximum session duration setting for the role. * `external_id` - (Optional) External identifier to use when assuming the role. * `policy` - (Optional) IAM Policy JSON describing further restricting permissions for the IAM Role being assumed. * `policy_arns` - (Optional) Set of Amazon Resource Names (ARNs) of IAM Policies describing further restricting permissions for the IAM Role being assumed.