Skip to content

Commit 4f29209

Browse files
committed
Add generation support to VPN connections
1 parent 14d41ee commit 4f29209

File tree

3 files changed

+85
-4
lines changed

3 files changed

+85
-4
lines changed

docs/resources/space_vpn_connection.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,19 @@ Provides a resource for creating a VPN connection between a network and a Heroku
1313
## Example Usage
1414

1515
```hcl-terraform
16-
// Create a new Heroku space
16+
// Create a new Heroku space (Cedar generation)
1717
resource "heroku_space" "default" {
1818
name = "test-space"
1919
organization = "my-company"
2020
region = "virginia"
21+
generation = "cedar"
2122
}
2223
2324
// Connect the Heroku space to another network with a VPN
2425
resource "heroku_space_vpn_connection" "office" {
2526
name = "office"
2627
space = heroku_space.default.id
28+
generation = "cedar" # Must match space generation
2729
public_ip = "203.0.113.1"
2830
routable_cidrs = ["192.168.1.0/24"]
2931
}
@@ -35,13 +37,15 @@ The following arguments are supported:
3537

3638
* `name` - (Required) The name of the VPN connection.
3739
* `space` - (Required) The ID of the Heroku Private Space where the VPN connection will be established.
40+
* `generation` - (Optional) The generation of the space for VPN connection. Valid values are `cedar` and `fir`. Defaults to `cedar` for backward compatibility. Note: VPN connections are not supported for `fir` generation spaces. Cannot be changed after creation.
3841
* `public_ip` - (Required) The public IP address of the VPN endpoint on the network where the VPN connection will be established.
3942
* `routable_cidrs` - (Required) A list of IPv4 CIDR blocks used by the network where the VPN connection will be established.
4043

4144
## Attributes Reference
4245

4346
The following attributes are exported:
4447

48+
* `generation` - The generation of the space for VPN connection (`cedar` or `fir`).
4549
* `space_cidr_block` - The CIDR block for the Heroku Private Space. The network where the VPN will be established should be configured to route traffic destined for this CIDR block over the VPN link.
4650
* `ike_version` - The IKE version used to setup the IPsec tunnel.
4751
* `tunnels` - Details about each VPN tunnel endpoint.

heroku/resource_heroku_space_vpn_connection.go

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,16 @@ import (
99

1010
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
1111
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
12+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
1213
heroku "github.com/heroku/heroku-go/v6"
1314
)
1415

1516
func resourceHerokuSpaceVPNConnection() *schema.Resource {
1617
return &schema.Resource{
17-
Create: resourceHerokuSpaceVPNConnectionCreate,
18-
Read: resourceHerokuSpaceVPNConnectionRead,
19-
Delete: resourceHerokuSpaceVPNConnectionDelete,
18+
Create: resourceHerokuSpaceVPNConnectionCreate,
19+
Read: resourceHerokuSpaceVPNConnectionRead,
20+
Delete: resourceHerokuSpaceVPNConnectionDelete,
21+
CustomizeDiff: resourceHerokuSpaceVPNConnectionCustomizeDiff,
2022

2123
Importer: &schema.ResourceImporter{
2224
State: schema.ImportStatePassthrough,
@@ -29,6 +31,15 @@ func resourceHerokuSpaceVPNConnection() *schema.Resource {
2931
ForceNew: true,
3032
},
3133

34+
"generation": {
35+
Type: schema.TypeString,
36+
Optional: true,
37+
Default: "cedar",
38+
ForceNew: true,
39+
ValidateFunc: validation.StringInSlice([]string{"cedar", "fir"}, false),
40+
Description: "Generation of the space for VPN connection. Defaults to cedar for backward compatibility.",
41+
},
42+
3243
"name": {
3344
Type: schema.TypeString,
3445
Required: true,
@@ -190,3 +201,20 @@ func spaceVPNConnectionStateRefreshFunc(client *heroku.Service, space, connectio
190201
return vpn, vpn.Status, nil
191202
}
192203
}
204+
205+
// resourceHerokuSpaceVPNConnectionCustomizeDiff validates generation-specific feature support during plan phase
206+
func resourceHerokuSpaceVPNConnectionCustomizeDiff(ctx context.Context, diff *schema.ResourceDiff, v interface{}) error {
207+
generation, generationExists := diff.GetOk("generation")
208+
209+
// Only validate if generation field is present
210+
if generationExists {
211+
generationStr := generation.(string)
212+
213+
// Check if VPN connections are supported for this generation
214+
if !IsFeatureSupported(generationStr, "space", "vpn_connection") {
215+
return fmt.Errorf("VPN connections are not supported for %s generation spaces", generationStr)
216+
}
217+
}
218+
219+
return nil
220+
}

heroku/resource_heroku_space_vpn_connection_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,52 @@ resource "heroku_space_vpn_connection" "foobar" {
8686
}
8787
`, spaceConfig)
8888
}
89+
90+
func TestHerokuSpaceVPNConnectionGeneration(t *testing.T) {
91+
tests := []struct {
92+
name string
93+
generation string
94+
expectError bool
95+
description string
96+
}{
97+
{
98+
name: "Cedar generation should be supported",
99+
generation: "cedar",
100+
expectError: false,
101+
description: "Cedar supports VPN connections",
102+
},
103+
{
104+
name: "Fir generation should be unsupported",
105+
generation: "fir",
106+
expectError: true,
107+
description: "Fir does not support VPN connections",
108+
},
109+
{
110+
name: "Default generation (cedar) should be supported",
111+
generation: "", // Will default to cedar
112+
expectError: false,
113+
description: "Default cedar generation supports VPN connections",
114+
},
115+
}
116+
117+
for _, tt := range tests {
118+
t.Run(tt.name, func(t *testing.T) {
119+
// Test the feature support logic
120+
generation := tt.generation
121+
if generation == "" {
122+
generation = "cedar" // Default
123+
}
124+
125+
supported := IsFeatureSupported(generation, "space", "vpn_connection")
126+
shouldError := !supported
127+
128+
if shouldError != tt.expectError {
129+
t.Errorf("Expected error: %t, but got: %t for generation %s",
130+
tt.expectError, shouldError, generation)
131+
}
132+
133+
t.Logf("✅ Generation: %s, Supported: %t, ShouldError: %t",
134+
generation, supported, shouldError)
135+
})
136+
}
137+
}

0 commit comments

Comments
 (0)