Skip to content

Commit 3ebe780

Browse files
committed
feat(terraform): Set default vsys for template and template stacks on create
1 parent 2a5f22c commit 3ebe780

14 files changed

Lines changed: 400 additions & 111 deletions

File tree

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package provider
2+
3+
import (
4+
"context"
5+
6+
"github.com/PaloAltoNetworks/pango/panorama/template"
7+
"github.com/hashicorp/terraform-plugin-framework/resource"
8+
"github.com/hashicorp/terraform-plugin-log/tflog"
9+
)
10+
11+
// TemplateCustom stores state shared between PreCreate and PostCreate hooks.
12+
type TemplateCustom struct {
13+
savedDefaultVsys *string
14+
}
15+
16+
func NewTemplateCustom(data *ProviderData) (*TemplateCustom, error) {
17+
return &TemplateCustom{}, nil
18+
}
19+
20+
// PreCreate saves and strips default_vsys from the SDK object before Create,
21+
// because PAN-OS cannot set this field during initial creation.
22+
func (o *TemplateResource) PreCreate(
23+
ctx context.Context,
24+
req resource.CreateRequest,
25+
resp *resource.CreateResponse,
26+
state *TemplateResourceModel,
27+
location template.Location,
28+
obj *template.Entry,
29+
ev *EncryptedValuesManager,
30+
) {
31+
o.custom.savedDefaultVsys = obj.DefaultVsys
32+
obj.DefaultVsys = nil
33+
}
34+
35+
// PostCreate creates the vsys referenced by default_vsys inside the template,
36+
// then sets default_vsys via an Update call. The vsys is added directly to the
37+
// SDK entry's Config struct so the Update's edit action includes it.
38+
func (o *TemplateResource) PostCreate(
39+
ctx context.Context,
40+
req resource.CreateRequest,
41+
resp *resource.CreateResponse,
42+
state *TemplateResourceModel,
43+
location template.Location,
44+
obj *template.Entry,
45+
ev *EncryptedValuesManager,
46+
) {
47+
if o.custom.savedDefaultVsys == nil {
48+
return
49+
}
50+
51+
defaultVsys := *o.custom.savedDefaultVsys
52+
templateName := state.Name.ValueString()
53+
54+
tflog.Info(ctx, "performing post-create update to set default_vsys", map[string]any{
55+
"resource_name": "panos_template",
56+
"name": templateName,
57+
"default_vsys": defaultVsys,
58+
})
59+
60+
// Populate the Config struct with the vsys entry so the SDK's edit
61+
// action includes it. PAN-OS requires the vsys to exist before it
62+
// accepts it as a valid default_vsys reference.
63+
obj.Config = &template.Config{
64+
Devices: []template.ConfigDevices{
65+
{
66+
Name: "localhost.localdomain",
67+
Vsys: []template.ConfigDevicesVsys{
68+
{Name: defaultVsys},
69+
},
70+
},
71+
},
72+
}
73+
obj.DefaultVsys = o.custom.savedDefaultVsys
74+
75+
components, err := state.resourceXpathParentComponents()
76+
if err != nil {
77+
resp.Diagnostics.AddError("Error creating resource xpath for post-create update", err.Error())
78+
return
79+
}
80+
81+
updated, err := o.manager.Update(ctx, location, components, obj, "")
82+
if err != nil {
83+
resp.Diagnostics.AddError(
84+
"Error setting default_vsys after create",
85+
"Template created successfully but setting default_vsys failed. "+
86+
"Run terraform apply again to retry. Error: "+err.Error(),
87+
)
88+
return
89+
}
90+
91+
resp.Diagnostics.Append(state.CopyFromPango(ctx, o.client, nil, updated, ev)...)
92+
}

assets/terraform/test/resource_panorama_template_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,59 @@ func TestAccPanosTemplate_RequiredInputs(t *testing.T) {
4040
})
4141
}
4242

43+
// TestAccPanosTemplate_DefaultVsys verifies that default_vsys can be set
44+
// during the initial create step. The CRUD hooks strip default_vsys from the
45+
// create call and set it via a post-create update.
46+
func TestAccPanosTemplate_DefaultVsys(t *testing.T) {
47+
t.Parallel()
48+
49+
nameSuffix := acctest.RandStringFromCharSet(6, acctest.CharSetAlphaNum)
50+
prefix := fmt.Sprintf("test-acc-%s", nameSuffix)
51+
52+
location := config.ObjectVariable(map[string]config.Variable{
53+
"panorama": config.ObjectVariable(map[string]config.Variable{}),
54+
})
55+
56+
configVars := map[string]config.Variable{
57+
"prefix": config.StringVariable(prefix),
58+
"location": location,
59+
}
60+
61+
resource.Test(t, resource.TestCase{
62+
PreCheck: func() { testAccPreCheck(t) },
63+
ProtoV6ProviderFactories: testAccProviders,
64+
Steps: []resource.TestStep{
65+
{
66+
Config: template_DefaultVsys_Tmpl,
67+
ConfigVariables: configVars,
68+
ConfigStateChecks: []statecheck.StateCheck{
69+
statecheck.ExpectKnownValue(
70+
"panos_template.test",
71+
tfjsonpath.New("name"),
72+
knownvalue.StringExact(fmt.Sprintf("%s-template", prefix)),
73+
),
74+
statecheck.ExpectKnownValue(
75+
"panos_template.test",
76+
tfjsonpath.New("default_vsys"),
77+
knownvalue.StringExact("vsys1"),
78+
),
79+
},
80+
},
81+
},
82+
})
83+
}
84+
85+
const template_DefaultVsys_Tmpl = `
86+
variable "prefix" { type = string }
87+
variable "location" { type = any }
88+
89+
resource "panos_template" "test" {
90+
location = var.location
91+
name = "${var.prefix}-template"
92+
default_vsys = "vsys1"
93+
}
94+
`
95+
4396
func makePanosTemplateConfig(label string) string {
4497
configTpl := `
4598
variable "template_name" { type = string }

0 commit comments

Comments
 (0)