Skip to content

Commit

Permalink
WDAPI-1041 add device settings policy APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
Tyler Stanish committed Nov 10, 2022
1 parent 1db7aa6 commit 4b10311
Show file tree
Hide file tree
Showing 13 changed files with 733 additions and 20 deletions.
11 changes: 11 additions & 0 deletions .changelog/1926.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
```release-note:new-resource
cloudflare_device_policy
```

```release-note:enhancement
resource/cloudflare_split_tunnel: Add configuring split tunnel for device policies
```

```release-note:enhancement
resource/cloudflare_fallback_domain: Add creating fallback domains for device policies
```
1 change: 1 addition & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ func New(version string) func() *schema.Provider {
"cloudflare_custom_hostname": resourceCloudflareCustomHostname(),
"cloudflare_custom_pages": resourceCloudflareCustomPages(),
"cloudflare_custom_ssl": resourceCloudflareCustomSsl(),
"cloudflare_device_policy": resourceCloudflareDeviceSettingsPolicy(),
"cloudflare_device_policy_certificates": resourceCloudflareDevicePolicyCertificates(),
"cloudflare_device_posture_integration": resourceCloudflareDevicePostureIntegration(),
"cloudflare_device_posture_rule": resourceCloudflareDevicePostureRule(),
Expand Down
261 changes: 261 additions & 0 deletions internal/provider/resource_cloudflare_device_settings_policy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
package provider

import (
"context"
"fmt"
"strings"

"github.com/cloudflare/cloudflare-go"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func resourceCloudflareDeviceSettingsPolicy() *schema.Resource {
return &schema.Resource{
Schema: resourceCloudflareDeviceSettingsPolicySchema(),
CreateContext: resourceCloudflareDeviceSettingsPolicyCreate,
ReadContext: resourceCloudflareDeviceSettingsPolicyRead,
UpdateContext: resourceCloudflareDeviceSettingsPolicyUpdate,
DeleteContext: resourceCloudflareDeviceSettingsPolicyDelete,
Importer: &schema.ResourceImporter{
StateContext: resourceCloudflareDeviceSettingsPolicyImport,
},
}
}

func resourceCloudflareDeviceSettingsPolicyCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*cloudflare.API)
accountID := d.Get("account_id").(string)
defaultPolicy := d.Get("default").(bool)

tflog.Debug(ctx, fmt.Sprintf("Creating Cloudflare device settings policy: accountID=%s default=%t", accountID, defaultPolicy))

if defaultPolicy {
d.SetId(fmt.Sprintf("%s/default", accountID))
return resourceCloudflareDeviceSettingsPolicyUpdate(ctx, d, meta)
}

req, err := buildDeviceSettingsPolicyRequest(d)
if err != nil {
return diag.FromErr(fmt.Errorf("error creating Cloudflare device settings policy request: %q: %w", accountID, err))
}

policy, err := client.CreateDeviceSettingsPolicy(ctx, accountID, req)
if err != nil {
return diag.FromErr(fmt.Errorf("error creating Cloudflare device settings policy %q: %w", accountID, err))
}

if policy.Result.PolicyID == nil {
return diag.FromErr(fmt.Errorf("error creating Cloudflare device settings policy: returned policyID was missing after creating policy for account: %q", accountID))
}
d.SetId(fmt.Sprintf("%s/%s", accountID, *policy.Result.PolicyID))
return resourceCloudflareDeviceSettingsPolicyRead(ctx, d, meta)
}

func resourceCloudflareDeviceSettingsPolicyUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*cloudflare.API)
accountID, policyID, err := parseDeviceSettingsID(d.Id())
if err != nil {
return diag.FromErr(err)
}

tflog.Debug(ctx, fmt.Sprintf("Updating Cloudflare device settings policy: accountID=%s policyID=%s", accountID, policyID))

req, err := buildDeviceSettingsPolicyRequest(d)
if err != nil {
return diag.FromErr(fmt.Errorf("error creating Cloudflare device settings policy request: %q: %w", accountID, err))
}

if policyID == "default" {
_, err = client.UpdateDefaultDeviceSettingsPolicy(ctx, accountID, req)
} else {
_, err = client.UpdateDeviceSettingsPolicy(ctx, accountID, policyID, req)
}
if err != nil {
return diag.FromErr(fmt.Errorf("error updating Cloudflare device settings policy %q: %w", accountID, err))
}

return resourceCloudflareDeviceSettingsPolicyRead(ctx, d, meta)
}

func resourceCloudflareDeviceSettingsPolicyRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*cloudflare.API)
accountID, policyID, err := parseDeviceSettingsID(d.Id())
if err != nil {
return diag.FromErr(err)
}

var policy cloudflare.DeviceSettingsPolicyResponse
if policyID == "default" {
policy, err = client.GetDefaultDeviceSettingsPolicy(ctx, accountID)
} else {
policy, err = client.GetDeviceSettingsPolicy(ctx, accountID, policyID)
}

if err != nil {
return diag.FromErr(fmt.Errorf("error reading device settings policy %q %s: %w", accountID, policyID, err))
}

if err := d.Set("disable_auto_fallback", policy.Result.DisableAutoFallback); err != nil {
return diag.FromErr(fmt.Errorf("error parsing disable_auto_fallback"))
}
if err := d.Set("captive_portal", policy.Result.CaptivePortal); err != nil {
return diag.FromErr(fmt.Errorf("error parsing captive_portal"))
}
if err := d.Set("allow_mode_switch", policy.Result.AllowModeSwitch); err != nil {
return diag.FromErr(fmt.Errorf("error parsing allow_mode_switch"))
}
if err := d.Set("switch_locked", policy.Result.SwitchLocked); err != nil {
return diag.FromErr(fmt.Errorf("error parsing switch_locked"))
}
if err := d.Set("allow_updates", policy.Result.AllowUpdates); err != nil {
return diag.FromErr(fmt.Errorf("error parsing allow_updates"))
}
if err := d.Set("auto_connect", policy.Result.AutoConnect); err != nil {
return diag.FromErr(fmt.Errorf("error parsing auto_connect"))
}
if err := d.Set("allowed_to_leave", policy.Result.AllowedToLeave); err != nil {
return diag.FromErr(fmt.Errorf("error parsing allowed_to_leave"))
}
if err := d.Set("support_url", policy.Result.SupportURL); err != nil {
return diag.FromErr(fmt.Errorf("error parsing support_url"))
}
if err := d.Set("default", policy.Result.Default); err != nil {
return diag.FromErr(fmt.Errorf("error setting default"))
}
if err := d.Set("service_mode_v2_mode", policy.Result.ServiceModeV2.Mode); err != nil {
return diag.FromErr(fmt.Errorf("error setting service_mode_v2_mode"))
}
if err := d.Set("service_mode_v2_port", policy.Result.ServiceModeV2.Port); err != nil {
return diag.FromErr(fmt.Errorf("error setting service_mode_v2_port"))
}
// ignore setting forbidden fields for default policies
if policy.Result.Name != nil {
if err := d.Set("name", policy.Result.Name); err != nil {
return diag.FromErr(fmt.Errorf("error parsing name"))
}
}
if policy.Result.Precedence != nil {
if err := d.Set("precedence", apiToProviderRulePrecedence(uint64(*policy.Result.Precedence), d.Get("name").(string))); err != nil {
return diag.FromErr(fmt.Errorf("error parsing precedence"))
}
}
if policy.Result.Match != nil {
if err := d.Set("match", policy.Result.Match); err != nil {
return diag.FromErr(fmt.Errorf("error parsing match"))
}
}
if policy.Result.Enabled != nil {
if err := d.Set("enabled", policy.Result.Enabled); err != nil {
return diag.FromErr(fmt.Errorf("error parsing enabled"))
}
}

return nil
}

func resourceCloudflareDeviceSettingsPolicyImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
accountID, policyID, err := parseDeviceSettingsID(d.Id())
if err != nil {
return nil, err
}

tflog.Debug(ctx, fmt.Sprintf("Importing Cloudflare device settings policy: id %s for account %s", policyID, accountID))

d.Set("account_id", accountID)
d.SetId(fmt.Sprintf("%s/%s", accountID, policyID))

resourceCloudflareDeviceSettingsPolicyRead(ctx, d, meta)

return []*schema.ResourceData{d}, nil
}

func resourceCloudflareDeviceSettingsPolicyDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
accountID, policyID, err := parseDeviceSettingsID(d.Id())
if err != nil {
return diag.FromErr(err)
}

client := meta.(*cloudflare.API)
if policyID == "default" {
d.SetId("")
return diag.Diagnostics{diag.Diagnostic{
Severity: diag.Warning,
Summary: "Cannot delete a default policy, instead simply removing from terraform management without deleting",
}}
}

if _, err := client.DeleteDeviceSettingsPolicy(ctx, accountID, policyID); err != nil {
return diag.FromErr(fmt.Errorf("error deleting device settings policy %q: %w", accountID, err))
}

d.SetId("")

return nil
}

func buildDeviceSettingsPolicyRequest(d *schema.ResourceData) (cloudflare.DeviceSettingsPolicyRequest, error) {
defaultPolicy := (d.Get("default").(bool) || d.Id() == fmt.Sprintf("%s/default", d.Get("account_id")))

req := cloudflare.DeviceSettingsPolicyRequest{
DisableAutoFallback: cloudflare.BoolPtr(d.Get("disable_auto_fallback").(bool)),
CaptivePortal: cloudflare.IntPtr(d.Get("captive_portal").(int)),
AllowModeSwitch: cloudflare.BoolPtr(d.Get("allow_mode_switch").(bool)),
SwitchLocked: cloudflare.BoolPtr(d.Get("switch_locked").(bool)),
AllowUpdates: cloudflare.BoolPtr(d.Get("allow_updates").(bool)),
AutoConnect: cloudflare.IntPtr(d.Get("auto_connect").(int)),
AllowedToLeave: cloudflare.BoolPtr(d.Get("allowed_to_leave").(bool)),
SupportURL: cloudflare.StringPtr(d.Get("support_url").(string)),
ServiceModeV2: &cloudflare.ServiceModeV2{
Mode: d.Get("service_mode_v2_mode").(string),
Port: d.Get("service_mode_v2_port").(int),
},
}

name := d.Get("name").(string)
enabled := d.Get("enabled").(bool)
if defaultPolicy && !enabled {
return req, fmt.Errorf("enabled cannot be false for default policies")
}
if !defaultPolicy {
req.Name = &name
req.Enabled = &enabled
}

match, ok := d.GetOk("match")
if defaultPolicy && ok {
return req, fmt.Errorf("match cannot be set for default policies")
}
if !defaultPolicy && !ok {
return req, fmt.Errorf("match must be set for non-default policies")
}
if ok {
matchStr := match.(string)
req.Match = &matchStr
}

precedence, ok := d.GetOk("precedence")
if defaultPolicy && ok {
return req, fmt.Errorf("precedence cannot be set for default policies")
}
if !defaultPolicy && !ok {
return req, fmt.Errorf("precedence must be set for non-default policies")
}
if ok {
precedenceVal := int(providerToApiRulePrecedence(int64(precedence.(int)), d.Get("name").(string)))
req.Precedence = &precedenceVal
}

return req, nil
}

func parseDeviceSettingsID(id string) (string, string, error) {
attributes := strings.SplitN(id, "/", 2)

if len(attributes) != 2 {
return "", "", fmt.Errorf("invalid id (\"%s\") specified, should be in format \"accountID/policyID\" or \"accountID/default\" for the default account policy", id)
}

return attributes[0], attributes[1], nil
}
Loading

0 comments on commit 4b10311

Please sign in to comment.