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/api_gateway_rest_api_policy - add new resource #13619

Merged
merged 9 commits into from
Nov 17, 2020
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
1 change: 1 addition & 0 deletions aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,7 @@ func Provider() *schema.Provider {
"aws_api_gateway_request_validator": resourceAwsApiGatewayRequestValidator(),
"aws_api_gateway_resource": resourceAwsApiGatewayResource(),
"aws_api_gateway_rest_api": resourceAwsApiGatewayRestApi(),
"aws_api_gateway_rest_api_policy": resourceAwsApiGatewayRestApiPolicy(),
"aws_api_gateway_stage": resourceAwsApiGatewayStage(),
"aws_api_gateway_usage_plan": resourceAwsApiGatewayUsagePlan(),
"aws_api_gateway_usage_plan_key": resourceAwsApiGatewayUsagePlanKey(),
Expand Down
1 change: 1 addition & 0 deletions aws/resource_aws_api_gateway_rest_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ func resourceAwsApiGatewayRestApi() *schema.Resource {
"policy": {
Type: schema.TypeString,
Optional: true,
Computed: true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This update breaks the existing TestAccAWSAPIGatewayRestApi_policy test:

=== CONT  TestAccAWSAPIGatewayRestApi_policy
TestAccAWSAPIGatewayRestApi_policy: resource_aws_api_gateway_rest_api_test.go:446: Step 4/4 error: Check failed: Check 1/1 error: aws_api_gateway_rest_api.test: Attribute 'policy' expected "", got "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Deny\",\"Principal\":{\"AWS\":\"*\"},\"Action\":\"execute-api:Invoke\",\"Resource\":\"*\"}]}"
--- FAIL: TestAccAWSAPIGatewayRestApi_policy (199.92s)

Since the removal behavior is expected unless it is configured to an empty string (""), will update the test and document the behavior in the resource documentation on merge.

ValidateFunc: validation.StringIsJSON,
DiffSuppressFunc: suppressEquivalentAwsPolicyDiffs,
},
Expand Down
129 changes: 129 additions & 0 deletions aws/resource_aws_api_gateway_rest_api_policy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package aws

import (
"fmt"
"log"
"strconv"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/apigateway"
"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"
)

func resourceAwsApiGatewayRestApiPolicy() *schema.Resource {
return &schema.Resource{
Create: resourceAwsApiGatewayRestApiPolicyPut,
Read: resourceAwsApiGatewayRestApiPolicyRead,
Update: resourceAwsApiGatewayRestApiPolicyPut,
Delete: resourceAwsApiGatewayRestApiPolicyDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Schema: map[string]*schema.Schema{
"rest_api_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},

"policy": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringIsJSON,
DiffSuppressFunc: suppressEquivalentAwsPolicyDiffs,
},
},
}
}

func resourceAwsApiGatewayRestApiPolicyPut(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).apigatewayconn

restApiId := d.Get("rest_api_id").(string)
log.Printf("[DEBUG] Setting API Gateway REST API Policy: %s", restApiId)

operations := make([]*apigateway.PatchOperation, 0)

operations = append(operations, &apigateway.PatchOperation{
Op: aws.String(apigateway.OpReplace),
Path: aws.String("/policy"),
Value: aws.String(d.Get("policy").(string)),
})

res, err := conn.UpdateRestApi(&apigateway.UpdateRestApiInput{
RestApiId: aws.String(restApiId),
PatchOperations: operations,
})

if err != nil {
return fmt.Errorf("error setting API Gateway REST API Policy %w", err)
}

log.Printf("[DEBUG] API Gateway REST API Policy Set: %s", restApiId)

d.SetId(aws.StringValue(res.Id))

return resourceAwsApiGatewayRestApiPolicyRead(d, meta)
}

func resourceAwsApiGatewayRestApiPolicyRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).apigatewayconn

log.Printf("[DEBUG] Reading API Gateway REST API Policy %s", d.Id())

api, err := conn.GetRestApi(&apigateway.GetRestApiInput{
RestApiId: aws.String(d.Id()),
})
if isAWSErr(err, apigateway.ErrCodeNotFoundException, "") {
log.Printf("[WARN] API Gateway REST API Policy (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}
if err != nil {
return fmt.Errorf("error reading API Gateway REST API Policy (%s): %w", d.Id(), err)
}

normalizedPolicy, err := structure.NormalizeJsonString(`"` + aws.StringValue(api.Policy) + `"`)
if err != nil {
return fmt.Errorf("error normalizing API Gateway REST API policy JSON: %w", err)
}
policy, err := strconv.Unquote(normalizedPolicy)
if err != nil {
return fmt.Errorf("error unescaping API Gateway REST API policy: %w", err)
}
d.Set("policy", policy)
d.Set("rest_api_id", api.Id)

return nil
}

func resourceAwsApiGatewayRestApiPolicyDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).apigatewayconn

restApiId := d.Get("rest_api_id").(string)
log.Printf("[DEBUG] Deleting API Gateway REST API Policy: %s", restApiId)

operations := make([]*apigateway.PatchOperation, 0)

operations = append(operations, &apigateway.PatchOperation{
Op: aws.String(apigateway.OpReplace),
Path: aws.String("/policy"),
Value: aws.String(""),
})

_, err := conn.UpdateRestApi(&apigateway.UpdateRestApiInput{
RestApiId: aws.String(restApiId),
PatchOperations: operations,
})

if err != nil {
return fmt.Errorf("error deleting API Gateway REST API policy: %w", err)
}

log.Printf("[DEBUG] API Gateway REST API Policy Deleted: %s", restApiId)

return nil
}
218 changes: 218 additions & 0 deletions aws/resource_aws_api_gateway_rest_api_policy_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
package aws

import (
"fmt"
"regexp"
"strconv"
"testing"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/apigateway"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)

func TestAccAWSAPIGatewayRestApiPolicy_basic(t *testing.T) {
var v apigateway.RestApi
resourceName := "aws_api_gateway_rest_api_policy.test"
rName := acctest.RandomWithPrefix("tf-acc-test")

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSAPIGatewayRestApiPolicyDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSAPIGatewayRestApiPolicyConfig(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayRestApiPolicyExists(resourceName, &v),
resource.TestMatchResourceAttr(resourceName, "policy", regexp.MustCompile(`"Action":"execute-api:Invoke".+`)),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
{
Config: testAccAWSAPIGatewayRestApiPolicyConfigUpdated(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayRestApiPolicyExists(resourceName, &v),
resource.TestMatchResourceAttr(resourceName, "policy", regexp.MustCompile(`"aws:SourceIp":"123.123.123.123/32".+`))),
},
},
})
}

func TestAccAWSAPIGatewayRestApiPolicy_disappears(t *testing.T) {
var v apigateway.RestApi
resourceName := "aws_api_gateway_rest_api_policy.test"
rName := acctest.RandomWithPrefix("tf-acc-test")

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSAPIGatewayRestApiPolicyDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSAPIGatewayRestApiPolicyConfig(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayRestApiPolicyExists(resourceName, &v),
testAccCheckResourceDisappears(testAccProvider, resourceAwsApiGatewayRestApiPolicy(), resourceName),
),
ExpectNonEmptyPlan: true,
},
},
})
}

func TestAccAWSAPIGatewayRestApiPolicy_disappears_restApi(t *testing.T) {
var v apigateway.RestApi
resourceName := "aws_api_gateway_rest_api_policy.test"
rName := acctest.RandomWithPrefix("tf-acc-test")

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSAPIGatewayRestApiPolicyDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSAPIGatewayRestApiPolicyConfig(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayRestApiPolicyExists(resourceName, &v),
testAccCheckResourceDisappears(testAccProvider, resourceAwsApiGatewayRestApi(), "aws_api_gateway_rest_api.test"),
),
ExpectNonEmptyPlan: true,
},
},
})
}

func testAccCheckAWSAPIGatewayRestApiPolicyExists(n string, res *apigateway.RestApi) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}

if rs.Primary.ID == "" {
return fmt.Errorf("No API Gateway ID is set")
}

conn := testAccProvider.Meta().(*AWSClient).apigatewayconn

req := &apigateway.GetRestApiInput{
RestApiId: aws.String(rs.Primary.ID),
}
describe, err := conn.GetRestApi(req)
if err != nil {
return err
}

normalizedPolicy, err := structure.NormalizeJsonString(`"` + aws.StringValue(describe.Policy) + `"`)
if err != nil {
return fmt.Errorf("error normalizing API Gateway REST API policy JSON: %w", err)
}
policy, err := strconv.Unquote(normalizedPolicy)
if err != nil {
return fmt.Errorf("error unescaping API Gateway REST API policy: %w", err)
}

if aws.StringValue(describe.Id) != rs.Primary.ID &&
policy != rs.Primary.Attributes["policy"] {
return fmt.Errorf("API Gateway REST API Policy not found")
}

*res = *describe

return nil
}
}

func testAccCheckAWSAPIGatewayRestApiPolicyDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).apigatewayconn

for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_api_gateway_rest_api_policy" {
continue
}

req := &apigateway.GetRestApisInput{}
describe, err := conn.GetRestApis(req)

if err == nil {
if len(describe.Items) != 0 &&
aws.StringValue(describe.Items[0].Id) == rs.Primary.ID &&
aws.StringValue(describe.Items[0].Policy) == "" {
return fmt.Errorf("API Gateway REST API Policy still exists")
}
}

return err
}

return nil
}

func testAccAWSAPIGatewayRestApiPolicyConfig(rName string) string {
return fmt.Sprintf(`
resource "aws_api_gateway_rest_api" "test" {
name = %[1]q
}

resource "aws_api_gateway_rest_api_policy" "test" {
rest_api_id = aws_api_gateway_rest_api.test.id

policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Principal": {
"AWS": "*"
},
"Action": "execute-api:Invoke",
"Resource": "${aws_api_gateway_rest_api.test.arn}"
}
]
}
EOF
}
`, rName)
}

func testAccAWSAPIGatewayRestApiPolicyConfigUpdated(rName string) string {
return fmt.Sprintf(`
resource "aws_api_gateway_rest_api" "test" {
name = %[1]q
}

resource "aws_api_gateway_rest_api_policy" "test" {
rest_api_id = aws_api_gateway_rest_api.test.id

policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "execute-api:Invoke",
"Resource": "${aws_api_gateway_rest_api.test.arn}",
"Condition": {
"IpAddress": {
"aws:SourceIp": "123.123.123.123/32"
}
}
}
]
}
EOF
}
`, rName)
}
Loading