Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changelog/44668.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
```release-note:bug
resource/aws_lambda_layer_version_permission: Fix wrong permissions being read into state file during refresh
```
```release-note:breaking-change
resource/aws_lambda_layer_version_permission: Import syntax adjusted to contain statement ID for correct targeting during read
```
2 changes: 1 addition & 1 deletion internal/service/lambda/exports_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ var (
FindFunctionEventInvokeConfigByTwoPartKey = findFunctionEventInvokeConfigByTwoPartKey
FindFunctionRecursionConfigByName = findFunctionRecursionConfigByName
FindFunctionURLByTwoPartKey = findFunctionURLByTwoPartKey
FindLayerVersionByTwoPartKey = findLayerVersionByTwoPartKey
FindLayerVersionPermissionByThreePartKey = findLayerVersionPermissionByThreePartKey
FindLayerVersionPolicyByTwoPartKey = findLayerVersionPolicyByTwoPartKey
FindPolicyStatementByTwoPartKey = findPolicyStatementByTwoPartKey
FindProvisionedConcurrencyConfigByTwoPartKey = findProvisionedConcurrencyConfigByTwoPartKey
Expand Down
132 changes: 72 additions & 60 deletions internal/service/lambda/layer_version_permission.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"encoding/json"
"log"
"reflect"
"slices"
"strconv"

"github.com/YakDriver/regexache"
Expand All @@ -29,7 +30,7 @@ import (
)

const (
layerVersionPermissionResourceIDPartCount = 2
layerVersionPermissionResourceIDPartCount = 3
)

// @SDKResource("aws_lambda_layer_version_permission", name="Layer Version Permission")
Expand Down Expand Up @@ -102,15 +103,16 @@ func resourceLayerVersionPermissionCreate(ctx context.Context, d *schema.Resourc

layerName := d.Get("layer_name").(string)
versionNumber := d.Get("version_number").(int)
id, err := flex.FlattenResourceId([]string{layerName, strconv.FormatInt(int64(versionNumber), 10)}, layerVersionPermissionResourceIDPartCount, true)
statementId := d.Get("statement_id").(string)
id, err := flex.FlattenResourceId([]string{layerName, strconv.FormatInt(int64(versionNumber), 10), statementId}, layerVersionPermissionResourceIDPartCount, true)
if err != nil {
return sdkdiag.AppendFromErr(diags, err)
}
input := &lambda.AddLayerVersionPermissionInput{
Action: aws.String(d.Get(names.AttrAction).(string)),
LayerName: aws.String(layerName),
Principal: aws.String(d.Get(names.AttrPrincipal).(string)),
StatementId: aws.String(d.Get("statement_id").(string)),
StatementId: aws.String(statementId),
VersionNumber: aws.Int64(int64(versionNumber)),
}

Expand All @@ -131,12 +133,12 @@ func resourceLayerVersionPermissionRead(ctx context.Context, d *schema.ResourceD
var diags diag.Diagnostics
conn := meta.(*conns.AWSClient).LambdaClient(ctx)

layerName, versionNumber, err := layerVersionPermissionParseResourceID(d.Id())
layerName, versionNumber, statementId, err := layerVersionPermissionParseResourceID(d.Id())
if err != nil {
return sdkdiag.AppendFromErr(diags, err)
}

output, err := findLayerVersionPolicyByTwoPartKey(ctx, conn, layerName, versionNumber)
policy, statement, err := findLayerVersionPermissionByThreePartKey(ctx, conn, layerName, versionNumber, statementId)

if !d.IsNewResource() && tfresource.NotFound(err) {
log.Printf("[WARN] Lambda Layer Version Permission (%s) not found, removing from state", d.Id())
Expand All @@ -148,61 +150,54 @@ func resourceLayerVersionPermissionRead(ctx context.Context, d *schema.ResourceD
return sdkdiag.AppendErrorf(diags, "reading Lambda Layer Version Permission (%s): %s", d.Id(), err)
}

policyDoc := &IAMPolicyDoc{}
if err := json.Unmarshal([]byte(aws.ToString(output.Policy)), policyDoc); err != nil {
return sdkdiag.AppendFromErr(diags, err)
}

d.Set("layer_name", layerName)
d.Set(names.AttrPolicy, output.Policy)
d.Set("revision_id", output.RevisionId)
d.Set(names.AttrPolicy, policy.Policy)
d.Set("revision_id", policy.RevisionId)
d.Set("version_number", versionNumber)
d.Set("statement_id", statementId)

if actions := statement.Actions; actions != nil {
var action string

if t := reflect.TypeOf(actions); t.String() == "[]string" && len(actions.([]string)) > 0 {
action = actions.([]string)[0]
} else if t.String() == "string" {
action = actions.(string)
}

if len(policyDoc.Statements) > 0 {
d.Set("statement_id", policyDoc.Statements[0].Sid)
d.Set(names.AttrAction, action)
}

if actions := policyDoc.Statements[0].Actions; actions != nil {
var action string
if len(statement.Conditions) > 0 {
if values := statement.Conditions[0].Values; values != nil {
var organizationID string

if t := reflect.TypeOf(actions); t.String() == "[]string" && len(actions.([]string)) > 0 {
action = actions.([]string)[0]
if t := reflect.TypeOf(values); t.String() == "[]string" && len(values.([]string)) > 0 {
organizationID = values.([]string)[0]
} else if t.String() == "string" {
action = actions.(string)
organizationID = values.(string)
}

d.Set(names.AttrAction, action)
d.Set("organization_id", organizationID)
}
}

if len(policyDoc.Statements[0].Conditions) > 0 {
if values := policyDoc.Statements[0].Conditions[0].Values; values != nil {
var organizationID string
if len(statement.Principals) > 0 {
if identifiers := statement.Principals[0].Identifiers; identifiers != nil {
var principal string

if t := reflect.TypeOf(values); t.String() == "[]string" && len(values.([]string)) > 0 {
organizationID = values.([]string)[0]
} else if t.String() == "string" {
organizationID = values.(string)
if t := reflect.TypeOf(identifiers); t.String() == "[]string" && len(identifiers.([]string)) > 0 && identifiers.([]string)[0] == "*" {
principal = "*"
} else if t.String() == "string" {
policyPrincipalARN, err := arn.Parse(identifiers.(string))
if err != nil {
return sdkdiag.AppendFromErr(diags, err)
}

d.Set("organization_id", organizationID)
principal = policyPrincipalARN.AccountID
}
}

if len(policyDoc.Statements[0].Principals) > 0 {
if identifiers := policyDoc.Statements[0].Principals[0].Identifiers; identifiers != nil {
var principal string

if t := reflect.TypeOf(identifiers); t.String() == "[]string" && len(identifiers.([]string)) > 0 && identifiers.([]string)[0] == "*" {
principal = "*"
} else if t.String() == "string" {
policyPrincipalARN, err := arn.Parse(identifiers.(string))
if err != nil {
return sdkdiag.AppendFromErr(diags, err)
}
principal = policyPrincipalARN.AccountID
}

d.Set(names.AttrPrincipal, principal)
}
d.Set(names.AttrPrincipal, principal)
}
}

Expand All @@ -213,7 +208,7 @@ func resourceLayerVersionPermissionDelete(ctx context.Context, d *schema.Resourc
var diags diag.Diagnostics
conn := meta.(*conns.AWSClient).LambdaClient(ctx)

layerName, versionNumber, err := layerVersionPermissionParseResourceID(d.Id())
layerName, versionNumber, statementId, err := layerVersionPermissionParseResourceID(d.Id())
if err != nil {
return sdkdiag.AppendFromErr(diags, err)
}
Expand All @@ -226,7 +221,7 @@ func resourceLayerVersionPermissionDelete(ctx context.Context, d *schema.Resourc
log.Printf("[INFO] Deleting Lambda Layer Permission Version: %s", d.Id())
_, err = conn.RemoveLayerVersionPermission(ctx, &lambda.RemoveLayerVersionPermissionInput{
LayerName: aws.String(layerName),
StatementId: aws.String(d.Get("statement_id").(string)),
StatementId: aws.String(statementId),
VersionNumber: aws.Int64(versionNumber),
})

Expand All @@ -241,49 +236,66 @@ func resourceLayerVersionPermissionDelete(ctx context.Context, d *schema.Resourc
return diags
}

func layerVersionPermissionParseResourceID(id string) (string, int64, error) {
func layerVersionPermissionParseResourceID(id string) (string, int64, string, error) {
parts, err := flex.ExpandResourceId(id, layerVersionPermissionResourceIDPartCount, true)

if err != nil {
return "", 0, err
return "", 0, "", err
}

layerName := parts[0]
versionNumber, err := strconv.ParseInt(parts[1], 10, 64)
statementId := parts[2]

if err != nil {
return "", 0, err
return "", 0, "", err
}

return layerName, versionNumber, nil
return layerName, versionNumber, statementId, nil
}

func findLayerVersionPolicy(ctx context.Context, conn *lambda.Client, input *lambda.GetLayerVersionPolicyInput) (*lambda.GetLayerVersionPolicyOutput, error) {
output, err := conn.GetLayerVersionPolicy(ctx, input)
func findLayerVersionPermission(ctx context.Context, conn *lambda.Client, input *lambda.GetLayerVersionPolicyInput, statementId string) (*lambda.GetLayerVersionPolicyOutput, *IAMPolicyStatement, error) {
policyOutput, err := conn.GetLayerVersionPolicy(ctx, input)

if errs.IsA[*awstypes.ResourceNotFoundException](err) {
return nil, &retry.NotFoundError{
return nil, nil, &retry.NotFoundError{
LastError: err,
LastRequest: input,
}
}

if err != nil {
return nil, err
return nil, nil, err
}

if policyOutput == nil {
return nil, nil, tfresource.NewEmptyResultError(input)
}

if output == nil {
return nil, tfresource.NewEmptyResultError(input)
policyDoc := &IAMPolicyDoc{}
if err := json.Unmarshal([]byte(aws.ToString(policyOutput.Policy)), policyDoc); err != nil {
return nil, nil, err
}

return output, nil
statementIndex := slices.IndexFunc(policyDoc.Statements, func(statement *IAMPolicyStatement) bool {
return (*statement).Sid == statementId
})

if statementIndex == -1 {
return nil, nil, &retry.NotFoundError{
LastError: err,
LastRequest: input,
}
}

return policyOutput, policyDoc.Statements[statementIndex], nil
}

func findLayerVersionPolicyByTwoPartKey(ctx context.Context, conn *lambda.Client, layerName string, versionNumber int64) (*lambda.GetLayerVersionPolicyOutput, error) {
func findLayerVersionPermissionByThreePartKey(ctx context.Context, conn *lambda.Client, layerName string, versionNumber int64, statementId string) (*lambda.GetLayerVersionPolicyOutput, *IAMPolicyStatement, error) {
input := &lambda.GetLayerVersionPolicyInput{
LayerName: aws.String(layerName),
VersionNumber: aws.Int64(versionNumber),
}

return findLayerVersionPolicy(ctx, conn, input)
return findLayerVersionPermission(ctx, conn, input, statementId)
}
41 changes: 37 additions & 4 deletions internal/service/lambda/layer_version_permission_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ import (
func TestAccLambdaLayerVersionPermission_basic_byARN(t *testing.T) {
ctx := acctest.Context(t)
resourceName := "aws_lambda_layer_version_permission.test"
// Used to ensure the correct resource is read when other statements exist before and after.
resourceNameFoo := "aws_lambda_layer_version_permission.foo"
resourceNameBar := "aws_lambda_layer_version_permission.bar"

rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)

resource.ParallelTest(t, resource.TestCase{
Expand All @@ -33,12 +37,23 @@ func TestAccLambdaLayerVersionPermission_basic_byARN(t *testing.T) {
Config: testAccLayerVersionPermissionConfig_basicARN(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckLayerVersionPermissionExists(ctx, resourceName),
testAccCheckLayerVersionPermissionExists(ctx, resourceNameFoo),
testAccCheckLayerVersionPermissionExists(ctx, resourceNameBar),
resource.TestCheckResourceAttr(resourceName, names.AttrAction, "lambda:GetLayerVersion"),
resource.TestCheckResourceAttr(resourceName, names.AttrPrincipal, "*"),
resource.TestCheckResourceAttr(resourceName, "statement_id", "xaccount"),
resource.TestCheckResourceAttrPair(resourceName, "layer_name", "aws_lambda_layer_version.test", "layer_arn"),
),
},
{
/* Each permission resource keeps track of the overall policy and policy
* revision separately. This means that when another permission is added,
* these attributes become out of date in the state of prior existing
* permission resources. Therefore, for an accurate ImportStateVerify
* test of multiple permission resources, each one must be refreshed prior
* to the test. */
RefreshState: true,
},
{
ResourceName: resourceName,
ImportState: true,
Expand Down Expand Up @@ -205,12 +220,30 @@ resource "aws_lambda_layer_version" "test" {
layer_name = %[1]q
}

resource "aws_lambda_layer_version_permission" "foo" {
layer_name = aws_lambda_layer_version.test.layer_arn
version_number = aws_lambda_layer_version.test.version
action = "lambda:GetLayerVersion"
statement_id = "fooaccount"
principal = "*"
}

resource "aws_lambda_layer_version_permission" "test" {
layer_name = aws_lambda_layer_version.test.layer_arn
version_number = aws_lambda_layer_version.test.version
action = "lambda:GetLayerVersion"
statement_id = "xaccount"
principal = "*"
depends_on = [aws_lambda_layer_version_permission.foo]
}

resource "aws_lambda_layer_version_permission" "bar" {
layer_name = aws_lambda_layer_version.test.layer_arn
version_number = aws_lambda_layer_version.test.version
action = "lambda:GetLayerVersion"
statement_id = "baraccount"
principal = "*"
depends_on = [aws_lambda_layer_version_permission.test]
}
`, layerName)
}
Expand Down Expand Up @@ -294,14 +327,14 @@ func testAccCheckLayerVersionPermissionExists(ctx context.Context, n string) res
return fmt.Errorf("Not found: %s", n)
}

layerName, versionNumber, err := tflambda.LayerVersionPermissionParseResourceID(rs.Primary.ID)
layerName, versionNumber, statementId, err := tflambda.LayerVersionPermissionParseResourceID(rs.Primary.ID)
if err != nil {
return err
}

conn := acctest.Provider.Meta().(*conns.AWSClient).LambdaClient(ctx)

_, err = tflambda.FindLayerVersionPolicyByTwoPartKey(ctx, conn, layerName, versionNumber)
_, _, err = tflambda.FindLayerVersionPermissionByThreePartKey(ctx, conn, layerName, versionNumber, statementId)

return err
}
Expand All @@ -316,12 +349,12 @@ func testAccCheckLayerVersionPermissionDestroy(ctx context.Context) resource.Tes
continue
}

layerName, versionNumber, err := tflambda.LayerVersionPermissionParseResourceID(rs.Primary.ID)
layerName, versionNumber, statementId, err := tflambda.LayerVersionPermissionParseResourceID(rs.Primary.ID)
if err != nil {
return err
}

_, err = tflambda.FindLayerVersionPolicyByTwoPartKey(ctx, conn, layerName, versionNumber)
_, _, err = tflambda.FindLayerVersionPermissionByThreePartKey(ctx, conn, layerName, versionNumber, statementId)

if tfresource.NotFound(err) {
continue
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ This resource exports the following attributes in addition to the arguments abov

## Import

In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import Lambda Layer Permissions using `layer_name` and `version_number`, separated by a comma (`,`). For example:
In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import Lambda Layer Permissions using `layer_name`, `version_number` and `statement_id`, separated by a comma (`,`). For example:

```python
# DO NOT EDIT. Code generated by 'cdktf convert' - Please report bugs at https://cdk.tf/bug
Expand All @@ -175,13 +175,13 @@ from imports.aws.lambda_layer_version_permission import LambdaLayerVersionPermis
class MyConvertedCode(TerraformStack):
def __init__(self, scope, name):
super().__init__(scope, name)
LambdaLayerVersionPermission.generate_config_for_import(self, "example", "arn:aws:lambda:us-west-2:123456789012:layer:shared_utilities,1")
LambdaLayerVersionPermission.generate_config_for_import(self, "example", "arn:aws:lambda:us-west-2:123456789012:layer:shared_utilities,1,statement1")
```

For backwards compatibility, the following legacy `terraform import` command is also supported:

```console
% terraform import aws_lambda_layer_version_permission.example arn:aws:lambda:us-west-2:123456789012:layer:shared_utilities,1
% terraform import aws_lambda_layer_version_permission.example arn:aws:lambda:us-west-2:123456789012:layer:shared_utilities,1,statement1
```

<!-- cache-key: cdktf-0.20.8 input-a49e40dd11a1f11a080f11c3cc347f86a43f422d194ecc77f6d1c8cd087738da -->
<!-- cache-key: cdktf-0.20.8 input-a49e40dd11a1f11a080f11c3cc347f86a43f422d194ecc77f6d1c8cd087738da -->
Loading
Loading