Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

r/vpc_endpoint_policy - new resource #17039

Merged
merged 2 commits into from
Feb 23, 2022
Merged
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
3 changes: 3 additions & 0 deletions .changelog/17039.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-resource
aws_vpc_endpoint_policy
```
1 change: 1 addition & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -1225,6 +1225,7 @@ func Provider() *schema.Provider {
"aws_vpc_endpoint": ec2.ResourceVPCEndpoint(),
"aws_vpc_endpoint_connection_accepter": ec2.ResourceVPCEndpointConnectionAccepter(),
"aws_vpc_endpoint_connection_notification": ec2.ResourceVPCEndpointConnectionNotification(),
"aws_vpc_endpoint_policy": ec2.ResourceVPCEndpointPolicy(),
"aws_vpc_endpoint_route_table_association": ec2.ResourceVPCEndpointRouteTableAssociation(),
"aws_vpc_endpoint_service": ec2.ResourceVPCEndpointService(),
"aws_vpc_endpoint_service_allowed_principal": ec2.ResourceVPCEndpointServiceAllowedPrincipal(),
Expand Down
141 changes: 141 additions & 0 deletions internal/service/ec2/vpc_endpoint_policy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package ec2

import (
"fmt"
"log"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/hashicorp/terraform-provider-aws/internal/conns"
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
"github.com/hashicorp/terraform-provider-aws/internal/verify"
)

func ResourceVPCEndpointPolicy() *schema.Resource {
return &schema.Resource{
Create: resourceVPCEndpointPolicyPut,
Update: resourceVPCEndpointPolicyPut,
Read: resourceVPCEndpointPolicyRead,
Delete: resourceVPCEndpointPolicyDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Schema: map[string]*schema.Schema{
"policy": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: validation.StringIsJSON,
DiffSuppressFunc: verify.SuppressEquivalentPolicyDiffs,
StateFunc: func(v interface{}) string {
json, _ := structure.NormalizeJsonString(v)
return json
},
},
"vpc_endpoint_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
},

Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(10 * time.Minute),
Delete: schema.DefaultTimeout(10 * time.Minute),
},
}
}

func resourceVPCEndpointPolicyPut(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*conns.AWSClient).EC2Conn

endpointID := d.Get("vpc_endpoint_id").(string)
req := &ec2.ModifyVpcEndpointInput{
VpcEndpointId: aws.String(endpointID),
}

policy, err := structure.NormalizeJsonString(d.Get("policy"))
if err != nil {
return fmt.Errorf("policy contains an invalid JSON: %w", err)
}

if policy == "" {
req.ResetPolicy = aws.Bool(true)
} else {
req.PolicyDocument = aws.String(policy)
}

log.Printf("[DEBUG] Updating VPC Endpoint Policy: %#v", req)
if _, err := conn.ModifyVpcEndpoint(req); err != nil {
return fmt.Errorf("Error updating VPC Endpoint Policy: %w", err)
}
d.SetId(endpointID)

_, err = WaitVPCEndpointAvailable(conn, endpointID, d.Timeout(schema.TimeoutCreate))

if err != nil {
return fmt.Errorf("error waiting for VPC Endpoint (%s) to policy to set: %w", endpointID, err)
}

return resourceVPCEndpointPolicyRead(d, meta)
}

func resourceVPCEndpointPolicyRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*conns.AWSClient).EC2Conn

vpce, err := FindVPCEndpointByID(conn, d.Id())

if !d.IsNewResource() && tfresource.NotFound(err) {
log.Printf("[WARN] VPC Endpoint Policy (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}

if err != nil {
return fmt.Errorf("error reading VPC Endpoint Policy (%s): %w", d.Id(), err)
}

d.Set("vpc_endpoint_id", d.Id())

policyToSet, err := verify.SecondJSONUnlessEquivalent(d.Get("policy").(string), aws.StringValue(vpce.PolicyDocument))

if err != nil {
return fmt.Errorf("while setting policy (%s), encountered: %w", policyToSet, err)
}

policyToSet, err = structure.NormalizeJsonString(policyToSet)

if err != nil {
return fmt.Errorf("policy (%s) is invalid JSON: %w", policyToSet, err)
}

d.Set("policy", policyToSet)
return nil
}

func resourceVPCEndpointPolicyDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*conns.AWSClient).EC2Conn

req := &ec2.ModifyVpcEndpointInput{
VpcEndpointId: aws.String(d.Id()),
ResetPolicy: aws.Bool(true),
}

log.Printf("[DEBUG] Resetting VPC Endpoint Policy: %#v", req)
if _, err := conn.ModifyVpcEndpoint(req); err != nil {
return fmt.Errorf("Error Resetting VPC Endpoint Policy: %w", err)
}

_, err := WaitVPCEndpointAvailable(conn, d.Id(), d.Timeout(schema.TimeoutDelete))

if err != nil {
return fmt.Errorf("error waiting for VPC Endpoint (%s) to be reset: %w", d.Id(), err)
}

return nil
}
158 changes: 158 additions & 0 deletions internal/service/ec2/vpc_endpoint_policy_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package ec2_test

import (
"fmt"
"testing"

"github.com/aws/aws-sdk-go/service/ec2"
sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-provider-aws/internal/acctest"
tfec2 "github.com/hashicorp/terraform-provider-aws/internal/service/ec2"
)

func TestAccEC2VPCEndpointPolicy_basic(t *testing.T) {
var endpoint ec2.VpcEndpoint

resourceName := "aws_vpc_endpoint_policy.test"
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID),
Providers: acctest.Providers,
CheckDestroy: testAccCheckVpcEndpointDestroy,
Steps: []resource.TestStep{
{
Config: testAccVpcEndpointPolicyBasicConfig(rName, policy1),
Check: resource.ComposeTestCheckFunc(
testAccCheckVpcEndpointExists(resourceName, &endpoint),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
{
Config: testAccVpcEndpointPolicyBasicConfig(rName, policy2),
Check: resource.ComposeTestCheckFunc(
testAccCheckVpcEndpointExists(resourceName, &endpoint),
),
},
},
})
}

func TestAccEC2VPCEndpointPolicy_disappears(t *testing.T) {
var endpoint ec2.VpcEndpoint
resourceName := "aws_vpc_endpoint_policy.test"
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID),
Providers: acctest.Providers,
CheckDestroy: testAccCheckVpcEndpointDestroy,
Steps: []resource.TestStep{
{
Config: testAccVpcEndpointPolicyBasicConfig(rName, policy1),
Check: resource.ComposeTestCheckFunc(
testAccCheckVpcEndpointExists(resourceName, &endpoint),
acctest.CheckResourceDisappears(acctest.Provider, tfec2.ResourceVPCEndpointPolicy(), resourceName),
),
ExpectNonEmptyPlan: true,
},
},
})
}

func TestAccEC2VPCEndpointPolicy_disappears_endpoint(t *testing.T) {
var endpoint ec2.VpcEndpoint
resourceName := "aws_vpc_endpoint_policy.test"
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID),
Providers: acctest.Providers,
CheckDestroy: testAccCheckVpcEndpointDestroy,
Steps: []resource.TestStep{
{
Config: testAccVpcEndpointPolicyBasicConfig(rName, policy1),
Check: resource.ComposeTestCheckFunc(
testAccCheckVpcEndpointExists(resourceName, &endpoint),
acctest.CheckResourceDisappears(acctest.Provider, tfec2.ResourceVPCEndpoint(), "aws_vpc_endpoint.test"),
),
ExpectNonEmptyPlan: true,
},
},
})
}

const policy1 = `
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ReadOnly",
"Principal": "*",
"Action": [
"dynamodb:DescribeTable",
"dynamodb:ListTables"
],
"Effect": "Allow",
"Resource": "*"
}
]
}
`

const policy2 = `
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowAll",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "*",
"Resource": "*"
}
]
}
`

func testAccVpcEndpointPolicyBasicConfig(rName, policy string) string {
return fmt.Sprintf(`
data "aws_vpc_endpoint_service" "test" {
service = "dynamodb"
}

resource "aws_vpc" "test" {
cidr_block = "10.0.0.0/16"

tags = {
Name = %[1]q
}
}

resource "aws_vpc_endpoint" "test" {
service_name = data.aws_vpc_endpoint_service.test.service_name
vpc_id = aws_vpc.test.id

tags = {
Name = %[1]q
}
}

resource "aws_vpc_endpoint_policy" "test" {
vpc_endpoint_id = aws_vpc_endpoint.test.id
policy = <<POLICY
%[2]s
POLICY
}
`, rName, policy)
}
69 changes: 69 additions & 0 deletions website/docs/r/vpc_endpoint_policy.html.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
---
subcategory: "VPC"
layout: "aws"
page_title: "AWS: aws_vpc_endpoint_policy"
description: |-
Provides a VPC Endpoint Policy resource.
---

# Resource: aws_vpc_endpoint_policy

Provides a VPC Endpoint Policy resource.

## Example Usage

```terraform
data "aws_vpc_endpoint_service" "example" {
service = "dynamodb"
}

resource "aws_vpc" "example" {
cidr_block = "10.0.0.0/16"
}

resource "aws_vpc_endpoint" "example" {
service_name = data.aws_vpc_endpoint_service.example.service_name
vpc_id = aws_vpc.example.id
}

resource "aws_vpc_endpoint_policy" "example" {
vpc_endpoint_id = aws_vpc_endpoint.example.id
policy = jsonencode({
"Version" : "2012-10-17",
"Statement" : [
{
"Sid" : "AllowAll",
"Effect" : "Allow",
"Principal" : {
"AWS" : "*"
},
"Action" : [
"dynamodb:*"
],
"Resource" : "*"
}
]
})
}
```

## Argument Reference

The following arguments are supported:

* `vpc_endpoint_id` - (Required) The VPC Endpoint ID.
* `policy` - (Optional) A policy to attach to the endpoint that controls access to the service. Defaults to full access. All `Gateway` and some `Interface` endpoints support policies - see the [relevant AWS documentation](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-endpoints-access.html) for more details. For more information about building AWS IAM policy documents with Terraform, see the [AWS IAM Policy Document Guide](https://learn.hashicorp.com/terraform/aws/iam-policy).

## Attributes Reference

In addition to all arguments above, the following attributes are exported:

* `id` - The ID of the VPC endpoint.

## Import

VPC Endpoint Policies can be imported using the `id`, e.g.

```
$ terraform import aws_vpc_endpoint_policy.example vpce-3ecf2a57
```