From e96a4a818d428f6fe94f93a4bd2c646f3699fb33 Mon Sep 17 00:00:00 2001 From: Curtis Lowder Date: Tue, 8 Aug 2023 13:24:19 -0500 Subject: [PATCH 1/8] resource/cloudflare_bot_managment: add Bot Management resource --- .changelog/2672.txt | 3 + docs/resources/bot_management.md | 67 ++++++++ .../cloudflare_bot_management/import.sh | 2 + .../cloudflare_bot_management/resource.tf | 12 ++ internal/sdkv2provider/provider.go | 1 + .../resource_cloudflare_bot_management.go | 154 ++++++++++++++++++ ...resource_cloudflare_bot_management_test.go | 106 ++++++++++++ .../schema_cloudflare_bot_management.go | 67 ++++++++ 8 files changed, 412 insertions(+) create mode 100644 .changelog/2672.txt create mode 100644 docs/resources/bot_management.md create mode 100644 examples/resources/cloudflare_bot_management/import.sh create mode 100644 examples/resources/cloudflare_bot_management/resource.tf create mode 100644 internal/sdkv2provider/resource_cloudflare_bot_management.go create mode 100644 internal/sdkv2provider/resource_cloudflare_bot_management_test.go create mode 100644 internal/sdkv2provider/schema_cloudflare_bot_management.go diff --git a/.changelog/2672.txt b/.changelog/2672.txt new file mode 100644 index 0000000000..dc06b0c2d7 --- /dev/null +++ b/.changelog/2672.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +introduce bot management resource +``` diff --git a/docs/resources/bot_management.md b/docs/resources/bot_management.md new file mode 100644 index 0000000000..5f7192f016 --- /dev/null +++ b/docs/resources/bot_management.md @@ -0,0 +1,67 @@ +--- +page_title: "cloudflare_bot_management Resource - Cloudflare" +subcategory: "" +description: |- + Provides a resource to configure Bot Management. + Specifically, this resource can be used to manage: + - Bot Fight Mode + - Super Bot Fight Mode + - Bot Management for Enterprise +--- + +# cloudflare_bot_management (Resource) + +Provides a resource to configure Bot Management. +Specifically, this resource can be used to manage: +- **Bot Fight Mode** +- **Super Bot Fight Mode** +- **Bot Management for Enterprise** + +## Example Usage + +```terraform +# Bot Management +resource "cloudflare_bot_management" "sbfm_biz_example" { + zone_id = "0da42c8d2132a9ddaf714f9e7c920711" + + enable_js = true + + sbfm_definitely_automated = "block" + sbfm_likely_automated = "managed_challenge" + sbfm_verified_bots = "allow" + sbfm_static_resource_protection = false + optimize_wordpress = true +} +``` + +## Schema + +### Required + +- `zone_id` (String) The zone identifier to target for the resource. **Modifying this attribute will force creation of a new resource.** + +### Optional + +- `auto_update_model` (Boolean) Automatically update to the newest bot detection models created by Cloudflare as they are released. [Learn more.](https://developers.cloudflare.com/bots/reference/machine-learning-models#model-versions-and-release-notes). +- `enable_js` (Boolean) Use lightweight, invisible JavaScript detections to improve Bot Management. [Learn more about JavaScript Detections](https://developers.cloudflare.com/bots/reference/javascript-detections/). +- `fight_mode` (Boolean) Whether to enable Bot Fight Mode. +- `optimize_wordpress` (Boolean) Whether to optimize Super Bot Fight Mode protections for Wordpress. +- `sbfm_definitely_automated` (String) Super Bot Fight Mode (SBFM) action to take on definitely automated requests. +- `sbfm_likely_automated` (String) Super Bot Fight Mode (SBFM) action to take on likely automated requests. +- `sbfm_static_resource_protection` (Boolean) Super Bot Fight Mode (SBFM) to enable static resource protection. Enable if static resources on your application need bot protection. Note: Static resource protection can also result in legitimate traffic being blocked. +- `sbfm_verified_bots` (String) Super Bot Fight Mode (SBFM) action to take on verified bots requests. +- `suppress_session_score` (Boolean) Whether to disable tracking the highest bot score for a session in the Bot Management cookie. + +### Read-Only + +- `id` (String) The ID of this resource. +- `using_latest_model` (Boolean) A read-only field that indicates whether the zone currently is running the latest ML model. + +## Import + +Import is supported using the following syntax: + +```shell +# Use the Zone ID to import. +$ terraform import cloudflare_bot_management.default +``` diff --git a/examples/resources/cloudflare_bot_management/import.sh b/examples/resources/cloudflare_bot_management/import.sh new file mode 100644 index 0000000000..b06155e97b --- /dev/null +++ b/examples/resources/cloudflare_bot_management/import.sh @@ -0,0 +1,2 @@ +# Use the Zone ID to import. +$ terraform import cloudflare_bot_management.default diff --git a/examples/resources/cloudflare_bot_management/resource.tf b/examples/resources/cloudflare_bot_management/resource.tf new file mode 100644 index 0000000000..909e4e0eb7 --- /dev/null +++ b/examples/resources/cloudflare_bot_management/resource.tf @@ -0,0 +1,12 @@ +# Bot Management +resource "cloudflare_bot_management" "sbfm_biz_example" { + zone_id = "0da42c8d2132a9ddaf714f9e7c920711" + + enable_js = true + + sbfm_definitely_automated = "block" + sbfm_likely_automated = "managed_challenge" + sbfm_verified_bots = "allow" + sbfm_static_resource_protection = false + optimize_wordpress = true +} \ No newline at end of file diff --git a/internal/sdkv2provider/provider.go b/internal/sdkv2provider/provider.go index de2bdeb565..e2e09605c6 100644 --- a/internal/sdkv2provider/provider.go +++ b/internal/sdkv2provider/provider.go @@ -241,6 +241,7 @@ func New(version string) func() *schema.Provider { "cloudflare_spectrum_application": resourceCloudflareSpectrumApplication(), "cloudflare_split_tunnel": resourceCloudflareSplitTunnel(), "cloudflare_static_route": resourceCloudflareStaticRoute(), + "cloudflare_bot_management": resourceCloudflareBotManagement(), "cloudflare_teams_account": resourceCloudflareTeamsAccount(), "cloudflare_teams_list": resourceCloudflareTeamsList(), "cloudflare_teams_location": resourceCloudflareTeamsLocation(), diff --git a/internal/sdkv2provider/resource_cloudflare_bot_management.go b/internal/sdkv2provider/resource_cloudflare_bot_management.go new file mode 100644 index 0000000000..008fd82f75 --- /dev/null +++ b/internal/sdkv2provider/resource_cloudflare_bot_management.go @@ -0,0 +1,154 @@ +package sdkv2provider + +import ( + "context" + "fmt" + + "github.com/MakeNowJust/heredoc/v2" + "github.com/cloudflare/cloudflare-go" + "github.com/cloudflare/terraform-provider-cloudflare/internal/consts" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/pkg/errors" +) + +func resourceCloudflareBotManagement() *schema.Resource { + return &schema.Resource{ + Schema: resourceCloudflareBotManagementSchema(), + CreateContext: resourceCloudflareBotManagementCreate, + ReadContext: resourceCloudflareBotManagementRead, + UpdateContext: resourceCloudflareBotManagementUpdate, + DeleteContext: resourceCloudflareBotManagementDelete, + Importer: &schema.ResourceImporter{ + StateContext: resourceCloudflareBotManagementImport, + }, + Description: heredoc.Doc(`Provides a resource to configure Bot Management. + Specifically, this resource can be used to manage: + - **Bot Fight Mode** + - **Super Bot Fight Mode** + - **Bot Management for Enterprise** + `), + } +} + +func resourceCloudflareBotManagementCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + return resourceCloudflareBotManagementUpdate(ctx, d, meta) +} + +func resourceCloudflareBotManagementRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*cloudflare.API) + zoneID := d.Get(consts.ZoneIDSchemaKey).(string) + + bm, err := client.GetBotManagement(ctx, cloudflare.ZoneIdentifier(zoneID)) + if err != nil { + return diag.FromErr(fmt.Errorf("failed to fetch Bot Management Configuration: %w", err)) + } + + parseBotManagementResource(bm, d) + + return nil +} + +func resourceCloudflareBotManagementUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*cloudflare.API) + zoneID := d.Get(consts.ZoneIDSchemaKey).(string) + + params := buildBotManagementParams(d) + + bm, err := client.UpdateBotManagement(ctx, cloudflare.ZoneIdentifier(zoneID), params) + if err != nil { + return diag.FromErr(errors.Wrap(err, "failed to update Bot Management Configuration")) + } + + parseBotManagementResource(bm, d) + + return nil +} + +// Deletion of bot management configuration is not something we support, we will use a dumby handler for now +func resourceCloudflareBotManagementDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + return nil +} + +func resourceCloudflareBotManagementImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + client := meta.(*cloudflare.API) + zoneID := d.Id() + + bm, err := client.GetBotManagement(ctx, cloudflare.ZoneIdentifier(zoneID)) + if err != nil { + return nil, fmt.Errorf("failed to fetch Bot Management Configuration %s", zoneID) + } + + parseBotManagementResource(bm, d) + + return []*schema.ResourceData{d}, nil +} + +func buildBotManagementParams(d *schema.ResourceData) cloudflare.UpdateBotManagementParams { + bm := cloudflare.UpdateBotManagementParams{} + + if val, exists := d.GetOkExists("enable_js"); exists { + bm.EnableJS = cloudflare.BoolPtr(val.(bool)) + } + if val, exists := d.GetOkExists("fight_mode"); exists { + bm.EnableJS = cloudflare.BoolPtr(val.(bool)) + } + + if val, exists := d.GetOkExists("sbfm_definitely_automated"); exists { + bm.SBFMDefinitelyAutomated = cloudflare.StringPtr(val.(string)) + } + if val, exists := d.GetOkExists("sbfm_likely_automated"); exists { + bm.SBFMLikelyAutomated = cloudflare.StringPtr(val.(string)) + } + if val, exists := d.GetOkExists("sbfm_verified_bots"); exists { + bm.SBFMVerifiedBots = cloudflare.StringPtr(val.(string)) + } + if val, exists := d.GetOkExists("sbfm_static_resource_protection"); exists { + bm.SBFMStaticResourceProtection = cloudflare.BoolPtr(val.(bool)) + } + if val, exists := d.GetOkExists("optimize_wordpress"); exists { + bm.OptimizeWordpress = cloudflare.BoolPtr(val.(bool)) + } + + if val, exists := d.GetOkExists("suppress_session_score"); exists { + bm.SuppressSessionScore = cloudflare.BoolPtr(val.(bool)) + } + if val, exists := d.GetOkExists("auto_update_model"); exists { + bm.AutoUpdateModel = cloudflare.BoolPtr(val.(bool)) + } + + return bm +} + +func parseBotManagementResource(bm cloudflare.BotManagement, d *schema.ResourceData) { + if bm.EnableJS != nil { + d.Set("enable_js", bm.EnableJS) + } + if bm.FightMode != nil { + d.Set("fight_mode", bm.FightMode) + } + if bm.SBFMDefinitelyAutomated != nil { + d.Set("sbfm_definitely_automated", bm.SBFMDefinitelyAutomated) + } + if bm.SBFMLikelyAutomated != nil { + d.Set("sbfm_likely_automated", bm.SBFMLikelyAutomated) + } + if bm.SBFMVerifiedBots != nil { + d.Set("sbfm_verified_bots", bm.SBFMVerifiedBots) + } + if bm.SBFMStaticResourceProtection != nil { + d.Set("sbfm_static_resource_protection", bm.SBFMStaticResourceProtection) + } + if bm.OptimizeWordpress != nil { + d.Set("optimize_wordpress", bm.OptimizeWordpress) + } + if bm.SuppressSessionScore != nil { + d.Set("suppress_session_score", bm.SuppressSessionScore) + } + if bm.AutoUpdateModel != nil { + d.Set("auto_update_model", *bm.AutoUpdateModel) + } + if bm.UsingLatestModel != nil { + d.Set("using_latest_model", bm.UsingLatestModel) + } +} diff --git a/internal/sdkv2provider/resource_cloudflare_bot_management_test.go b/internal/sdkv2provider/resource_cloudflare_bot_management_test.go new file mode 100644 index 0000000000..290656146d --- /dev/null +++ b/internal/sdkv2provider/resource_cloudflare_bot_management_test.go @@ -0,0 +1,106 @@ +package sdkv2provider + +import ( + "fmt" + "os" + "testing" + + "github.com/cloudflare/cloudflare-go" + "github.com/cloudflare/terraform-provider-cloudflare/internal/consts" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestZoneBotManagement_SBFM(t *testing.T) { + rnd := generateRandomResourceName() + resourceID := "cloudflare_api_shield." + rnd + zoneID := os.Getenv("CLOUDFLARE_ZONE_ID") + + sbfmConfig := cloudflare.BotManagement{ + EnableJS: cloudflare.BoolPtr(true), + SBFMDefinitelyAutomated: cloudflare.StringPtr("managed_challenge"), + SBFMLikelyAutomated: cloudflare.StringPtr("block"), + SBFMVerifiedBots: cloudflare.StringPtr("allow"), + SBFMStaticResourceProtection: cloudflare.BoolPtr(false), + OptimizeWordpress: cloudflare.BoolPtr(true), + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: providerFactories, + Steps: []resource.TestStep{ + { + Config: testCloudflareBotManagementSBFM(rnd, zoneID, sbfmConfig), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceID, consts.ZoneIDSchemaKey, zoneID), + resource.TestCheckResourceAttr(resourceID, "enable_js", "true"), + resource.TestCheckResourceAttr(resourceID, "sbfm_definitely_automated", "managed_challenge"), + resource.TestCheckResourceAttr(resourceID, "sbfm_likely_automated", "block"), + resource.TestCheckResourceAttr(resourceID, "sbfm_verified_bots", "allow"), + resource.TestCheckResourceAttr(resourceID, "sbfm_static_resource_protection", "false"), + resource.TestCheckResourceAttr(resourceID, "optimize_wordpress", "true"), + ), + }, + }, + }) +} + +func TestZoneBotManagement_Unentitled(t *testing.T) { + rnd := generateRandomResourceName() + resourceID := "cloudflare_api_shield." + rnd + zoneID := os.Getenv("CLOUDFLARE_ZONE_ID") + + bmEntConfig := cloudflare.BotManagement{ + EnableJS: cloudflare.BoolPtr(true), + SuppressSessionScore: cloudflare.BoolPtr(false), + AutoUpdateModel: cloudflare.BoolPtr(true), + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: providerFactories, + Steps: []resource.TestStep{ + { + Config: testCloudflareBotManagementEntSubscription(rnd, zoneID, bmEntConfig), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceID, consts.ZoneIDSchemaKey, zoneID), + resource.TestCheckResourceAttr(resourceID, "enable_js", "true"), + resource.TestCheckResourceAttr(resourceID, "suppress_session_score", "false"), + resource.TestCheckResourceAttr(resourceID, "auto_update_model", "false"), + ), + }, + }, + }) +} + +func testCloudflareBotManagementSBFM(resourceName, rnd string, bm cloudflare.BotManagement) string { + return fmt.Sprintf(` + resource "cloudflare_bot_management" "%[1]s" { + zone_id = "%[2]s" + + enable_js = "%[3]t" + + sbfm_definitely_automated = "%[4]s" + sbfm_likely_automated = "%[5]s" + sbfm_verified_bots = "%[6]s" + sbfm_static_resource_protection = "%[7]t" + optimize_wordpress = "%[8]t" + } +`, resourceName, rnd, + *bm.EnableJS, *bm.SBFMDefinitelyAutomated, + *bm.SBFMLikelyAutomated, *bm.SBFMVerifiedBots, + *bm.SBFMStaticResourceProtection, *bm.OptimizeWordpress) +} + +func testCloudflareBotManagementEntSubscription(resourceName, rnd string, bm cloudflare.BotManagement) string { + return fmt.Sprintf(` + resource "cloudflare_bot_management" "%[1]s" { + zone_id = "%[2]s" + + enable_js = "%[3]t" + + suppress_session_score = "%[4]t" + auto_update_model = "%[5]t" + } +`, resourceName, rnd, + *bm.EnableJS, *bm.SuppressSessionScore, *bm.AutoUpdateModel) +} diff --git a/internal/sdkv2provider/schema_cloudflare_bot_management.go b/internal/sdkv2provider/schema_cloudflare_bot_management.go new file mode 100644 index 0000000000..978203b45a --- /dev/null +++ b/internal/sdkv2provider/schema_cloudflare_bot_management.go @@ -0,0 +1,67 @@ +package sdkv2provider + +import ( + "github.com/cloudflare/terraform-provider-cloudflare/internal/consts" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceCloudflareBotManagementSchema() map[string]*schema.Schema { + return map[string]*schema.Schema{ + consts.ZoneIDSchemaKey: { + Description: consts.ZoneIDSchemaDescription, + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "enable_js": { + Type: schema.TypeBool, + Optional: true, + Description: "Use lightweight, invisible JavaScript detections to improve Bot Management. [Learn more about JavaScript Detections](https://developers.cloudflare.com/bots/reference/javascript-detections/).", + }, + "fight_mode": { + Type: schema.TypeBool, + Optional: true, + Description: "Whether to enable Bot Fight Mode.", + }, + "sbfm_definitely_automated": { + Type: schema.TypeString, + Optional: true, + Description: "Super Bot Fight Mode (SBFM) action to take on definitely automated requests.", + }, + "sbfm_likely_automated": { + Type: schema.TypeString, + Optional: true, + Description: " Super Bot Fight Mode (SBFM) action to take on likely automated requests.", + }, + "sbfm_verified_bots": { + Type: schema.TypeString, + Optional: true, + Description: "Super Bot Fight Mode (SBFM) action to take on verified bots requests.", + }, + "sbfm_static_resource_protection": { + Type: schema.TypeBool, + Optional: true, + Description: "Super Bot Fight Mode (SBFM) to enable static resource protection. Enable if static resources on your application need bot protection. Note: Static resource protection can also result in legitimate traffic being blocked.", + }, + "optimize_wordpress": { + Type: schema.TypeBool, + Optional: true, + Description: "Whether to optimize Super Bot Fight Mode protections for Wordpress.", + }, + "suppress_session_score": { + Type: schema.TypeBool, + Optional: true, + Description: "Whether to disable tracking the highest bot score for a session in the Bot Management cookie.", + }, + "auto_update_model": { + Type: schema.TypeBool, + Optional: true, + Description: "Automatically update to the newest bot detection models created by Cloudflare as they are released. [Learn more.](https://developers.cloudflare.com/bots/reference/machine-learning-models#model-versions-and-release-notes)", + }, + "using_latest_model": { + Type: schema.TypeBool, + Computed: true, + Description: "A read-only field that indicates whether the zone currently is running the latest ML model.", + }, + } +} From e95fbbae10e1e1ec9d3242228f56e2cd80f5a912 Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Wed, 16 Aug 2023 14:20:14 +1000 Subject: [PATCH 2/8] fix test resource references --- .../sdkv2provider/resource_cloudflare_bot_management_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/sdkv2provider/resource_cloudflare_bot_management_test.go b/internal/sdkv2provider/resource_cloudflare_bot_management_test.go index 290656146d..3d6202c341 100644 --- a/internal/sdkv2provider/resource_cloudflare_bot_management_test.go +++ b/internal/sdkv2provider/resource_cloudflare_bot_management_test.go @@ -12,7 +12,7 @@ import ( func TestZoneBotManagement_SBFM(t *testing.T) { rnd := generateRandomResourceName() - resourceID := "cloudflare_api_shield." + rnd + resourceID := "cloudflare_bot_management." + rnd zoneID := os.Getenv("CLOUDFLARE_ZONE_ID") sbfmConfig := cloudflare.BotManagement{ @@ -46,7 +46,7 @@ func TestZoneBotManagement_SBFM(t *testing.T) { func TestZoneBotManagement_Unentitled(t *testing.T) { rnd := generateRandomResourceName() - resourceID := "cloudflare_api_shield." + rnd + resourceID := "cloudflare_bot_management." + rnd zoneID := os.Getenv("CLOUDFLARE_ZONE_ID") bmEntConfig := cloudflare.BotManagement{ From d8d6d5e9e5777d323afce06ef4dbd1d8b9ebf156 Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Wed, 16 Aug 2023 14:20:51 +1000 Subject: [PATCH 3/8] add `Import` test coverage --- .../sdkv2provider/resource_cloudflare_bot_management_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/internal/sdkv2provider/resource_cloudflare_bot_management_test.go b/internal/sdkv2provider/resource_cloudflare_bot_management_test.go index 3d6202c341..0af457b692 100644 --- a/internal/sdkv2provider/resource_cloudflare_bot_management_test.go +++ b/internal/sdkv2provider/resource_cloudflare_bot_management_test.go @@ -40,6 +40,11 @@ func TestZoneBotManagement_SBFM(t *testing.T) { resource.TestCheckResourceAttr(resourceID, "optimize_wordpress", "true"), ), }, + { + ResourceName: resourceID, + ImportState: true, + ImportStateVerify: true, + }, }, }) } From af01621651dea2c1c46f8e459ec61b70f471d0ba Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Wed, 16 Aug 2023 14:21:17 +1000 Subject: [PATCH 4/8] fix test name prefix for acceptance tests --- .../resource_cloudflare_bot_management_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/sdkv2provider/resource_cloudflare_bot_management_test.go b/internal/sdkv2provider/resource_cloudflare_bot_management_test.go index 0af457b692..56d9b97080 100644 --- a/internal/sdkv2provider/resource_cloudflare_bot_management_test.go +++ b/internal/sdkv2provider/resource_cloudflare_bot_management_test.go @@ -10,7 +10,7 @@ import ( "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) -func TestZoneBotManagement_SBFM(t *testing.T) { +func TestAccCloudflareBotManagement_SBFM(t *testing.T) { rnd := generateRandomResourceName() resourceID := "cloudflare_bot_management." + rnd zoneID := os.Getenv("CLOUDFLARE_ZONE_ID") @@ -49,7 +49,7 @@ func TestZoneBotManagement_SBFM(t *testing.T) { }) } -func TestZoneBotManagement_Unentitled(t *testing.T) { +func TestAccCloudflareBotManagement_Unentitled(t *testing.T) { rnd := generateRandomResourceName() resourceID := "cloudflare_bot_management." + rnd zoneID := os.Getenv("CLOUDFLARE_ZONE_ID") @@ -81,7 +81,7 @@ func testCloudflareBotManagementSBFM(resourceName, rnd string, bm cloudflare.Bot return fmt.Sprintf(` resource "cloudflare_bot_management" "%[1]s" { zone_id = "%[2]s" - + enable_js = "%[3]t" sbfm_definitely_automated = "%[4]s" From 005b66453904d161799e19f2763da6c21020d751 Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Wed, 16 Aug 2023 14:22:27 +1000 Subject: [PATCH 5/8] set `d.Id()` as the anchor for `Create` and `Import` operations --- internal/sdkv2provider/resource_cloudflare_bot_management.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/sdkv2provider/resource_cloudflare_bot_management.go b/internal/sdkv2provider/resource_cloudflare_bot_management.go index 008fd82f75..c478dd12f3 100644 --- a/internal/sdkv2provider/resource_cloudflare_bot_management.go +++ b/internal/sdkv2provider/resource_cloudflare_bot_management.go @@ -32,6 +32,8 @@ func resourceCloudflareBotManagement() *schema.Resource { } func resourceCloudflareBotManagementCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + d.SetId(d.Get(consts.ZoneIDSchemaKey).(string)) + return resourceCloudflareBotManagementUpdate(ctx, d, meta) } @@ -71,8 +73,7 @@ func resourceCloudflareBotManagementDelete(ctx context.Context, d *schema.Resour } func resourceCloudflareBotManagementImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - client := meta.(*cloudflare.API) - zoneID := d.Id() + d.SetId(d.Id()) bm, err := client.GetBotManagement(ctx, cloudflare.ZoneIdentifier(zoneID)) if err != nil { From c964809788a0cd33902f0233e667ea0f4fbc1ef3 Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Wed, 16 Aug 2023 14:24:57 +1000 Subject: [PATCH 6/8] `make docs` --- docs/resources/bot_management.md | 26 ++++++++----------- .../cloudflare_bot_management/import.sh | 3 +-- .../cloudflare_bot_management/resource.tf | 19 ++++++-------- .../resource_cloudflare_bot_management.go | 2 ++ 4 files changed, 22 insertions(+), 28 deletions(-) diff --git a/docs/resources/bot_management.md b/docs/resources/bot_management.md index 5f7192f016..87496cc557 100644 --- a/docs/resources/bot_management.md +++ b/docs/resources/bot_management.md @@ -4,15 +4,15 @@ subcategory: "" description: |- Provides a resource to configure Bot Management. Specifically, this resource can be used to manage: - - Bot Fight Mode - - Super Bot Fight Mode - - Bot Management for Enterprise + Bot Fight ModeSuper Bot Fight ModeBot Management for Enterprise --- # cloudflare_bot_management (Resource) Provides a resource to configure Bot Management. + Specifically, this resource can be used to manage: + - **Bot Fight Mode** - **Super Bot Fight Mode** - **Bot Management for Enterprise** @@ -20,17 +20,14 @@ Specifically, this resource can be used to manage: ## Example Usage ```terraform -# Bot Management -resource "cloudflare_bot_management" "sbfm_biz_example" { - zone_id = "0da42c8d2132a9ddaf714f9e7c920711" - - enable_js = true - - sbfm_definitely_automated = "block" - sbfm_likely_automated = "managed_challenge" - sbfm_verified_bots = "allow" +resource "cloudflare_bot_management" "example" { + zone_id = "0da42c8d2132a9ddaf714f9e7c920711" + enable_js = true + sbfm_definitely_automated = "block" + sbfm_likely_automated = "managed_challenge" + sbfm_verified_bots = "allow" sbfm_static_resource_protection = false - optimize_wordpress = true + optimize_wordpress = true } ``` @@ -62,6 +59,5 @@ resource "cloudflare_bot_management" "sbfm_biz_example" { Import is supported using the following syntax: ```shell -# Use the Zone ID to import. -$ terraform import cloudflare_bot_management.default +$ terraform import cloudflare_bot_management.example ``` diff --git a/examples/resources/cloudflare_bot_management/import.sh b/examples/resources/cloudflare_bot_management/import.sh index b06155e97b..b1c195ebd4 100644 --- a/examples/resources/cloudflare_bot_management/import.sh +++ b/examples/resources/cloudflare_bot_management/import.sh @@ -1,2 +1 @@ -# Use the Zone ID to import. -$ terraform import cloudflare_bot_management.default +$ terraform import cloudflare_bot_management.example diff --git a/examples/resources/cloudflare_bot_management/resource.tf b/examples/resources/cloudflare_bot_management/resource.tf index 909e4e0eb7..c289ed17d1 100644 --- a/examples/resources/cloudflare_bot_management/resource.tf +++ b/examples/resources/cloudflare_bot_management/resource.tf @@ -1,12 +1,9 @@ -# Bot Management -resource "cloudflare_bot_management" "sbfm_biz_example" { - zone_id = "0da42c8d2132a9ddaf714f9e7c920711" - - enable_js = true - - sbfm_definitely_automated = "block" - sbfm_likely_automated = "managed_challenge" - sbfm_verified_bots = "allow" +resource "cloudflare_bot_management" "example" { + zone_id = "0da42c8d2132a9ddaf714f9e7c920711" + enable_js = true + sbfm_definitely_automated = "block" + sbfm_likely_automated = "managed_challenge" + sbfm_verified_bots = "allow" sbfm_static_resource_protection = false - optimize_wordpress = true -} \ No newline at end of file + optimize_wordpress = true +} diff --git a/internal/sdkv2provider/resource_cloudflare_bot_management.go b/internal/sdkv2provider/resource_cloudflare_bot_management.go index c478dd12f3..4b8b1146b9 100644 --- a/internal/sdkv2provider/resource_cloudflare_bot_management.go +++ b/internal/sdkv2provider/resource_cloudflare_bot_management.go @@ -23,7 +23,9 @@ func resourceCloudflareBotManagement() *schema.Resource { StateContext: resourceCloudflareBotManagementImport, }, Description: heredoc.Doc(`Provides a resource to configure Bot Management. + Specifically, this resource can be used to manage: + - **Bot Fight Mode** - **Super Bot Fight Mode** - **Bot Management for Enterprise** From f927a9d213dc772b4bfcce5ef7eca3e5df63a915 Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Wed, 16 Aug 2023 14:25:53 +1000 Subject: [PATCH 7/8] move parsing of remote into `resourceCloudflareBotManagementRead` --- .../resource_cloudflare_bot_management.go | 94 +++++++++---------- 1 file changed, 45 insertions(+), 49 deletions(-) diff --git a/internal/sdkv2provider/resource_cloudflare_bot_management.go b/internal/sdkv2provider/resource_cloudflare_bot_management.go index 4b8b1146b9..39a6906a76 100644 --- a/internal/sdkv2provider/resource_cloudflare_bot_management.go +++ b/internal/sdkv2provider/resource_cloudflare_bot_management.go @@ -41,14 +41,50 @@ func resourceCloudflareBotManagementCreate(ctx context.Context, d *schema.Resour func resourceCloudflareBotManagementRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client := meta.(*cloudflare.API) - zoneID := d.Get(consts.ZoneIDSchemaKey).(string) - - bm, err := client.GetBotManagement(ctx, cloudflare.ZoneIdentifier(zoneID)) + bm, err := client.GetBotManagement(ctx, cloudflare.ZoneIdentifier(d.Id())) if err != nil { - return diag.FromErr(fmt.Errorf("failed to fetch Bot Management Configuration: %w", err)) + return diag.FromErr(fmt.Errorf("failed to fetch bot management configuration: %w", err)) + } + + if bm.EnableJS != nil { + d.Set("enable_js", bm.EnableJS) + } + + if bm.FightMode != nil { + d.Set("fight_mode", bm.FightMode) + } + + if bm.SBFMDefinitelyAutomated != nil { + d.Set("sbfm_definitely_automated", bm.SBFMDefinitelyAutomated) + } + + if bm.SBFMLikelyAutomated != nil { + d.Set("sbfm_likely_automated", bm.SBFMLikelyAutomated) + } + + if bm.SBFMVerifiedBots != nil { + d.Set("sbfm_verified_bots", bm.SBFMVerifiedBots) + } + + if bm.SBFMStaticResourceProtection != nil { + d.Set("sbfm_static_resource_protection", bm.SBFMStaticResourceProtection) } - parseBotManagementResource(bm, d) + if bm.OptimizeWordpress != nil { + d.Set("optimize_wordpress", bm.OptimizeWordpress) + } + + if bm.SuppressSessionScore != nil { + d.Set("suppress_session_score", bm.SuppressSessionScore) + } + + if bm.AutoUpdateModel != nil { + d.Set("auto_update_model", bm.AutoUpdateModel) + } + + if bm.UsingLatestModel != nil { + d.Set("using_latest_model", bm.UsingLatestModel) + } return nil } @@ -59,14 +95,12 @@ func resourceCloudflareBotManagementUpdate(ctx context.Context, d *schema.Resour params := buildBotManagementParams(d) - bm, err := client.UpdateBotManagement(ctx, cloudflare.ZoneIdentifier(zoneID), params) + _, err := client.UpdateBotManagement(ctx, cloudflare.ZoneIdentifier(zoneID), params) if err != nil { - return diag.FromErr(errors.Wrap(err, "failed to update Bot Management Configuration")) + return diag.FromErr(errors.Wrap(err, "failed to update bot management configuration")) } - parseBotManagementResource(bm, d) - - return nil + return resourceCloudflareBotManagementRead(ctx, d, meta) } // Deletion of bot management configuration is not something we support, we will use a dumby handler for now @@ -77,12 +111,7 @@ func resourceCloudflareBotManagementDelete(ctx context.Context, d *schema.Resour func resourceCloudflareBotManagementImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { d.SetId(d.Id()) - bm, err := client.GetBotManagement(ctx, cloudflare.ZoneIdentifier(zoneID)) - if err != nil { - return nil, fmt.Errorf("failed to fetch Bot Management Configuration %s", zoneID) - } - - parseBotManagementResource(bm, d) + resourceCloudflareBotManagementRead(ctx, d, meta) return []*schema.ResourceData{d}, nil } @@ -122,36 +151,3 @@ func buildBotManagementParams(d *schema.ResourceData) cloudflare.UpdateBotManage return bm } - -func parseBotManagementResource(bm cloudflare.BotManagement, d *schema.ResourceData) { - if bm.EnableJS != nil { - d.Set("enable_js", bm.EnableJS) - } - if bm.FightMode != nil { - d.Set("fight_mode", bm.FightMode) - } - if bm.SBFMDefinitelyAutomated != nil { - d.Set("sbfm_definitely_automated", bm.SBFMDefinitelyAutomated) - } - if bm.SBFMLikelyAutomated != nil { - d.Set("sbfm_likely_automated", bm.SBFMLikelyAutomated) - } - if bm.SBFMVerifiedBots != nil { - d.Set("sbfm_verified_bots", bm.SBFMVerifiedBots) - } - if bm.SBFMStaticResourceProtection != nil { - d.Set("sbfm_static_resource_protection", bm.SBFMStaticResourceProtection) - } - if bm.OptimizeWordpress != nil { - d.Set("optimize_wordpress", bm.OptimizeWordpress) - } - if bm.SuppressSessionScore != nil { - d.Set("suppress_session_score", bm.SuppressSessionScore) - } - if bm.AutoUpdateModel != nil { - d.Set("auto_update_model", *bm.AutoUpdateModel) - } - if bm.UsingLatestModel != nil { - d.Set("using_latest_model", bm.UsingLatestModel) - } -} From 0f45ad94868532677b530b77dc54a9fcef1f8092 Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Wed, 16 Aug 2023 14:26:11 +1000 Subject: [PATCH 8/8] update test case with expected failure for unentitled field --- internal/sdkv2provider/resource_cloudflare_bot_management.go | 4 +++- .../sdkv2provider/resource_cloudflare_bot_management_test.go | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/internal/sdkv2provider/resource_cloudflare_bot_management.go b/internal/sdkv2provider/resource_cloudflare_bot_management.go index 39a6906a76..83499aef72 100644 --- a/internal/sdkv2provider/resource_cloudflare_bot_management.go +++ b/internal/sdkv2provider/resource_cloudflare_bot_management.go @@ -103,13 +103,15 @@ func resourceCloudflareBotManagementUpdate(ctx context.Context, d *schema.Resour return resourceCloudflareBotManagementRead(ctx, d, meta) } -// Deletion of bot management configuration is not something we support, we will use a dumby handler for now +// Deletion of bot management configuration is not something we support, we will +// use a dummy handler for now. func resourceCloudflareBotManagementDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { return nil } func resourceCloudflareBotManagementImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { d.SetId(d.Id()) + d.Set(consts.ZoneIDSchemaKey, d.Id()) resourceCloudflareBotManagementRead(ctx, d, meta) diff --git a/internal/sdkv2provider/resource_cloudflare_bot_management_test.go b/internal/sdkv2provider/resource_cloudflare_bot_management_test.go index 56d9b97080..6cc36ae3c5 100644 --- a/internal/sdkv2provider/resource_cloudflare_bot_management_test.go +++ b/internal/sdkv2provider/resource_cloudflare_bot_management_test.go @@ -3,6 +3,7 @@ package sdkv2provider import ( "fmt" "os" + "regexp" "testing" "github.com/cloudflare/cloudflare-go" @@ -57,7 +58,7 @@ func TestAccCloudflareBotManagement_Unentitled(t *testing.T) { bmEntConfig := cloudflare.BotManagement{ EnableJS: cloudflare.BoolPtr(true), SuppressSessionScore: cloudflare.BoolPtr(false), - AutoUpdateModel: cloudflare.BoolPtr(true), + AutoUpdateModel: cloudflare.BoolPtr(false), } resource.Test(t, resource.TestCase{ @@ -72,6 +73,7 @@ func TestAccCloudflareBotManagement_Unentitled(t *testing.T) { resource.TestCheckResourceAttr(resourceID, "suppress_session_score", "false"), resource.TestCheckResourceAttr(resourceID, "auto_update_model", "false"), ), + ExpectError: regexp.MustCompile("zone not entitled to disable"), }, }, })