-
Notifications
You must be signed in to change notification settings - Fork 9.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #36723 from hashicorp/f-trim_iam_role_path
[New Function]: `trim_iam_role_path`
- Loading branch information
Showing
9 changed files
with
277 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
```release-note:new-function | ||
trim_iam_role_path | ||
``` |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package function | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/aws/aws-sdk-go-v2/aws/arn" | ||
"github.com/hashicorp/terraform-plugin-framework/function" | ||
) | ||
|
||
const ( | ||
// IAM role ARN reference: | ||
// https://docs.aws.amazon.com/IAM/latest/UserGuide/list_awsidentityandaccessmanagementiam.html#awsidentityandaccessmanagementiam-resources-for-iam-policies | ||
|
||
// resourceSectionPrefix is the expected prefix in the resource section of | ||
// an IAM role ARN | ||
resourceSectionPrefix = "role/" | ||
|
||
// serviceSection is the expected service section of an IAM role ARN | ||
serviceSection = "iam" | ||
) | ||
|
||
var _ function.Function = trimIAMRolePathFunction{} | ||
|
||
func NewTrimIAMRolePathFunction() function.Function { | ||
return &trimIAMRolePathFunction{} | ||
} | ||
|
||
type trimIAMRolePathFunction struct{} | ||
|
||
func (f trimIAMRolePathFunction) Metadata(ctx context.Context, req function.MetadataRequest, resp *function.MetadataResponse) { | ||
resp.Name = "trim_iam_role_path" | ||
} | ||
|
||
func (f trimIAMRolePathFunction) Definition(ctx context.Context, req function.DefinitionRequest, resp *function.DefinitionResponse) { | ||
resp.Definition = function.Definition{ | ||
Summary: "trim_iam_role_path Function", | ||
MarkdownDescription: "Trims the path prefix from an IAM role Amazon Resource Name (ARN). This " + | ||
"function can be used when services require role ARNs to be passed without a path.", | ||
Parameters: []function.Parameter{ | ||
function.StringParameter{ | ||
Name: "arn", | ||
MarkdownDescription: "IAM role Amazon Resource Name (ARN)", | ||
}, | ||
}, | ||
Return: function.StringReturn{}, | ||
} | ||
} | ||
|
||
func (f trimIAMRolePathFunction) Run(ctx context.Context, req function.RunRequest, resp *function.RunResponse) { | ||
var arg string | ||
|
||
resp.Error = function.ConcatFuncErrors(req.Arguments.Get(ctx, &arg)) | ||
if resp.Error != nil { | ||
return | ||
} | ||
|
||
result, err := trimPath(arg) | ||
if err != nil { | ||
resp.Error = function.ConcatFuncErrors(resp.Error, function.NewFuncError(err.Error())) | ||
return | ||
} | ||
|
||
resp.Error = function.ConcatFuncErrors(resp.Result.Set(ctx, result)) | ||
} | ||
|
||
// trimPath removes all path prefixes from the resource section of a role ARN | ||
func trimPath(s string) (string, error) { | ||
rarn, err := arn.Parse(s) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
if rarn.Service != serviceSection { | ||
return "", fmt.Errorf(`service must be "%s"`, serviceSection) | ||
} | ||
if rarn.Region != "" { | ||
return "", fmt.Errorf("region must be empty") | ||
} | ||
if !strings.HasPrefix(rarn.Resource, resourceSectionPrefix) { | ||
return "", fmt.Errorf(`resource must begin with "%s"`, resourceSectionPrefix) | ||
} | ||
|
||
sec := strings.Split(rarn.Resource, "/") | ||
rarn.Resource = fmt.Sprintf("%s%s", resourceSectionPrefix, sec[len(sec)-1]) | ||
|
||
return rarn.String(), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package function_test | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/YakDriver/regexache" | ||
"github.com/hashicorp/go-version" | ||
"github.com/hashicorp/terraform-plugin-testing/helper/resource" | ||
"github.com/hashicorp/terraform-plugin-testing/tfversion" | ||
"github.com/hashicorp/terraform-provider-aws/internal/acctest" | ||
) | ||
|
||
var ( | ||
// ExpectError parses the human readable output of a terraform apply run, in which | ||
// formatting (including line breaks) may change over time. For extra safety, we add | ||
// optional whitespace between each word in the expected error text. | ||
|
||
expectedErrorInvalidARN = regexache.MustCompile(`invalid[\s\n]*prefix`) | ||
expectedErrorInvalidService = regexache.MustCompile(`service[\s\n]*must`) | ||
expectedErrorInvalidRegion = regexache.MustCompile(`region[\s\n]*must`) | ||
expectedErrorInvalidResource = regexache.MustCompile(`resource[\s\n]*must`) | ||
) | ||
|
||
func TestTrimIAMRolePathFunction_valid(t *testing.T) { | ||
t.Parallel() | ||
arg := "arn:aws:iam::444455556666:role/example" | ||
|
||
resource.UnitTest(t, resource.TestCase{ | ||
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, | ||
TerraformVersionChecks: []tfversion.TerraformVersionCheck{ | ||
tfversion.SkipBelow(version.Must(version.NewVersion("1.8.0-beta1"))), | ||
}, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testTrimIAMRolePathFunctionConfig(arg), | ||
Check: resource.ComposeAggregateTestCheckFunc( | ||
resource.TestCheckOutput("test", arg), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func TestTrimIAMRolePathFunction_validWithPath(t *testing.T) { | ||
t.Parallel() | ||
arg := "arn:aws:iam::444455556666:role/with/some/path/parts/example" | ||
expected := "arn:aws:iam::444455556666:role/example" | ||
|
||
resource.UnitTest(t, resource.TestCase{ | ||
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, | ||
TerraformVersionChecks: []tfversion.TerraformVersionCheck{ | ||
tfversion.SkipBelow(version.Must(version.NewVersion("1.8.0-beta1"))), | ||
}, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testTrimIAMRolePathFunctionConfig(arg), | ||
Check: resource.ComposeAggregateTestCheckFunc( | ||
resource.TestCheckOutput("test", expected), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func TestTrimIAMRolePathFunction_invalidARN(t *testing.T) { | ||
t.Parallel() | ||
arg := "foo" | ||
|
||
resource.UnitTest(t, resource.TestCase{ | ||
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, | ||
TerraformVersionChecks: []tfversion.TerraformVersionCheck{ | ||
tfversion.SkipBelow(version.Must(version.NewVersion("1.8.0-beta1"))), | ||
}, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testTrimIAMRolePathFunctionConfig(arg), | ||
ExpectError: expectedErrorInvalidARN, | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func TestTrimIAMRolePathFunction_invalidService(t *testing.T) { | ||
t.Parallel() | ||
arg := "arn:aws:s3:::bucket/foo" | ||
|
||
resource.UnitTest(t, resource.TestCase{ | ||
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, | ||
TerraformVersionChecks: []tfversion.TerraformVersionCheck{ | ||
tfversion.SkipBelow(version.Must(version.NewVersion("1.8.0-beta1"))), | ||
}, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testTrimIAMRolePathFunctionConfig(arg), | ||
ExpectError: expectedErrorInvalidService, | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func TestTrimIAMRolePathFunction_invalidRegion(t *testing.T) { | ||
t.Parallel() | ||
arg := "arn:aws:iam:us-east-1:444455556666:role/example" | ||
|
||
resource.UnitTest(t, resource.TestCase{ | ||
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, | ||
TerraformVersionChecks: []tfversion.TerraformVersionCheck{ | ||
tfversion.SkipBelow(version.Must(version.NewVersion("1.8.0-beta1"))), | ||
}, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testTrimIAMRolePathFunctionConfig(arg), | ||
ExpectError: expectedErrorInvalidRegion, | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func TestTrimIAMRolePathFunction_invalidResource(t *testing.T) { | ||
t.Parallel() | ||
arg := "arn:aws:iam::444455556666:policy/example" | ||
|
||
resource.UnitTest(t, resource.TestCase{ | ||
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, | ||
TerraformVersionChecks: []tfversion.TerraformVersionCheck{ | ||
tfversion.SkipBelow(version.Must(version.NewVersion("1.8.0-beta1"))), | ||
}, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testTrimIAMRolePathFunctionConfig(arg), | ||
ExpectError: expectedErrorInvalidResource, | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testTrimIAMRolePathFunctionConfig(arg string) string { | ||
return fmt.Sprintf(` | ||
output "test" { | ||
value = provider::aws::trim_iam_role_path(%[1]q) | ||
}`, arg) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
--- | ||
subcategory: "" | ||
layout: "aws" | ||
page_title: "AWS: trim_iam_role_path" | ||
description: |- | ||
Trims the path prefix from an IAM role Amazon Resource Name (ARN). | ||
--- | ||
|
||
# Function: trim_iam_role_path | ||
|
||
~> Provider-defined function support is in technical preview and offered without compatibility promises until Terraform 1.8 is generally available. | ||
|
||
Trims the path prefix from an IAM role Amazon Resource Name (ARN). | ||
This function can be used when services require role ARNs to be passed without a path. | ||
|
||
See the [AWS IAM documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/list_awsidentityandaccessmanagementiam.html#awsidentityandaccessmanagementiam-resources-for-iam-policies) for additional information on IAM role ARNs. | ||
|
||
## Example Usage | ||
|
||
```terraform | ||
# result: arn:aws:iam::444455556666:role/example | ||
output "example" { | ||
value = provider::aws::trim_iam_role_path("arn:aws:iam::444455556666:role/with/path/example") | ||
} | ||
``` | ||
|
||
## Signature | ||
|
||
```text | ||
trim_iam_role_path(arn string) string | ||
``` | ||
|
||
## Arguments | ||
|
||
1. `arn` (String) IAM role Amazon Resource Name (ARN). |