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

Add new resource for managed networks #2126

Merged
merged 6 commits into from
Jan 10, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/2126.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-resource
cloudflare_device_managed_networks
```
46 changes: 46 additions & 0 deletions docs/resources/device_managed_networks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
layout: "cloudflare"
page_title: "Cloudflare: cloudflare_device_managed_networks"
description: Provides a Cloudflare Device Managed Networks resource.
---

# cloudflare_device_managed_networks

Provides a Cloudflare Device Managed Networks resource. Device managed networks allow for building location-aware device settings policies.

## Example Usage

```hcl
resource "cloudflare_device_managed_networks" "managed_networks" {
account_id = "f037e56e89293a057740de681ac9abbe"
name = "managed-network-1"
type = "tls"
config {
tls_sockaddr = "foobar:1234"
sha256 = "b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c"
}
}

```

## Argument Reference

The following arguments are supported:

- `account_id` - (Required) The account to which the device managed network should be added.
- `name` - (Required)The name of the Device Managed Network. Must be unique.
- `type` - (Required) The type of Device Managed Network. Valid values is `tls`.
- `config` - (Required) The configuration object containing information for the WARP client to detect the managed network.

### Config argument

- `tls_sockaddr` - (Required) The third-party API's URL.
- `sha256` - (Required) The third-party authorization API URL.

## Import

Import is supported using the following syntax:

```shell
$ terraform import cloudflare_device_managed_networks.example <account_id>/<device_managed_networks_id>
```
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
$ terraform import cloudflare_device_managed_networks.example <account_id>/<device_managed_networks_id>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
resource "cloudflare_device_managed_networks" "managed_networks" {
account_id = "f037e56e89293a057740de681ac9abbe"
name = "managed-network-1"
type = "tls"
config {
tls_sockaddr = "foobar:1234"
sha256 = "b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c"
}
}
1 change: 1 addition & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ func New(version string) func() *schema.Provider {
"cloudflare_device_policy_certificates": resourceCloudflareDevicePolicyCertificates(),
"cloudflare_device_posture_integration": resourceCloudflareDevicePostureIntegration(),
"cloudflare_device_posture_rule": resourceCloudflareDevicePostureRule(),
"cloudflare_device_managed_networks": resourceCloudflareDeviceManagedNetworks(),
"cloudflare_dlp_profile": resourceCloudflareDLPProfile(),
"cloudflare_email_routing_address": resourceCloudflareEmailRoutingAddress(),
"cloudflare_email_routing_catch_all": resourceCloudflareEmailRoutingCatchAll(),
Expand Down
154 changes: 154 additions & 0 deletions internal/provider/resource_cloudflare_device_managed_networks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package provider

import (
"context"
"errors"
"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 resourceCloudflareDeviceManagedNetworks() *schema.Resource {
return &schema.Resource{
Schema: resourceCloudflareDeviceManagedNetworksSchema(),
CreateContext: resourceCloudflareDeviceManagedNetworksCreate,
ReadContext: resourceCloudflareDeviceManagedNetworksRead,
UpdateContext: resourceCloudflareDeviceManagedNetworksUpdate,
DeleteContext: resourceCloudflareDeviceManagedNetworksDelete,
Importer: &schema.ResourceImporter{
StateContext: resourceCloudflareDeviceManagedNetworksImport,
},
Description: "Provides a Cloudflare Device Managed Network resource. Device managed networks allow for building location-aware device settings policies.",
}
}

func resourceCloudflareDeviceManagedNetworksRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*cloudflare.API)
identifier := cloudflare.AccountIdentifier(d.Get("account_id").(string))
tflog.Debug(ctx, fmt.Sprintf("Reading Cloudflare Device Managed Network for Id: %+v", d.Id()))

managedNetwork, err := client.GetDeviceManagedNetwork(ctx, identifier, d.Id())

var notFoundError *cloudflare.NotFoundError
if errors.As(err, &notFoundError) {
tflog.Info(ctx, fmt.Sprintf("Device Managed Network %s no longer exists", d.Id()))
d.SetId("")
return nil
}
if err != nil {
return diag.FromErr(fmt.Errorf("error reading Device Managed Network: %w", err))
}

d.Set("name", managedNetwork.Name)
d.Set("type", managedNetwork.Type)
d.Set("config", convertDeviceManagedNetworkConfigToSchema(managedNetwork.Config))

// TODO add config
jacobbednarz marked this conversation as resolved.
Show resolved Hide resolved

return nil
}

func resourceCloudflareDeviceManagedNetworksCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*cloudflare.API)
identifier := cloudflare.AccountIdentifier(d.Get("account_id").(string))

params := cloudflare.CreateDeviceManagedNetworkParams{
Name: d.Get("name").(string),
Type: d.Get("type").(string),
Config: &cloudflare.Config{
TlsSockAddr: d.Get("config.0.tls_sockaddr").(string),
Sha256: d.Get("config.0.sha256").(string),
},
}

tflog.Debug(ctx, fmt.Sprintf("Creating Cloudflare Device Managed Network with params: %+v", params))

managedNetwork, err := client.CreateDeviceManagedNetwork(ctx, identifier, params)

if err != nil {
return diag.FromErr(fmt.Errorf("error creating Device Managed Network with provided config: %w", err))
}

d.SetId(managedNetwork.NetworkID)

return resourceCloudflareDeviceManagedNetworksRead(ctx, d, meta)
}

func resourceCloudflareDeviceManagedNetworksUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*cloudflare.API)
identifier := cloudflare.AccountIdentifier(d.Get("account_id").(string))

updatedDeviceManagedNetworkParams := cloudflare.UpdateDeviceManagedNetworkParams{
NetworkID: d.Id(),
Name: d.Get("name").(string),
Type: d.Get("type").(string),
Config: &cloudflare.Config{
TlsSockAddr: d.Get("config.0.tls_sockaddr").(string),
Sha256: d.Get("config.0.sha256").(string),
},
}

tflog.Debug(ctx, fmt.Sprintf("Updating Cloudflare Device Managed Network with params: %+v", updatedDeviceManagedNetworkParams))

managedNetwork, err := client.UpdateDeviceManagedNetwork(ctx, identifier, updatedDeviceManagedNetworkParams)

if err != nil {
return diag.FromErr(fmt.Errorf("error updating Device Managed Network for ID %q: %w", d.Id(), err))
}
if managedNetwork.NetworkID == "" {
return diag.FromErr(fmt.Errorf("failed to find Network ID in update response; resource was empty"))
}

return resourceCloudflareDeviceManagedNetworksRead(ctx, d, meta)
}

func resourceCloudflareDeviceManagedNetworksDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*cloudflare.API)
identifier := cloudflare.AccountIdentifier(d.Get("account_id").(string))
tflog.Debug(ctx, fmt.Sprintf("Deleting Cloudflare Device Managed Network using ID: %s", d.Id()))

if _, err := client.DeleteManagedNetworks(ctx, identifier, d.Id()); err != nil {
return diag.FromErr(fmt.Errorf("error deleting DLP Profile for ID %q: %w", d.Id(), err))
}

resourceCloudflareDeviceManagedNetworksRead(ctx, d, meta)
return nil
}

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

tflog.Debug(ctx, fmt.Sprintf("Importing Cloudflare Device Managed Network: id %s for account %s", managedNetworkID, accountID))

d.Set("account_id", accountID)
d.SetId(managedNetworkID)

resourceCloudflareDeviceManagedNetworksRead(ctx, d, meta)

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

func parseDeviceManagedNetworksIDImport(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/networkID\"", id)
}

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

func convertDeviceManagedNetworkConfigToSchema(input *cloudflare.Config) []interface{} {
m := map[string]interface{}{
"sha256": &input.Sha256,
"tls_sockaddr": &input.TlsSockAddr,
}
return []interface{}{m}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package provider

import (
"fmt"
"testing"

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

func TestAccCloudflareDeviceManagedNetworks(t *testing.T) {
rnd := generateRandomResourceName()
name := fmt.Sprintf("cloudflare_device_managed_networks.%s", rnd)

resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheckAccount(t)
},
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: testAccCloudflareDeviceManagedNetworks(accountID, rnd, "custom profile"),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(name, "account_id", accountID),
resource.TestCheckResourceAttr(name, "name", rnd),
resource.TestCheckResourceAttr(name, "type", "custom"),
resource.TestCheckResourceAttr(name, "entry.0.name", fmt.Sprintf("%s_entry1", rnd)),
resource.TestCheckResourceAttr(name, "entry.0.enabled", "true"),
resource.TestCheckResourceAttr(name, "entry.0.pattern.0.regex", "^4[0-9]"),
resource.TestCheckResourceAttr(name, "entry.0.pattern.0.validation", "luhn"),
),
},
},
})
}

func testAccCloudflareDeviceManagedNetworks(accountID, rnd, description string) string {
return fmt.Sprintf(`
resource "cloudflare_device_managed_networks" "%[1]s" {
account_id = "%[3]s"
name = "%[1]s"
type = "tls"
config {
tls_sockaddr = "foobar:1234"
sha256 = b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c
}
}
`, rnd, description, accountID)
}
48 changes: 48 additions & 0 deletions internal/provider/schema_cloudflare_device_managed_networks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package provider

import (
"fmt"

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

func resourceCloudflareDeviceManagedNetworksSchema() map[string]*schema.Schema {
return map[string]*schema.Schema{
"account_id": {
Description: "The account identifier to target for the resource.",
Type: schema.TypeString,
Required: true,
},
"type": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{"tls"}, false),
Description: fmt.Sprintf("The type of Device Managed Network. %s", renderAvailableDocumentationValuesStringSlice([]string{"tls"})),
},
"name": {
Type: schema.TypeString,
Required: true,
Description: "The name of the Device Managed Network. Must be unique.",
},
"config": {
jacobbednarz marked this conversation as resolved.
Show resolved Hide resolved
Type: schema.TypeList,
Required: true,
Description: "The configuration containing information for the WARP client to detect the managed network.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"tls_sockaddr": {
Type: schema.TypeString,
Required: true,
Description: "A network address of the form \"host:port\" that the WARP client will use to detect the presence of a TLS host.",
},
"sha256": {
Type: schema.TypeString,
Required: true,
Description: "The SHA-256 hash of the TLS certificate presented by the host found at tls_sockaddr. If absent, regular certificate verification (trusted roots, valid timestamp, etc) will be used to validate the certificate.",
},
},
},
},
}
}