Skip to content

Commit

Permalink
Merge pull request #17039 from DrFaust92/r/vpc_endpoint_policy
Browse files Browse the repository at this point in the history
r/vpc_endpoint_policy - new resource
  • Loading branch information
ewbankkit authored Feb 23, 2022
2 parents 4d90616 + b407921 commit 16ea68f
Show file tree
Hide file tree
Showing 5 changed files with 372 additions and 0 deletions.
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 @@ -1248,6 +1248,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
```

0 comments on commit 16ea68f

Please sign in to comment.