Skip to content

Commit

Permalink
Merge pull request #1874 from jafowler/add-api-shield-resource
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobbednarz committed Sep 1, 2022
2 parents 47e0046 + 89eb893 commit 828bfe1
Show file tree
Hide file tree
Showing 7 changed files with 263 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .changelog/1874.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-resource
cloudflare_api_shield
```
43 changes: 43 additions & 0 deletions docs/resources/api_shield.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
page_title: "cloudflare_api_shield Resource - Cloudflare"
subcategory: ""
description: |-
Provides a resource to manage API Shield configurations.
---

# cloudflare_api_shield (Resource)

Provides a resource to manage API Shield configurations.

## Example Usage

```terraform
resource "cloudflare_api_shield" "example" {
zone_id = "0da42c8d2132a9ddaf714f9e7c920711"
auth_id_characteristics {
name = "my-example-header"
type = "header"
}
}
```
<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `auth_id_characteristics` (Block List, Min: 1) Characteristics define properties across which auth-ids can be computed in a privacy-preserving manner. (see [below for nested schema](#nestedblock--auth_id_characteristics))
- `zone_id` (String) The zone identifier to target for the resource.

### Read-Only

- `id` (String) The ID of this resource.

<a id="nestedblock--auth_id_characteristics"></a>
### Nested Schema for `auth_id_characteristics`

Required:

- `name` (String) The name of the characteristic.
- `type` (String) The type of characteristic. Available values: `header`, `cookie`.


7 changes: 7 additions & 0 deletions examples/resources/cloudflare_api_shield/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
resource "cloudflare_api_shield" "example" {
zone_id = "0da42c8d2132a9ddaf714f9e7c920711"
auth_id_characteristics {
name = "my-example-header"
type = "header"
}
}
1 change: 1 addition & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ func New(version string) func() *schema.Provider {
"cloudflare_access_rule": resourceCloudflareAccessRule(),
"cloudflare_access_service_token": resourceCloudflareAccessServiceToken(),
"cloudflare_account_member": resourceCloudflareAccountMember(),
"cloudflare_api_shield": resourceCloudflareAPIShield(),
"cloudflare_api_token": resourceCloudflareApiToken(),
"cloudflare_argo_tunnel": resourceCloudflareArgoTunnel(),
"cloudflare_argo": resourceCloudflareArgo(),
Expand Down
117 changes: 117 additions & 0 deletions internal/provider/resource_cloudflare_api_shield.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package provider

import (
"context"
"fmt"

"github.com/MakeNowJust/heredoc/v2"
"github.com/cloudflare/cloudflare-go"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/pkg/errors"
)

func resourceCloudflareAPIShield() *schema.Resource {
return &schema.Resource{
Schema: resourceCloudflareAPIShieldSchema(),
CreateContext: resourceCloudflareAPIShieldCreate,
ReadContext: resourceCloudflareAPIShieldRead,
UpdateContext: resourceCloudflareAPIShieldUpdate,
DeleteContext: resourceCloudflareAPIShieldDelete,
Importer: &schema.ResourceImporter{
StateContext: nil,
},
Description: heredoc.Doc(`
Provides a resource to manage API Shield configurations.
`),
}
}

func resourceCloudflareAPIShieldCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*cloudflare.API)
zoneID := d.Get("zone_id").(string)

as, err := buildAPIShieldConfiguration(d)
if err != nil {
return diag.FromErr(errors.Wrap(err, fmt.Sprintf("failed to create API Shield Configuration")))
}

_, err = client.UpdateAPIShieldConfiguration(ctx, cloudflare.ZoneIdentifier(zoneID), cloudflare.UpdateAPIShieldParams{AuthIdCharacteristics: as.AuthIdCharacteristics})
if err != nil {
return diag.FromErr(errors.Wrap(err, fmt.Sprintf("failed to create API Shield Configuration")))
}

return resourceCloudflareAPIShieldRead(ctx, d, meta)
}

func resourceCloudflareAPIShieldRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*cloudflare.API)
zoneID := d.Get("zone_id").(string)

as, _, err := client.GetAPIShieldConfiguration(ctx, cloudflare.ZoneIdentifier(zoneID))
if err != nil {
return diag.FromErr(fmt.Errorf("failed to fetch API Shield Configuration: %w", err))
}

d.Set("auth_id_characteristics", flattenAPIShieldConfiguration(as.AuthIdCharacteristics))
d.Set("zone_id", zoneID)
d.SetId(zoneID)

return nil
}

func resourceCloudflareAPIShieldUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*cloudflare.API)
zoneID := d.Get("zone_id")

as, err := buildAPIShieldConfiguration(d)
if err != nil {
return diag.FromErr(errors.Wrap(err, fmt.Sprintf("failed to create API Shield Configuration")))
}

_, err = client.UpdateAPIShieldConfiguration(ctx, cloudflare.ZoneIdentifier(zoneID.(string)), cloudflare.UpdateAPIShieldParams{AuthIdCharacteristics: as.AuthIdCharacteristics})
if err != nil {
return diag.FromErr(errors.Wrap(err, fmt.Sprintf("failed to create API Shield Configuration")))
}

return resourceCloudflareAPIShieldRead(ctx, d, meta)
}

func resourceCloudflareAPIShieldDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*cloudflare.API)
zoneID := d.Get("zone_id")

_, err := client.UpdateAPIShieldConfiguration(ctx, cloudflare.ZoneIdentifier(zoneID.(string)), cloudflare.UpdateAPIShieldParams{AuthIdCharacteristics: []cloudflare.AuthIdCharacteristics{}})
if err != nil {
return diag.FromErr(errors.Wrap(err, fmt.Sprintf("failed to create API Shield Configuration")))
}

return resourceCloudflareAPIShieldRead(ctx, d, meta)
}

func buildAPIShieldConfiguration(d *schema.ResourceData) (cloudflare.APIShield, error) {
var as cloudflare.APIShield

configs, ok := d.Get("auth_id_characteristics").([]interface{})

if !ok {
return cloudflare.APIShield{}, errors.New("unable to create interface map type assertion for rule")
}

for i := 0; i < len(configs); i++ {
as.AuthIdCharacteristics = append(as.AuthIdCharacteristics, cloudflare.AuthIdCharacteristics{Name: configs[i].(map[string]interface{})["name"].(string), Type: configs[i].(map[string]interface{})["type"].(string)})
}

return as, nil
}

func flattenAPIShieldConfiguration(characteristics []cloudflare.AuthIdCharacteristics) []interface{} {
var flattened []interface{}
for _, c := range characteristics {
flattened = append(flattened, map[string]interface{}{
"name": c.Name,
"type": c.Type,
})
}
return flattened
}
53 changes: 53 additions & 0 deletions internal/provider/resource_cloudflare_api_shield_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package provider

import (
"fmt"
"os"
"testing"

"github.com/cloudflare/cloudflare-go"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccAPIShield_Basic(t *testing.T) {
// Temporarily unset CLOUDFLARE_API_TOKEN if it is set as the API token
// endpoint does not yet support the API tokens without an explicit scope.
if os.Getenv("CLOUDFLARE_API_TOKEN") != "" {
defer func(apiToken string) {
os.Setenv("CLOUDFLARE_API_TOKEN", apiToken)
}(os.Getenv("CLOUDFLARE_API_TOKEN"))
os.Setenv("CLOUDFLARE_API_TOKEN", "")
}

rnd := generateRandomResourceName()
resourceID := "cloudflare_api_shield." + rnd
zoneID := os.Getenv("CLOUDFLARE_ZONE_ID")

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: testAccCloudflareAPIShieldSingleEntry(rnd, zoneID, cloudflare.AuthIdCharacteristics{Name: "test-header", Type: "header"}),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceID, "zone_id", zoneID),
resource.TestCheckResourceAttr(resourceID, "auth_id_characteristics.#", "1"),
resource.TestCheckResourceAttr(resourceID, "auth_id_characteristics.0.name", "test-header"),
resource.TestCheckResourceAttr(resourceID, "auth_id_characteristics.0.type", "header"),
),
},
},
})
}

func testAccCloudflareAPIShieldSingleEntry(resourceName, rnd string, authChar cloudflare.AuthIdCharacteristics) string {
return fmt.Sprintf(`
resource "cloudflare_api_shield" "%[1]s" {
zone_id = "%[2]s"
auth_id_characteristics {
name = "%[3]s"
type = "%[4]s"
}
}
`, resourceName, rnd, authChar.Name, authChar.Type)
}
39 changes: 39 additions & 0 deletions internal/provider/schema_cloudflare_api_shield.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package provider

import (
"fmt"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)

func resourceCloudflareAPIShieldSchema() map[string]*schema.Schema {
return map[string]*schema.Schema{
"zone_id": {
Description: "The zone identifier to target for the resource.",
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"auth_id_characteristics": {
Description: "Characteristics define properties across which auth-ids can be computed in a privacy-preserving manner.",
Required: true,
Type: schema.TypeList,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"type": {
Description: fmt.Sprintf("The type of characteristic. %s", renderAvailableDocumentationValuesStringSlice([]string{"header", "cookie"})),
Required: true,
Type: schema.TypeString,
ValidateFunc: validation.StringInSlice([]string{"header", "cookie"}, false),
},
"name": {
Description: "The name of the characteristic.",
Required: true,
Type: schema.TypeString,
},
},
},
},
}
}

0 comments on commit 828bfe1

Please sign in to comment.