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 vnet param to tunnel routes #1668

Merged
merged 11 commits into from
Jun 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
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/1668.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/cloudflare_tunnel_route: Add `virtual_network_id` attribute
```
49 changes: 31 additions & 18 deletions docs/resources/cloudflare_tunnel_route.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
---
layout: "cloudflare"
page_title: "Cloudflare: cloudflare_tunnel_route"
description: Provides a resource which manages Cloudflare Tunnel Routes for Zero Trust
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "cloudflare_tunnel_route Resource - Cloudflare"
subcategory: ""
description: |-
Provides a resource, that manages Cloudflare tunnel routes for Zero Trust. Tunnel routes are used to direct IP traffic through Cloudflare Tunnels.
---

# cloudflare_tunnel_route
# cloudflare_tunnel_route (Resource)

Provides a resource, that manages Cloudflare tunnel routes for Zero Trust. Tunnel
routes are used to direct IP traffic through Cloudflare Tunnels.
Provides a resource, that manages Cloudflare tunnel routes for Zero Trust. Tunnel routes are used to direct IP traffic through Cloudflare Tunnels.

## Example Usage

```hcl
```terraform
# Tunnel route
resource "cloudflare_tunnel_route" "example" {
account_id = "c4a7362d577a6c3019a474fd6f485821"
tunnel_id = "f70ff985-a4ef-4643-bbbc-4a0ed4fc8415"
network = "192.0.2.24/32"
comment = "New tunnel route for documentation"
virtual_network_id = "bdc39a3c-3104-4c23-8ac0-9f455dda691a"
}
```

```hcl
# Tunnel with tunnel route
resource "cloudflare_argo_tunnel" "tunnel" {
account_id = "c4a7362d577a6c3019a474fd6f485821"
name = "my_tunnel"
Expand All @@ -32,22 +34,33 @@ resource "cloudflare_tunnel_route" "example" {
tunnel_id = cloudflare_argo_tunnel.tunnel.id
network = "192.0.2.24/32"
comment = "New tunnel route for documentation"
virtual_network_id = "bdc39a3c-3104-4c23-8ac0-9f455dda691a"
}
```

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

### Required

- `account_id` (String) The account identifier to target for the resource.
- `network` (String) The IPv4 or IPv6 network that should use this tunnel route, in CIDR notation.
- `tunnel_id` (String) The ID of the tunnel that will service the tunnel route.

The following arguments are supported:
### Optional

- `account_id` - (Required) The ID of the account where the tunnel route is being created.
- `tunnel_id` - (Required) The ID of the tunnel that will service the tunnel route.
- `network` - (Required) The IPv4 or IPv6 network that should use this tunnel route, in CIDR notation.
- `comment` - (Optional) Description of the tunnel route.
- `comment` (String) Description of the tunnel route.
- `virtual_network_id` (String) The ID of the virtual network for which this route is being added; uses the default virtual network of the account if none is provided.

### Read-Only

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

## Import

An existing tunnel route can be imported using the account ID and network CIDR.
Import is supported using the following syntax:

```
$ terraform import cloudflare_tunnel_route c4a7362d577a6c3019a474fd6f485821/192.0.2.24/32
```shell
# Use account ID, network CIDR and virtual network ID.
$ terraform import cloudflare_tunnel_route c4a7362d577a6c3019a474fd6f485821/192.0.2.24/32/bdc39a3c-3104-4c23-8ac0-9f455dda691a
```
2 changes: 2 additions & 0 deletions examples/resources/cloudflare_tunnel_route/import.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Use account ID, network CIDR and virtual network ID.
$ terraform import cloudflare_tunnel_route c4a7362d577a6c3019a474fd6f485821/192.0.2.24/32/bdc39a3c-3104-4c23-8ac0-9f455dda691a
23 changes: 23 additions & 0 deletions examples/resources/cloudflare_tunnel_route/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Tunnel route
resource "cloudflare_tunnel_route" "example" {
account_id = "c4a7362d577a6c3019a474fd6f485821"
tunnel_id = "f70ff985-a4ef-4643-bbbc-4a0ed4fc8415"
network = "192.0.2.24/32"
comment = "New tunnel route for documentation"
virtual_network_id = "bdc39a3c-3104-4c23-8ac0-9f455dda691a"
}

# Tunnel with tunnel route
resource "cloudflare_argo_tunnel" "tunnel" {
account_id = "c4a7362d577a6c3019a474fd6f485821"
name = "my_tunnel"
secret = "AQIDBAUGBwgBAgMEBQYHCAECAwQFBgcIAQIDBAUGBwg="
}

resource "cloudflare_tunnel_route" "example" {
account_id = "c4a7362d577a6c3019a474fd6f485821"
tunnel_id = cloudflare_argo_tunnel.tunnel.id
network = "192.0.2.24/32"
comment = "New tunnel route for documentation"
virtual_network_id = "bdc39a3c-3104-4c23-8ac0-9f455dda691a"
}
76 changes: 52 additions & 24 deletions internal/provider/resource_cloudflare_tunnel_route.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,25 @@ func resourceCloudflareTunnelRoute() *schema.Resource {
Importer: &schema.ResourceImporter{
StateContext: resourceCloudflareTunnelRouteImport,
},
Description: "Provides a resource, that manages Cloudflare tunnel routes for Zero Trust. Tunnel routes are used to direct IP traffic through Cloudflare Tunnels.",
}
}

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

resource := cloudflare.TunnelRoutesListParams{
AccountID: accountID,
IsDeleted: cloudflare.BoolPtr(false),
NetworkSubset: network,
NetworkSuperset: network,
VirtualNetworkID: virtualNetworkID,
}

tunnelRoutes, err := client.ListTunnelRoutes(ctx, cloudflare.TunnelRoutesListParams{
AccountID: accountID,
IsDeleted: cloudflare.BoolPtr(false),
NetworkSubset: network,
NetworkSuperset: network,
})
tunnelRoutes, err := client.ListTunnelRoutes(ctx, resource)

if err != nil {
return diag.FromErr(fmt.Errorf("failed to fetch Tunnel Route: %w", err))
Expand All @@ -55,16 +60,25 @@ func resourceCloudflareTunnelRouteRead(ctx context.Context, d *schema.ResourceDa
d.Set("comment", tunnelRoute.Comment)
}

// Virtual network id is optional. API always returns it. Do not set it unless it was specified explicitly.
// Othewise if route was created by old provider it will trigger redundant state changes.
// Old provider did not support virtual network ids at all.
if virtualNetworkID != "" {
d.Set("virtual_network_id", tunnelRoute.VirtualNetworkID)
}

return nil
}

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

resource := cloudflare.TunnelRoutesCreateParams{
AccountID: d.Get("account_id").(string),
TunnelID: d.Get("tunnel_id").(string),
Network: d.Get("network").(string),
AccountID: d.Get("account_id").(string),
TunnelID: d.Get("tunnel_id").(string),
Network: d.Get("network").(string),
VirtualNetworkID: virtualNetworkID,
}

if comment, ok := d.Get("comment").(string); ok {
Expand All @@ -76,7 +90,12 @@ func resourceCloudflareTunnelRouteCreate(ctx context.Context, d *schema.Resource
return diag.FromErr(fmt.Errorf("error creating Tunnel Route for Network %q: %w", d.Get("network").(string), err))
}

d.SetId(newTunnelRoute.Network)
if virtualNetworkID != "" {
// It's possible to create several routes with the same network but different virtual network ids.
d.SetId(stringChecksum(fmt.Sprintf("%s/%s", newTunnelRoute.Network, virtualNetworkID)))
} else {
d.SetId(newTunnelRoute.Network)
}

return resourceCloudflareTunnelRouteRead(ctx, d, meta)
}
Expand All @@ -85,10 +104,11 @@ func resourceCloudflareTunnelRouteUpdate(ctx context.Context, d *schema.Resource
client := meta.(*cloudflare.API)

resource := cloudflare.TunnelRoutesUpdateParams{
AccountID: d.Get("account_id").(string),
TunnelID: d.Get("tunnel_id").(string),
Network: d.Get("network").(string),
Comment: "",
AccountID: d.Get("account_id").(string),
TunnelID: d.Get("tunnel_id").(string),
Network: d.Get("network").(string),
Comment: "",
VirtualNetworkID: d.Get("virtual_network_id").(string),
}

if comment, ok := d.Get("comment").(string); ok {
Expand All @@ -105,28 +125,36 @@ func resourceCloudflareTunnelRouteUpdate(ctx context.Context, d *schema.Resource

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

resource := cloudflare.TunnelRoutesDeleteParams{
AccountID: d.Get("account_id").(string),
Network: network,
VirtualNetworkID: d.Get("virtual_network_id").(string),
}

err := client.DeleteTunnelRoute(ctx, cloudflare.TunnelRoutesDeleteParams{
AccountID: d.Get("account_id").(string),
Network: d.Get("network").(string),
})
err := client.DeleteTunnelRoute(ctx, resource)
if err != nil {
return diag.FromErr(fmt.Errorf("error deleting Tunnel Route for Network %q: %w", d.Get("network").(string), err))
return diag.FromErr(fmt.Errorf("error deleting Tunnel Route for Network %q: %w", network, err))
}

return nil
}

func resourceCloudflareTunnelRouteImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems unnecessarily complex; let's just force it to include the virtual_network_id for explicitness.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a cloudflared user I know that vnet is totally optional param there.
Making it required during import will create additional headache for developers that are always working inside of the "default" vnet since it's not easy at all to get GUID of the default vnet (you cannot do it through UI. Only by using API or cloudflared).

But anyway for me it does not make any difference. So let's make it required.

attributes := strings.SplitN(d.Id(), "/", 2)
attributes := strings.SplitN(d.Id(), "/", 4)

if len(attributes) != 2 {
return nil, fmt.Errorf(`invalid id (%q) specified, should be in format "accountID/network"`, d.Id())
// network is a CIDR that always contains slash inside. For example "192.168.0.0/26"
if len(attributes) != 4 {
return nil, fmt.Errorf(`invalid id (%q) specified, should be in format "accountID/network/virtual_network_id"`, d.Id())
}

accountID, network := attributes[0], attributes[1]
accountID, network := attributes[0], fmt.Sprintf("%s/%s", attributes[1], attributes[2])

// It's possible to create several routes with the same network but different virtual network ids.
d.SetId(stringChecksum(fmt.Sprintf("%s/%s", network, attributes[4])))
d.Set("virtual_network_id", accountID)

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func testSweepCloudflareTunnelRoute(r string) error {
for _, tunnel := range tunnelRoutes {
tflog.Info(ctx, fmt.Sprintf("Deleting Cloudflare Tunnel Route network: %s", tunnel.Network))
//nolint:errcheck
client.DeleteTunnelRoute(context.Background(), cloudflare.TunnelRoutesDeleteParams{AccountID: accountID, Network: tunnel.Network})
client.DeleteTunnelRoute(context.Background(), cloudflare.TunnelRoutesDeleteParams{AccountID: accountID, Network: tunnel.Network, VirtualNetworkID: tunnel.TunnelID})
}

return nil
Expand Down
23 changes: 16 additions & 7 deletions internal/provider/schema_cloudflare_tunnel_route.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,28 @@ func resourceCloudflareTunnelRouteSchema() map[string]*schema.Schema {
Description: "The account identifier to target for the resource.",
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"tunnel_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "The ID of the tunnel that will service the tunnel route.",
Type: schema.TypeString,
Required: true,
},
"network": {
Type: schema.TypeString,
Required: true,
Description: "The IPv4 or IPv6 network that should use this tunnel route, in CIDR notation.",
Type: schema.TypeString,
Required: true,
},
"comment": {
Type: schema.TypeString,
Optional: true,
Description: "Description of the tunnel route.",
Type: schema.TypeString,
Optional: true,
},
"virtual_network_id": {
Description: "The ID of the virtual network for which this route is being added; uses the default virtual network of the account if none is provided.",
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
}
}
53 changes: 0 additions & 53 deletions templates/resources/cloudflare_tunnel_route.md

This file was deleted.