Skip to content

Commit

Permalink
Merge pull request #1672 from mapped/add-virtual-networks
Browse files Browse the repository at this point in the history
Add support for virtual networks
  • Loading branch information
jacobbednarz committed Jun 10, 2022
2 parents 9648b75 + 3c4358c commit 64d13cb
Show file tree
Hide file tree
Showing 8 changed files with 384 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .changelog/1672.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-resource
cloudflare_tunnel_virtual_network
```
50 changes: 50 additions & 0 deletions docs/resources/cloudflare_tunnel_virtual_network.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "cloudflare_tunnel_virtual_network Resource - Cloudflare"
subcategory: ""
description: |-
Provides a resource, that manages Cloudflare tunnel virtual networks for Zero Trust. Tunnel
virtual networks are used for segregation of Tunnel IP Routes via Virtualized Networks to
handle overlapping private IPs in your origins.
---

# cloudflare_tunnel_virtual_network (Resource)

Provides a resource, that manages Cloudflare tunnel virtual networks for Zero Trust. Tunnel
virtual networks are used for segregation of Tunnel IP Routes via Virtualized Networks to
handle overlapping private IPs in your origins.

## Example Usage

```terraform
resource "cloudflare_tunnel_virtual_network" "example" {
account_id = "c4a7362d577a6c3019a474fd6f485821"
name = "vnet-for-documentation"
comment = "New tunnel virtual network for documentation"
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `account_id` (String) The account identifier to target for the resource.
- `name` (String) A user-friendly name chosen when the virtual network is created.

### Optional

- `comment` (String) Description of the tunnel virtual network.
- `is_default_network` (Boolean) Whether this virtual network is the default one for the account. This means IP Routes belong to this virtual network and Teams Clients in the account route through this virtual network, unless specified otherwise for each case.

### Read-Only

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

## Import

Import is supported using the following syntax:

```shell
terraform import cloudflare_tunnel_virtual_network c4a7362d577a6c3019a474fd6f485821/3c8ff8af-b487-45bd-89e3-4c85a1532600
```
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
$ terraform import cloudflare_tunnel_virtual_network c4a7362d577a6c3019a474fd6f485821/3c8ff8af-b487-45bd-89e3-4c85a1532600
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
resource "cloudflare_tunnel_virtual_network" "example" {
account_id = "c4a7362d577a6c3019a474fd6f485821"
name = "vnet-for-documentation"
comment = "New tunnel virtual network for documentation"
}
1 change: 1 addition & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ func New(version string) func() *schema.Provider {
"cloudflare_teams_rule": resourceCloudflareTeamsRule(),
"cloudflare_teams_proxy_endpoint": resourceCloudflareTeamsProxyEndpoint(),
"cloudflare_tunnel_route": resourceCloudflareTunnelRoute(),
"cloudflare_tunnel_virtual_network": resourceCloudflareTunnelVirtualNetwork(),
"cloudflare_waf_group": resourceCloudflareWAFGroup(),
"cloudflare_waf_override": resourceCloudflareWAFOverride(),
"cloudflare_waf_package": resourceCloudflareWAFPackage(),
Expand Down
142 changes: 142 additions & 0 deletions internal/provider/resource_cloudflare_tunnel_virtual_network.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
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 resourceCloudflareTunnelVirtualNetwork() *schema.Resource {
return &schema.Resource{
Schema: resourceCloudflareTunnelVirtualNetworkSchema(),
CreateContext: resourceCloudflareTunnelVirtualNetworkCreate,
ReadContext: resourceCloudflareTunnelVirtualNetworkRead,
UpdateContext: resourceCloudflareTunnelVirtualNetworkUpdate,
DeleteContext: resourceCloudflareTunnelVirtualNetworkDelete,
Importer: &schema.ResourceImporter{
StateContext: resourceCloudflareTunnelVirtualNetworkImport,
},
Description: `
Provides a resource, that manages Cloudflare tunnel virtual networks for Zero Trust. Tunnel
virtual networks are used for segregation of Tunnel IP Routes via Virtualized Networks to
handle overlapping private IPs in your origins.`,
}
}

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

tunnelVirtualNetworks, err := client.ListTunnelVirtualNetworks(ctx, cloudflare.TunnelVirtualNetworksListParams{
AccountID: accountID,
IsDeleted: cloudflare.BoolPtr(false),
ID: d.Id(),
})

if err != nil {
return diag.FromErr(fmt.Errorf("failed to fetch Tunnel Virtual Network: %w", err))
}

if len(tunnelVirtualNetworks) < 1 {
tflog.Info(ctx, fmt.Sprintf("Tunnel Virtual Network for ID %s in account %s not found", d.Id(), accountID))
d.SetId("")
return nil
}

tunnelVirtualNetwork := tunnelVirtualNetworks[0]

d.Set("name", tunnelVirtualNetwork.Name)
d.Set("is_default_network", tunnelVirtualNetwork.IsDefaultNetwork)

if len(tunnelVirtualNetwork.Comment) > 0 {
d.Set("comment", tunnelVirtualNetwork.Comment)
}

return nil
}

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

resource := cloudflare.TunnelVirtualNetworkCreateParams{
AccountID: d.Get("account_id").(string),
Name: name,
IsDefault: d.Get("is_default_network").(bool),
}

if comment, ok := d.Get("comment").(string); ok {
resource.Comment = comment
}

newTunnelVirtualNetwork, err := client.CreateTunnelVirtualNetwork(ctx, resource)
if err != nil {
return diag.FromErr(fmt.Errorf("error creating Tunnel Virtual Network %q: %w", name, err))
}

d.SetId(newTunnelVirtualNetwork.ID)

return resourceCloudflareTunnelVirtualNetworkRead(ctx, d, meta)
}

func resourceCloudflareTunnelVirtualNetworkUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*cloudflare.API)

resource := cloudflare.TunnelVirtualNetworkUpdateParams{
AccountID: d.Get("account_id").(string),
Name: d.Get("name").(string),
IsDefaultNetwork: cloudflare.BoolPtr(d.Get("is_default_network").(bool)),
VnetID: d.Id(),
}

if comment, ok := d.Get("comment").(string); ok {
resource.Comment = comment
}

_, err := client.UpdateTunnelVirtualNetwork(ctx, resource)
if err != nil {
return diag.FromErr(fmt.Errorf("error updating Tunnel Virtual Network %q: %w", d.Id(), err))
}

return resourceCloudflareTunnelVirtualNetworkRead(ctx, d, meta)
}

func resourceCloudflareTunnelVirtualNetworkDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*cloudflare.API)

err := client.DeleteTunnelVirtualNetwork(ctx, cloudflare.TunnelVirtualNetworkDeleteParams{
AccountID: d.Get("account_id").(string),
VnetID: d.Id(),
})
if err != nil {
return diag.FromErr(fmt.Errorf("error deleting Tunnel Virtual Network %q: %w", d.Id(), err))
}

return nil
}

func resourceCloudflareTunnelVirtualNetworkImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
attributes := strings.SplitN(d.Id(), "/", 2)

if len(attributes) != 2 {
return nil, fmt.Errorf(`invalid id (%q) specified, should be in format "accountID/vnetID"`, d.Id())
}

accountID, vnetID := attributes[0], attributes[1]

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

err := resourceCloudflareTunnelVirtualNetworkRead(ctx, d, meta)
if err != nil {
return nil, errors.New("failed to read Tunnel Virtual Network state")
}

return []*schema.ResourceData{d}, nil
}
151 changes: 151 additions & 0 deletions internal/provider/resource_cloudflare_tunnel_virtual_network_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package provider

import (
"context"
"errors"
"fmt"
"log"
"os"
"testing"

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

func init() {
resource.AddTestSweepers("cloudflare_tunnel_virtual_network", &resource.Sweeper{
Name: "cloudflare_tunnel_virtual_network",
F: testSweepCloudflareTunnelVirtualNetwork,
})
}

func testSweepCloudflareTunnelVirtualNetwork(r string) error {
ctx := context.Background()
client, clientErr := sharedClient()
if clientErr != nil {
tflog.Error(ctx, fmt.Sprintf("Failed to create Cloudflare client: %s", clientErr))
}

accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID")
if accountID == "" {
return errors.New("CLOUDFLARE_ACCOUNT_ID must be set")
}

tunnelVirtualNetworks, err := client.ListTunnelVirtualNetworks(context.Background(), cloudflare.TunnelVirtualNetworksListParams{AccountID: accountID})
if err != nil {
tflog.Error(ctx, fmt.Sprintf("Failed to fetch Cloudflare Tunnel Virtual Networks: %s", err))
}

if len(tunnelVirtualNetworks) == 0 {
log.Print("[DEBUG] No Cloudflare Tunnel Virtual Networks to sweep")
return nil
}

for _, vnet := range tunnelVirtualNetworks {
tflog.Info(ctx, fmt.Sprintf("Deleting Cloudflare Tunnel Virtual Network %s", vnet.ID))
//nolint:errcheck
client.DeleteTunnelVirtualNetwork(context.Background(), cloudflare.TunnelVirtualNetworkDeleteParams{AccountID: accountID, VnetID: vnet.ID})
}

return nil
}

func TestAccCloudflareTunnelVirtualNetwork_Exists(t *testing.T) {
rnd := generateRandomResourceName()
name := fmt.Sprintf("cloudflare_tunnel_virtual_network.%s", rnd)
accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID")

var TunnelVirtualNetwork cloudflare.TunnelVirtualNetwork

resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
testAccPreCheckAccount(t)
},
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: testAccCloudflareTunnelVirtualNetworkSimple(rnd, rnd, accountID, rnd, false),
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudflareTunnelVirtualNetworkExists(name, &TunnelVirtualNetwork),
resource.TestCheckResourceAttr(name, "account_id", accountID),
resource.TestCheckResourceAttr(name, "name", rnd),
resource.TestCheckResourceAttr(name, "comment", rnd),
resource.TestCheckResourceAttr(name, "is_default_network", "false"),
),
},
},
})
}

func testAccCheckCloudflareTunnelVirtualNetworkExists(name string, virtualNetwork *cloudflare.TunnelVirtualNetwork) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("Not found: %s", name)
}

if rs.Primary.ID == "" {
return errors.New("No Tunnel Virtual Network is set")
}

client := testAccProvider.Meta().(*cloudflare.API)
foundTunnelVirtualNetworks, err := client.ListTunnelVirtualNetworks(context.Background(), cloudflare.TunnelVirtualNetworksListParams{
AccountID: rs.Primary.Attributes["account_id"],
IsDeleted: cloudflare.BoolPtr(false),
ID: rs.Primary.ID,
})

if err != nil {
return err
}

*virtualNetwork = foundTunnelVirtualNetworks[0]

return nil
}
}

func TestAccCloudflareTunnelVirtualNetwork_UpdateComment(t *testing.T) {
rnd := generateRandomResourceName()
name := fmt.Sprintf("cloudflare_tunnel_virtual_network.%s", rnd)
accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID")

var TunnelVirtualNetwork cloudflare.TunnelVirtualNetwork

resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
testAccPreCheckAccount(t)
},
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: testAccCloudflareTunnelVirtualNetworkSimple(rnd, rnd, accountID, rnd, false),
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudflareTunnelVirtualNetworkExists(name, &TunnelVirtualNetwork),
resource.TestCheckResourceAttr(name, "comment", rnd),
),
},
{
Config: testAccCloudflareTunnelVirtualNetworkSimple(rnd, rnd+"-updated", accountID, rnd, false),
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudflareTunnelVirtualNetworkExists(name, &TunnelVirtualNetwork),
resource.TestCheckResourceAttr(name, "comment", rnd+"-updated"),
),
},
},
})
}

func testAccCloudflareTunnelVirtualNetworkSimple(ID, comment, accountID, name string, isDefault bool) string {
return fmt.Sprintf(`
resource "cloudflare_tunnel_virtual_network" "%[1]s" {
account_id = "%[3]s"
name = "%[4]s"
comment = "%[2]s"
is_default_network = "%[5]t"
}`, ID, comment, accountID, name, isDefault)
}

0 comments on commit 64d13cb

Please sign in to comment.