diff --git a/.gitignore b/.gitignore index fad74c21..7d764a69 100644 --- a/.gitignore +++ b/.gitignore @@ -29,7 +29,8 @@ website/node_modules .vscode/settings.json website/vendor - +vendor +running.sh # Test exclusions !command/test-fixtures/**/*.tfstate !command/test-fixtures/**/.terraform/ diff --git a/docs/data-sources/device_ha_pair.md b/docs/data-sources/device_ha_pair.md new file mode 100644 index 00000000..69df232b --- /dev/null +++ b/docs/data-sources/device_ha_pair.md @@ -0,0 +1,54 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "fmc_device_ha_pair Data Source - terraform-provider-fmc" +subcategory: "Device" +description: |- + This data source can read the Device HA Pair. +--- + +# fmc_device_ha_pair (Data Source) + +This data source can read the Device HA Pair. + +## Example Usage + +```terraform +data "fmc_device_ha_pair" "example" { + id = "76d24097-41c4-4558-a4d0-a8c07ac08470" +} +``` + + +## Schema + +### Optional + +- `domain` (String) The name of the FMC domain +- `id` (String) The id of the object +- `lan_failover_interface_name` (String) Name of physical interface +- `name` (String) The name of the High Availability Pair. +- `stateful_failover_interface_name` (String) Name of physical interface + +### Read-Only + +- `action` (String) FTD HA PUT operation action. Specifically used for manual switch. HA Break will be triggered when you run terraform destroy +- `enc_key_generation_scheme` (String) Select the encyption key generation scheme. +- `is_encryption_enabled` (Boolean) Boolean field to enable encryption +- `lan_failover_active_ip` (String) +- `lan_failover_interface_id` (String) ID of physical interface. +- `lan_failover_interface_type` (String) Type of physical interface. +- `lan_failover_ipv6_addr` (Boolean) +- `lan_failover_name` (String) +- `lan_failover_netmask` (String) +- `lan_failover_standby_ip` (String) +- `primary_device_id` (String) ID of primary FTD in the HA Pair. +- `secondary_device_id` (String) ID of secondary FTD in the HA Pair. +- `shared_key` (String) Pass the unique shared key if needed. +- `stateful_failover_active_ip` (String) +- `stateful_failover_interface_id` (String) ID of physical interface. +- `stateful_failover_interface_type` (String) Type of physical interface. +- `stateful_failover_ipv6_addr` (Boolean) +- `stateful_failover_name` (String) +- `stateful_failover_standby_ip` (String) +- `stateful_failover_subnet_mask` (String) +- `use_same_link_for_failovers` (Boolean) Boolean field to enable same link for failovers diff --git a/docs/resources/device_ha_pair.md b/docs/resources/device_ha_pair.md new file mode 100644 index 00000000..abb7dfbb --- /dev/null +++ b/docs/resources/device_ha_pair.md @@ -0,0 +1,90 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "fmc_device_ha_pair Resource - terraform-provider-fmc" +subcategory: "Device" +description: |- + This resource can manage a Device HA Pair. +--- + +# fmc_device_ha_pair (Resource) + +This resource can manage a Device HA Pair. + +## Example Usage + +```terraform +resource "fmc_device_ha_pair" "example" { + name = "FTD_HA" + primary_device_id = "76d24097-41c4-4558-a4d0-a8c07ac08470" + secondary_device_id = "96d24097-41c4-4332-a4d0-a8c07ac08482" + is_encryption_enabled = false + use_same_link_for_failovers = false + shared_key = "cisco123" + enc_key_generation_scheme = "CUSTOM" + lan_failover_standby_ip = "1.1.1.2" + lan_failover_active_ip = "1.1.1.1" + lan_failover_name = "LAN-INTERFACE" + lan_failover_netmask = "255.255.255.0" + lan_failover_ipv6_addr = false + lan_failover_interface_name = "GigabitEthernet0/0" + lan_failover_interface_id = "757kdgh5-41c4-4558-a4d0-a8c07ac08470" + lan_failover_interface_type = "PhysicalInterface" + stateful_failover_standby_ip = "10.10.10.2" + stateful_failover_active_ip = "10.10.10.1" + stateful_failover_name = "Stateful-INTERFACE" + stateful_failover_subnet_mask = "255.255.255.0" + stateful_failover_ipv6_addr = false + stateful_failover_interface_name = "GigabitEthernet0/0" + stateful_failover_interface_id = "76d24097-hj7r-7786-a4d0-a8c07ac08470" + stateful_failover_interface_type = "PhysicalInterface" + action = "SWITCH" +} +``` + + +## Schema + +### Required + +- `lan_failover_active_ip` (String) +- `lan_failover_interface_id` (String) ID of physical interface. +- `lan_failover_interface_type` (String) Type of physical interface. +- `lan_failover_name` (String) +- `lan_failover_standby_ip` (String) +- `name` (String) The name of the High Availability Pair. +- `primary_device_id` (String) ID of primary FTD in the HA Pair. +- `secondary_device_id` (String) ID of secondary FTD in the HA Pair. +- `use_same_link_for_failovers` (Boolean) Boolean field to enable same link for failovers + +### Optional + +- `action` (String) FTD HA PUT operation action. Specifically used for manual switch. HA Break will be triggered when you run terraform destroy + - Choices: `SWITCH` +- `domain` (String) The name of the FMC domain +- `enc_key_generation_scheme` (String) Select the encyption key generation scheme. + - Choices: `AUTO`, `CUSTOM` +- `is_encryption_enabled` (Boolean) Boolean field to enable encryption +- `lan_failover_interface_name` (String) Name of physical interface +- `lan_failover_ipv6_addr` (Boolean) - Default value: `false` +- `lan_failover_netmask` (String) +- `shared_key` (String) Pass the unique shared key if needed. +- `stateful_failover_active_ip` (String) +- `stateful_failover_interface_id` (String) ID of physical interface. +- `stateful_failover_interface_name` (String) Name of physical interface +- `stateful_failover_interface_type` (String) Type of physical interface. +- `stateful_failover_ipv6_addr` (Boolean) +- `stateful_failover_name` (String) +- `stateful_failover_standby_ip` (String) +- `stateful_failover_subnet_mask` (String) + +### Read-Only + +- `id` (String) The id of the object + +## Import + +Import is supported using the following syntax: + +```shell +terraform import fmc_device_ha_pair.example "" +``` diff --git a/examples/data-sources/fmc_device_ha_pair/data-source.tf b/examples/data-sources/fmc_device_ha_pair/data-source.tf new file mode 100644 index 00000000..aac62b48 --- /dev/null +++ b/examples/data-sources/fmc_device_ha_pair/data-source.tf @@ -0,0 +1,3 @@ +data "fmc_device_ha_pair" "example" { + id = "76d24097-41c4-4558-a4d0-a8c07ac08470" +} diff --git a/examples/data-sources/fmc_device_ha_pairs/data-source.tf b/examples/data-sources/fmc_device_ha_pairs/data-source.tf new file mode 100644 index 00000000..50f3b111 --- /dev/null +++ b/examples/data-sources/fmc_device_ha_pairs/data-source.tf @@ -0,0 +1,3 @@ +data "fmc_device_ha_pairs" "example" { + id = "76d24097-41c4-4558-a4d0-a8c07ac08470" +} diff --git a/examples/resources/fmc_device_ha_pair/import.sh b/examples/resources/fmc_device_ha_pair/import.sh new file mode 100644 index 00000000..224ab835 --- /dev/null +++ b/examples/resources/fmc_device_ha_pair/import.sh @@ -0,0 +1 @@ +terraform import fmc_device_ha_pair.example "" diff --git a/examples/resources/fmc_device_ha_pair/resource.tf b/examples/resources/fmc_device_ha_pair/resource.tf new file mode 100644 index 00000000..372ff604 --- /dev/null +++ b/examples/resources/fmc_device_ha_pair/resource.tf @@ -0,0 +1,26 @@ +resource "fmc_device_ha_pair" "example" { + name = "FTD_HA" + primary_device_id = "76d24097-41c4-4558-a4d0-a8c07ac08470" + secondary_device_id = "96d24097-41c4-4332-a4d0-a8c07ac08482" + is_encryption_enabled = false + use_same_link_for_failovers = false + shared_key = "cisco123" + enc_key_generation_scheme = "CUSTOM" + lan_failover_standby_ip = "1.1.1.2" + lan_failover_active_ip = "1.1.1.1" + lan_failover_name = "LAN-INTERFACE" + lan_failover_netmask = "255.255.255.0" + lan_failover_ipv6_addr = false + lan_failover_interface_name = "GigabitEthernet0/0" + lan_failover_interface_id = "757kdgh5-41c4-4558-a4d0-a8c07ac08470" + lan_failover_interface_type = "PhysicalInterface" + stateful_failover_standby_ip = "10.10.10.2" + stateful_failover_active_ip = "10.10.10.1" + stateful_failover_name = "Stateful-INTERFACE" + stateful_failover_subnet_mask = "255.255.255.0" + stateful_failover_ipv6_addr = false + stateful_failover_interface_name = "GigabitEthernet0/0" + stateful_failover_interface_id = "76d24097-hj7r-7786-a4d0-a8c07ac08470" + stateful_failover_interface_type = "PhysicalInterface" + action = "SWITCH" +} diff --git a/examples/resources/fmc_device_ha_pairs/import.sh b/examples/resources/fmc_device_ha_pairs/import.sh new file mode 100644 index 00000000..ea9a2e67 --- /dev/null +++ b/examples/resources/fmc_device_ha_pairs/import.sh @@ -0,0 +1 @@ +terraform import fmc_device_ha_pairs.example "" diff --git a/examples/resources/fmc_device_ha_pairs/resource.tf b/examples/resources/fmc_device_ha_pairs/resource.tf new file mode 100644 index 00000000..fc4def7f --- /dev/null +++ b/examples/resources/fmc_device_ha_pairs/resource.tf @@ -0,0 +1,27 @@ +resource "fmc_device_ha_pairs" "example" { + name = "FTD_HA" + primary_device_id = "" + secondary_device_id = "" + is_encryption_enabled = false + use_same_link_for_failovers = false + shared_key = "cisco123" + enc_key_generation_scheme = "CUSTOM" + lan_failover_standby_ip = "1.1.1.2" + lan_failover_active_ip = "1.1.1.1" + lan_failover_name = "LAN-INTERFACE" + lan_failover_subnet_mask = "255.255.255.0" + lan_failover_ipv6_addr = false + lan_failover_interface_name = "GigabitEthernet0/0" + lan_failover_interface_id = "" + lan_failover_interface_type = "PhysicalInterface" + stateful_failover_standby_ip = "1.1.1.2" + stateful_failover_active_ip = "1.1.1.1" + stateful_failover_name = "Stateful-INTERFACE" + stateful_failover_subnet_mask = "255.255.255.0" + stateful_failover_ipv6_addr = false + stateful_failover_interface_name = "GigabitEthernet0/0" + stateful_failover_interface_id = "" + stateful_failover_interface_type = "PhysicalInterface" + action = "SWITCH" + force_break = false +} diff --git a/gen/definitions/device_ha_pair.yaml b/gen/definitions/device_ha_pair.yaml new file mode 100644 index 00000000..56f441a3 --- /dev/null +++ b/gen/definitions/device_ha_pair.yaml @@ -0,0 +1,158 @@ +--- +name: Device HA Pair +rest_endpoint: /api/fmc_config/v1/domain/{DOMAIN_UUID}/devicehapairs/ftddevicehapairs +data_source_name_query: true +doc_category: Device +test_tags: [TF_VAR_device_id, TF_VAR_device_2_id] +attributes: + - model_name: name + type: String + mandatory: true + description: The name of the High Availability Pair. + example: FTD_HA + - model_name: type + type: String + description: Type of the resource (DeviceHAPair). + value: DeviceHAPair + - model_name: id + type: String + mandatory: true + tf_name: primary_device_id + data_path: [primary] + description: ID of primary FTD in the HA Pair. + example: 76d24097-41c4-4558-a4d0-a8c07ac08470 + test_value: var.device_id + - model_name: id + type: String + mandatory: true + tf_name: secondary_device_id + data_path: [secondary] + description: ID of secondary FTD in the HA Pair. + example: 96d24097-41c4-4332-a4d0-a8c07ac08482 + test_value: var.device_2_id + - model_name: isEncryptionEnabled + type: Bool + data_path: [ftdHABootstrap] + description: Boolean field to enable encryption + example: false + - model_name: useSameLinkForFailovers + type: Bool + mandatory: true + write_only: true + data_path: [ftdHABootstrap] + example: false + description: Boolean field to enable same link for failovers + - model_name: sharedKey + type: String + data_path: [ftdHABootstrap] + description: Pass the unique shared key if needed. + example: cisco123 + - model_name: encKeyGenerationScheme + type: String + data_path: [ftdHABootstrap] + write_only: true + description: Select the encyption key generation scheme. + example: "CUSTOM" + enum_values: [ AUTO, CUSTOM ] + - model_name: standbyIP + type: String + mandatory: true + tf_name: lan_failover_standby_ip + data_path: [ftdHABootstrap,lanFailover] + example: "1.1.1.2" + - model_name: activeIP + tf_name: lan_failover_active_ip + mandatory: true + type: String + data_path: [ftdHABootstrap,lanFailover] + example: "1.1.1.1" + - model_name: logicalName + type: String + mandatory: true + tf_name: lan_failover_name + data_path: [ftdHABootstrap,lanFailover] + example: LAN-INTERFACE + - model_name: subnetMask + type: String + tf_name: lan_failover_netmask + data_path: [ftdHABootstrap,lanFailover] + example: "255.255.255.0" + - model_name: useIPv6Address + type: Bool + tf_name: lan_failover_ipv6_addr + default_value: false + data_path: [ftdHABootstrap,lanFailover] + example: false + - model_name: name + type: String + tf_name: lan_failover_interface_name + data_path: [ftdHABootstrap,lanFailover,interfaceObject] + description: Name of physical interface + example: GigabitEthernet0/0 + - model_name: id + type: String + mandatory: true + tf_name: lan_failover_interface_id + data_path: [ftdHABootstrap,lanFailover,interfaceObject] + write_only: true + description: ID of physical interface. + example: 757kdgh5-41c4-4558-a4d0-a8c07ac08470 + - model_name: type + type: String + mandatory: true + tf_name: lan_failover_interface_type + data_path: [ftdHABootstrap,lanFailover,interfaceObject] + write_only: true + description: Type of physical interface. + example: PhysicalInterface + - model_name: standbyIP + type: String + tf_name: stateful_failover_standby_ip + data_path: [ftdHABootstrap,statefulFailover] + example: "10.10.10.2" + - model_name: activeIP + tf_name: stateful_failover_active_ip + type: String + data_path: [ftdHABootstrap,statefulFailover] + example: "10.10.10.1" + - model_name: logicalName + type: String + tf_name: stateful_failover_name + data_path: [ftdHABootstrap,statefulFailover] + example: Stateful-INTERFACE + - model_name: subnetMask + type: String + tf_name: stateful_failover_subnet_mask + data_path: [ftdHABootstrap,statefulFailover] + example: "255.255.255.0" + - model_name: useIPv6Address + type: Bool + tf_name: stateful_failover_ipv6_addr + data_path: [ftdHABootstrap,statefulFailover] + example: false + - model_name: name + type: String + tf_name: stateful_failover_interface_name + data_path: [ftdHABootstrap,statefulFailover,interfaceObject] + description: Name of physical interface + example: GigabitEthernet0/0 + - model_name: id + type: String + tf_name: stateful_failover_interface_id + data_path: [ftdHABootstrap,statefulFailover,interfaceObject] + description: ID of physical interface. + example: 76d24097-hj7r-7786-a4d0-a8c07ac08470 + - model_name: type + type: String + tf_name: stateful_failover_interface_type + data_path: [ftdHABootstrap,statefulFailover,interfaceObject] + description: Type of physical interface. + example: PhysicalInterface + - model_name: action + type: String + description: FTD HA PUT operation action. Specifically used for manual switch. HA Break will be triggered when you run terraform destroy + enum_values: [ SWITCH ] + exclude_test: true + example: SWITCH +test_prerequisites: |- + variable "device_id" { default = null } // tests will set $TF_VAR_device_id \ No newline at end of file diff --git a/internal/provider/data_source_fmc_device_ha_pair.go b/internal/provider/data_source_fmc_device_ha_pair.go new file mode 100644 index 00000000..71788b2e --- /dev/null +++ b/internal/provider/data_source_fmc_device_ha_pair.go @@ -0,0 +1,259 @@ +// Copyright © 2023 Cisco Systems, Inc. and its affiliates. +// All rights reserved. +// +// Licensed under the Mozilla Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://mozilla.org/MPL/2.0/ +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: MPL-2.0 + +package provider + +// Section below is generated&owned by "gen/generator.go". //template:begin imports +import ( + "context" + "fmt" + "net/url" + + "github.com/hashicorp/terraform-plugin-framework-validators/datasourcevalidator" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/netascode/go-fmc" + "github.com/tidwall/gjson" +) + +// End of section. //template:end imports + +// Section below is generated&owned by "gen/generator.go". //template:begin model + +// Ensure the implementation satisfies the expected interfaces. +var ( + _ datasource.DataSource = &DeviceHAPairDataSource{} + _ datasource.DataSourceWithConfigure = &DeviceHAPairDataSource{} +) + +func NewDeviceHAPairDataSource() datasource.DataSource { + return &DeviceHAPairDataSource{} +} + +type DeviceHAPairDataSource struct { + client *fmc.Client +} + +func (d *DeviceHAPairDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_device_ha_pair" +} + +func (d *DeviceHAPairDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + // This description is used by the documentation generator and the language server. + MarkdownDescription: "This data source can read the Device HA Pair.", + + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + MarkdownDescription: "The id of the object", + Optional: true, + Computed: true, + }, + "domain": schema.StringAttribute{ + MarkdownDescription: "The name of the FMC domain", + Optional: true, + }, + "name": schema.StringAttribute{ + MarkdownDescription: "The name of the High Availability Pair.", + Optional: true, + Computed: true, + }, + "primary_device_id": schema.StringAttribute{ + MarkdownDescription: "ID of primary FTD in the HA Pair.", + Computed: true, + }, + "secondary_device_id": schema.StringAttribute{ + MarkdownDescription: "ID of secondary FTD in the HA Pair.", + Computed: true, + }, + "is_encryption_enabled": schema.BoolAttribute{ + MarkdownDescription: "Boolean field to enable encryption", + Computed: true, + }, + "use_same_link_for_failovers": schema.BoolAttribute{ + MarkdownDescription: "Boolean field to enable same link for failovers", + Computed: true, + }, + "shared_key": schema.StringAttribute{ + MarkdownDescription: "Pass the unique shared key if needed.", + Computed: true, + }, + "enc_key_generation_scheme": schema.StringAttribute{ + MarkdownDescription: "Select the encyption key generation scheme.", + Computed: true, + }, + "lan_failover_standby_ip": schema.StringAttribute{ + MarkdownDescription: "", + Computed: true, + }, + "lan_failover_active_ip": schema.StringAttribute{ + MarkdownDescription: "", + Computed: true, + }, + "lan_failover_name": schema.StringAttribute{ + MarkdownDescription: "", + Computed: true, + }, + "lan_failover_netmask": schema.StringAttribute{ + MarkdownDescription: "", + Computed: true, + }, + "lan_failover_ipv6_addr": schema.BoolAttribute{ + MarkdownDescription: "", + Computed: true, + }, + "lan_failover_interface_name": schema.StringAttribute{ + MarkdownDescription: "Name of physical interface", + Optional: true, + Computed: true, + }, + "lan_failover_interface_id": schema.StringAttribute{ + MarkdownDescription: "ID of physical interface.", + Computed: true, + }, + "lan_failover_interface_type": schema.StringAttribute{ + MarkdownDescription: "Type of physical interface.", + Computed: true, + }, + "stateful_failover_standby_ip": schema.StringAttribute{ + MarkdownDescription: "", + Computed: true, + }, + "stateful_failover_active_ip": schema.StringAttribute{ + MarkdownDescription: "", + Computed: true, + }, + "stateful_failover_name": schema.StringAttribute{ + MarkdownDescription: "", + Computed: true, + }, + "stateful_failover_subnet_mask": schema.StringAttribute{ + MarkdownDescription: "", + Computed: true, + }, + "stateful_failover_ipv6_addr": schema.BoolAttribute{ + MarkdownDescription: "", + Computed: true, + }, + "stateful_failover_interface_name": schema.StringAttribute{ + MarkdownDescription: "Name of physical interface", + Optional: true, + Computed: true, + }, + "stateful_failover_interface_id": schema.StringAttribute{ + MarkdownDescription: "ID of physical interface.", + Computed: true, + }, + "stateful_failover_interface_type": schema.StringAttribute{ + MarkdownDescription: "Type of physical interface.", + Computed: true, + }, + "action": schema.StringAttribute{ + MarkdownDescription: "FTD HA PUT operation action. Specifically used for manual switch. HA Break will be triggered when you run terraform destroy", + Computed: true, + }, + }, + } +} +func (d *DeviceHAPairDataSource) ConfigValidators(ctx context.Context) []datasource.ConfigValidator { + return []datasource.ConfigValidator{ + datasourcevalidator.ExactlyOneOf( + path.MatchRoot("id"), + path.MatchRoot("name"), + ), + } +} + +func (d *DeviceHAPairDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, _ *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + d.client = req.ProviderData.(*FmcProviderData).Client +} + +// End of section. //template:end model + +// Section below is generated&owned by "gen/generator.go". //template:begin read + +func (d *DeviceHAPairDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var config DeviceHAPair + + // Read config + diags := req.Config.Get(ctx, &config) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + // Set request domain if provided + reqMods := [](func(*fmc.Req)){} + if !config.Domain.IsNull() && config.Domain.ValueString() != "" { + reqMods = append(reqMods, fmc.DomainName(config.Domain.ValueString())) + } + + tflog.Debug(ctx, fmt.Sprintf("%s: Beginning Read", config.Id.String())) + if config.Id.IsNull() && !config.Name.IsNull() { + offset := 0 + limit := 1000 + for page := 1; ; page++ { + queryString := fmt.Sprintf("?limit=%d&offset=%d", limit, offset) + res, err := d.client.Get(config.getPath()+queryString, reqMods...) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to retrieve objects, got error: %s", err)) + return + } + if value := res.Get("items"); len(value.Array()) > 0 { + value.ForEach(func(k, v gjson.Result) bool { + if config.Name.ValueString() == v.Get("name").String() { + config.Id = types.StringValue(v.Get("id").String()) + tflog.Debug(ctx, fmt.Sprintf("%s: Found object with name '%v', id: %v", config.Id.String(), config.Name.ValueString(), config.Id.String())) + return false + } + return true + }) + } + if !config.Id.IsNull() || !res.Get("paging.next.0").Exists() { + break + } + offset += limit + } + + if config.Id.IsNull() { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to find object with name: %s", config.Name.ValueString())) + return + } + } + urlPath := config.getPath() + "/" + url.QueryEscape(config.Id.ValueString()) + res, err := d.client.Get(urlPath, reqMods...) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to retrieve object, got error: %s", err)) + return + } + + config.fromBody(ctx, res) + + tflog.Debug(ctx, fmt.Sprintf("%s: Read finished successfully", config.Id.ValueString())) + + diags = resp.State.Set(ctx, &config) + resp.Diagnostics.Append(diags...) +} + +// End of section. //template:end read diff --git a/internal/provider/data_source_fmc_device_ha_pair_test.go b/internal/provider/data_source_fmc_device_ha_pair_test.go new file mode 100644 index 00000000..b9795ee5 --- /dev/null +++ b/internal/provider/data_source_fmc_device_ha_pair_test.go @@ -0,0 +1,152 @@ +// Copyright © 2023 Cisco Systems, Inc. and its affiliates. +// All rights reserved. +// +// Licensed under the Mozilla Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://mozilla.org/MPL/2.0/ +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: MPL-2.0 + +package provider + +// Section below is generated&owned by "gen/generator.go". //template:begin imports +import ( + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +// End of section. //template:end imports + +// Section below is generated&owned by "gen/generator.go". //template:begin testAccDataSource + +func TestAccDataSourceFmcDeviceHAPair(t *testing.T) { + if os.Getenv("TF_VAR_device_id") == "" && os.Getenv("TF_VAR_device_2_id") == "" { + t.Skip("skipping test, set environment variable TF_VAR_device_id or TF_VAR_device_2_id") + } + var checks []resource.TestCheckFunc + checks = append(checks, resource.TestCheckResourceAttr("data.fmc_device_ha_pair.test", "name", "FTD_HA")) + checks = append(checks, resource.TestCheckResourceAttr("data.fmc_device_ha_pair.test", "is_encryption_enabled", "false")) + checks = append(checks, resource.TestCheckResourceAttr("data.fmc_device_ha_pair.test", "shared_key", "cisco123")) + checks = append(checks, resource.TestCheckResourceAttr("data.fmc_device_ha_pair.test", "lan_failover_standby_ip", "1.1.1.2")) + checks = append(checks, resource.TestCheckResourceAttr("data.fmc_device_ha_pair.test", "lan_failover_active_ip", "1.1.1.1")) + checks = append(checks, resource.TestCheckResourceAttr("data.fmc_device_ha_pair.test", "lan_failover_name", "LAN-INTERFACE")) + checks = append(checks, resource.TestCheckResourceAttr("data.fmc_device_ha_pair.test", "lan_failover_netmask", "255.255.255.0")) + checks = append(checks, resource.TestCheckResourceAttr("data.fmc_device_ha_pair.test", "lan_failover_ipv6_addr", "false")) + checks = append(checks, resource.TestCheckResourceAttr("data.fmc_device_ha_pair.test", "lan_failover_interface_name", "GigabitEthernet0/0")) + checks = append(checks, resource.TestCheckResourceAttr("data.fmc_device_ha_pair.test", "stateful_failover_standby_ip", "10.10.10.2")) + checks = append(checks, resource.TestCheckResourceAttr("data.fmc_device_ha_pair.test", "stateful_failover_active_ip", "10.10.10.1")) + checks = append(checks, resource.TestCheckResourceAttr("data.fmc_device_ha_pair.test", "stateful_failover_name", "Stateful-INTERFACE")) + checks = append(checks, resource.TestCheckResourceAttr("data.fmc_device_ha_pair.test", "stateful_failover_subnet_mask", "255.255.255.0")) + checks = append(checks, resource.TestCheckResourceAttr("data.fmc_device_ha_pair.test", "stateful_failover_ipv6_addr", "false")) + checks = append(checks, resource.TestCheckResourceAttr("data.fmc_device_ha_pair.test", "stateful_failover_interface_name", "GigabitEthernet0/0")) + checks = append(checks, resource.TestCheckResourceAttr("data.fmc_device_ha_pair.test", "stateful_failover_interface_id", "76d24097-hj7r-7786-a4d0-a8c07ac08470")) + checks = append(checks, resource.TestCheckResourceAttr("data.fmc_device_ha_pair.test", "stateful_failover_interface_type", "PhysicalInterface")) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceFmcDeviceHAPairPrerequisitesConfig + testAccDataSourceFmcDeviceHAPairConfig(), + Check: resource.ComposeTestCheckFunc(checks...), + }, + { + Config: testAccDataSourceFmcDeviceHAPairPrerequisitesConfig + testAccNamedDataSourceFmcDeviceHAPairConfig(), + Check: resource.ComposeTestCheckFunc(checks...), + }, + }, + }) +} + +// End of section. //template:end testAccDataSource + +// Section below is generated&owned by "gen/generator.go". //template:begin testPrerequisites + +const testAccDataSourceFmcDeviceHAPairPrerequisitesConfig = ` +variable "device_id" { default = null } // tests will set $TF_VAR_device_id +` + +// End of section. //template:end testPrerequisites + +// Section below is generated&owned by "gen/generator.go". //template:begin testAccDataSourceConfig + +func testAccDataSourceFmcDeviceHAPairConfig() string { + config := `resource "fmc_device_ha_pair" "test" {` + "\n" + config += ` name = "FTD_HA"` + "\n" + config += ` primary_device_id = var.device_id` + "\n" + config += ` secondary_device_id = var.device_2_id` + "\n" + config += ` is_encryption_enabled = false` + "\n" + config += ` use_same_link_for_failovers = false` + "\n" + config += ` shared_key = "cisco123"` + "\n" + config += ` enc_key_generation_scheme = "CUSTOM"` + "\n" + config += ` lan_failover_standby_ip = "1.1.1.2"` + "\n" + config += ` lan_failover_active_ip = "1.1.1.1"` + "\n" + config += ` lan_failover_name = "LAN-INTERFACE"` + "\n" + config += ` lan_failover_netmask = "255.255.255.0"` + "\n" + config += ` lan_failover_ipv6_addr = false` + "\n" + config += ` lan_failover_interface_name = "GigabitEthernet0/0"` + "\n" + config += ` lan_failover_interface_id = "757kdgh5-41c4-4558-a4d0-a8c07ac08470"` + "\n" + config += ` lan_failover_interface_type = "PhysicalInterface"` + "\n" + config += ` stateful_failover_standby_ip = "10.10.10.2"` + "\n" + config += ` stateful_failover_active_ip = "10.10.10.1"` + "\n" + config += ` stateful_failover_name = "Stateful-INTERFACE"` + "\n" + config += ` stateful_failover_subnet_mask = "255.255.255.0"` + "\n" + config += ` stateful_failover_ipv6_addr = false` + "\n" + config += ` stateful_failover_interface_name = "GigabitEthernet0/0"` + "\n" + config += ` stateful_failover_interface_id = "76d24097-hj7r-7786-a4d0-a8c07ac08470"` + "\n" + config += ` stateful_failover_interface_type = "PhysicalInterface"` + "\n" + config += `}` + "\n" + + config += ` + data "fmc_device_ha_pair" "test" { + id = fmc_device_ha_pair.test.id + } + ` + return config +} + +func testAccNamedDataSourceFmcDeviceHAPairConfig() string { + config := `resource "fmc_device_ha_pair" "test" {` + "\n" + config += ` name = "FTD_HA"` + "\n" + config += ` primary_device_id = var.device_id` + "\n" + config += ` secondary_device_id = var.device_2_id` + "\n" + config += ` is_encryption_enabled = false` + "\n" + config += ` use_same_link_for_failovers = false` + "\n" + config += ` shared_key = "cisco123"` + "\n" + config += ` enc_key_generation_scheme = "CUSTOM"` + "\n" + config += ` lan_failover_standby_ip = "1.1.1.2"` + "\n" + config += ` lan_failover_active_ip = "1.1.1.1"` + "\n" + config += ` lan_failover_name = "LAN-INTERFACE"` + "\n" + config += ` lan_failover_netmask = "255.255.255.0"` + "\n" + config += ` lan_failover_ipv6_addr = false` + "\n" + config += ` lan_failover_interface_name = "GigabitEthernet0/0"` + "\n" + config += ` lan_failover_interface_id = "757kdgh5-41c4-4558-a4d0-a8c07ac08470"` + "\n" + config += ` lan_failover_interface_type = "PhysicalInterface"` + "\n" + config += ` stateful_failover_standby_ip = "10.10.10.2"` + "\n" + config += ` stateful_failover_active_ip = "10.10.10.1"` + "\n" + config += ` stateful_failover_name = "Stateful-INTERFACE"` + "\n" + config += ` stateful_failover_subnet_mask = "255.255.255.0"` + "\n" + config += ` stateful_failover_ipv6_addr = false` + "\n" + config += ` stateful_failover_interface_name = "GigabitEthernet0/0"` + "\n" + config += ` stateful_failover_interface_id = "76d24097-hj7r-7786-a4d0-a8c07ac08470"` + "\n" + config += ` stateful_failover_interface_type = "PhysicalInterface"` + "\n" + config += `}` + "\n" + + config += ` + data "fmc_device_ha_pair" "test" { + name = fmc_device_ha_pair.test.name + } + ` + return config +} + +// End of section. //template:end testAccDataSourceConfig diff --git a/internal/provider/model_fmc_device_ha_pair.go b/internal/provider/model_fmc_device_ha_pair.go new file mode 100644 index 00000000..76386ae3 --- /dev/null +++ b/internal/provider/model_fmc_device_ha_pair.go @@ -0,0 +1,415 @@ +// Copyright © 2023 Cisco Systems, Inc. and its affiliates. +// All rights reserved. +// +// Licensed under the Mozilla Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://mozilla.org/MPL/2.0/ +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: MPL-2.0 + +package provider + +// Section below is generated&owned by "gen/generator.go". //template:begin imports +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/tidwall/gjson" + "github.com/tidwall/sjson" +) + +// End of section. //template:end imports + +// Section below is generated&owned by "gen/generator.go". //template:begin types + +type DeviceHAPair struct { + Id types.String `tfsdk:"id"` + Domain types.String `tfsdk:"domain"` + Name types.String `tfsdk:"name"` + PrimaryDeviceId types.String `tfsdk:"primary_device_id"` + SecondaryDeviceId types.String `tfsdk:"secondary_device_id"` + IsEncryptionEnabled types.Bool `tfsdk:"is_encryption_enabled"` + UseSameLinkForFailovers types.Bool `tfsdk:"use_same_link_for_failovers"` + SharedKey types.String `tfsdk:"shared_key"` + EncKeyGenerationScheme types.String `tfsdk:"enc_key_generation_scheme"` + LanFailoverStandbyIp types.String `tfsdk:"lan_failover_standby_ip"` + LanFailoverActiveIp types.String `tfsdk:"lan_failover_active_ip"` + LanFailoverName types.String `tfsdk:"lan_failover_name"` + LanFailoverNetmask types.String `tfsdk:"lan_failover_netmask"` + LanFailoverIpv6Addr types.Bool `tfsdk:"lan_failover_ipv6_addr"` + LanFailoverInterfaceName types.String `tfsdk:"lan_failover_interface_name"` + LanFailoverInterfaceId types.String `tfsdk:"lan_failover_interface_id"` + LanFailoverInterfaceType types.String `tfsdk:"lan_failover_interface_type"` + StatefulFailoverStandbyIp types.String `tfsdk:"stateful_failover_standby_ip"` + StatefulFailoverActiveIp types.String `tfsdk:"stateful_failover_active_ip"` + StatefulFailoverName types.String `tfsdk:"stateful_failover_name"` + StatefulFailoverSubnetMask types.String `tfsdk:"stateful_failover_subnet_mask"` + StatefulFailoverIpv6Addr types.Bool `tfsdk:"stateful_failover_ipv6_addr"` + StatefulFailoverInterfaceName types.String `tfsdk:"stateful_failover_interface_name"` + StatefulFailoverInterfaceId types.String `tfsdk:"stateful_failover_interface_id"` + StatefulFailoverInterfaceType types.String `tfsdk:"stateful_failover_interface_type"` + Action types.String `tfsdk:"action"` +} + +// End of section. //template:end types + +// Section below is generated&owned by "gen/generator.go". //template:begin getPath + +func (data DeviceHAPair) getPath() string { + return "/api/fmc_config/v1/domain/{DOMAIN_UUID}/devicehapairs/ftddevicehapairs" +} + +// End of section. //template:end getPath + +// Section below is generated&owned by "gen/generator.go". //template:begin toBody + +func (data DeviceHAPair) toBody(ctx context.Context, state DeviceHAPair) string { + body := "" + if data.Id.ValueString() != "" { + body, _ = sjson.Set(body, "id", data.Id.ValueString()) + } + if !data.Name.IsNull() { + body, _ = sjson.Set(body, "name", data.Name.ValueString()) + } + body, _ = sjson.Set(body, "type", "DeviceHAPair") + if !data.PrimaryDeviceId.IsNull() { + body, _ = sjson.Set(body, "primary.id", data.PrimaryDeviceId.ValueString()) + } + if !data.SecondaryDeviceId.IsNull() { + body, _ = sjson.Set(body, "secondary.id", data.SecondaryDeviceId.ValueString()) + } + if !data.IsEncryptionEnabled.IsNull() { + body, _ = sjson.Set(body, "ftdHABootstrap.isEncryptionEnabled", data.IsEncryptionEnabled.ValueBool()) + } + if !data.UseSameLinkForFailovers.IsNull() { + body, _ = sjson.Set(body, "ftdHABootstrap.useSameLinkForFailovers", data.UseSameLinkForFailovers.ValueBool()) + } + if !data.SharedKey.IsNull() { + body, _ = sjson.Set(body, "ftdHABootstrap.sharedKey", data.SharedKey.ValueString()) + } + if !data.EncKeyGenerationScheme.IsNull() { + body, _ = sjson.Set(body, "ftdHABootstrap.encKeyGenerationScheme", data.EncKeyGenerationScheme.ValueString()) + } + if !data.LanFailoverStandbyIp.IsNull() { + body, _ = sjson.Set(body, "ftdHABootstrap.lanFailover.standbyIP", data.LanFailoverStandbyIp.ValueString()) + } + if !data.LanFailoverActiveIp.IsNull() { + body, _ = sjson.Set(body, "ftdHABootstrap.lanFailover.activeIP", data.LanFailoverActiveIp.ValueString()) + } + if !data.LanFailoverName.IsNull() { + body, _ = sjson.Set(body, "ftdHABootstrap.lanFailover.logicalName", data.LanFailoverName.ValueString()) + } + if !data.LanFailoverNetmask.IsNull() { + body, _ = sjson.Set(body, "ftdHABootstrap.lanFailover.subnetMask", data.LanFailoverNetmask.ValueString()) + } + if !data.LanFailoverIpv6Addr.IsNull() { + body, _ = sjson.Set(body, "ftdHABootstrap.lanFailover.useIPv6Address", data.LanFailoverIpv6Addr.ValueBool()) + } + if !data.LanFailoverInterfaceName.IsNull() { + body, _ = sjson.Set(body, "ftdHABootstrap.lanFailover.interfaceObject.name", data.LanFailoverInterfaceName.ValueString()) + } + if !data.LanFailoverInterfaceId.IsNull() { + body, _ = sjson.Set(body, "ftdHABootstrap.lanFailover.interfaceObject.id", data.LanFailoverInterfaceId.ValueString()) + } + if !data.LanFailoverInterfaceType.IsNull() { + body, _ = sjson.Set(body, "ftdHABootstrap.lanFailover.interfaceObject.type", data.LanFailoverInterfaceType.ValueString()) + } + if !data.StatefulFailoverStandbyIp.IsNull() { + body, _ = sjson.Set(body, "ftdHABootstrap.statefulFailover.standbyIP", data.StatefulFailoverStandbyIp.ValueString()) + } + if !data.StatefulFailoverActiveIp.IsNull() { + body, _ = sjson.Set(body, "ftdHABootstrap.statefulFailover.activeIP", data.StatefulFailoverActiveIp.ValueString()) + } + if !data.StatefulFailoverName.IsNull() { + body, _ = sjson.Set(body, "ftdHABootstrap.statefulFailover.logicalName", data.StatefulFailoverName.ValueString()) + } + if !data.StatefulFailoverSubnetMask.IsNull() { + body, _ = sjson.Set(body, "ftdHABootstrap.statefulFailover.subnetMask", data.StatefulFailoverSubnetMask.ValueString()) + } + if !data.StatefulFailoverIpv6Addr.IsNull() { + body, _ = sjson.Set(body, "ftdHABootstrap.statefulFailover.useIPv6Address", data.StatefulFailoverIpv6Addr.ValueBool()) + } + if !data.StatefulFailoverInterfaceName.IsNull() { + body, _ = sjson.Set(body, "ftdHABootstrap.statefulFailover.interfaceObject.name", data.StatefulFailoverInterfaceName.ValueString()) + } + if !data.StatefulFailoverInterfaceId.IsNull() { + body, _ = sjson.Set(body, "ftdHABootstrap.statefulFailover.interfaceObject.id", data.StatefulFailoverInterfaceId.ValueString()) + } + if !data.StatefulFailoverInterfaceType.IsNull() { + body, _ = sjson.Set(body, "ftdHABootstrap.statefulFailover.interfaceObject.type", data.StatefulFailoverInterfaceType.ValueString()) + } + if !data.Action.IsNull() { + body, _ = sjson.Set(body, "action", data.Action.ValueString()) + } + return body +} + +// End of section. //template:end toBody + +// Section below is generated&owned by "gen/generator.go". //template:begin fromBody + +func (data *DeviceHAPair) fromBody(ctx context.Context, res gjson.Result) { + if value := res.Get("name"); value.Exists() { + data.Name = types.StringValue(value.String()) + } else { + data.Name = types.StringNull() + } + if value := res.Get("primary.id"); value.Exists() { + data.PrimaryDeviceId = types.StringValue(value.String()) + } else { + data.PrimaryDeviceId = types.StringNull() + } + if value := res.Get("secondary.id"); value.Exists() { + data.SecondaryDeviceId = types.StringValue(value.String()) + } else { + data.SecondaryDeviceId = types.StringNull() + } + if value := res.Get("ftdHABootstrap.isEncryptionEnabled"); value.Exists() { + data.IsEncryptionEnabled = types.BoolValue(value.Bool()) + } else { + data.IsEncryptionEnabled = types.BoolNull() + } + if value := res.Get("ftdHABootstrap.sharedKey"); value.Exists() { + data.SharedKey = types.StringValue(value.String()) + } else { + data.SharedKey = types.StringNull() + } + if value := res.Get("ftdHABootstrap.lanFailover.standbyIP"); value.Exists() { + data.LanFailoverStandbyIp = types.StringValue(value.String()) + } else { + data.LanFailoverStandbyIp = types.StringNull() + } + if value := res.Get("ftdHABootstrap.lanFailover.activeIP"); value.Exists() { + data.LanFailoverActiveIp = types.StringValue(value.String()) + } else { + data.LanFailoverActiveIp = types.StringNull() + } + if value := res.Get("ftdHABootstrap.lanFailover.logicalName"); value.Exists() { + data.LanFailoverName = types.StringValue(value.String()) + } else { + data.LanFailoverName = types.StringNull() + } + if value := res.Get("ftdHABootstrap.lanFailover.subnetMask"); value.Exists() { + data.LanFailoverNetmask = types.StringValue(value.String()) + } else { + data.LanFailoverNetmask = types.StringNull() + } + if value := res.Get("ftdHABootstrap.lanFailover.useIPv6Address"); value.Exists() { + data.LanFailoverIpv6Addr = types.BoolValue(value.Bool()) + } else { + data.LanFailoverIpv6Addr = types.BoolValue(false) + } + if value := res.Get("ftdHABootstrap.lanFailover.interfaceObject.name"); value.Exists() { + data.LanFailoverInterfaceName = types.StringValue(value.String()) + } else { + data.LanFailoverInterfaceName = types.StringNull() + } + if value := res.Get("ftdHABootstrap.statefulFailover.standbyIP"); value.Exists() { + data.StatefulFailoverStandbyIp = types.StringValue(value.String()) + } else { + data.StatefulFailoverStandbyIp = types.StringNull() + } + if value := res.Get("ftdHABootstrap.statefulFailover.activeIP"); value.Exists() { + data.StatefulFailoverActiveIp = types.StringValue(value.String()) + } else { + data.StatefulFailoverActiveIp = types.StringNull() + } + if value := res.Get("ftdHABootstrap.statefulFailover.logicalName"); value.Exists() { + data.StatefulFailoverName = types.StringValue(value.String()) + } else { + data.StatefulFailoverName = types.StringNull() + } + if value := res.Get("ftdHABootstrap.statefulFailover.subnetMask"); value.Exists() { + data.StatefulFailoverSubnetMask = types.StringValue(value.String()) + } else { + data.StatefulFailoverSubnetMask = types.StringNull() + } + if value := res.Get("ftdHABootstrap.statefulFailover.useIPv6Address"); value.Exists() { + data.StatefulFailoverIpv6Addr = types.BoolValue(value.Bool()) + } else { + data.StatefulFailoverIpv6Addr = types.BoolNull() + } + if value := res.Get("ftdHABootstrap.statefulFailover.interfaceObject.name"); value.Exists() { + data.StatefulFailoverInterfaceName = types.StringValue(value.String()) + } else { + data.StatefulFailoverInterfaceName = types.StringNull() + } + if value := res.Get("ftdHABootstrap.statefulFailover.interfaceObject.id"); value.Exists() { + data.StatefulFailoverInterfaceId = types.StringValue(value.String()) + } else { + data.StatefulFailoverInterfaceId = types.StringNull() + } + if value := res.Get("ftdHABootstrap.statefulFailover.interfaceObject.type"); value.Exists() { + data.StatefulFailoverInterfaceType = types.StringValue(value.String()) + } else { + data.StatefulFailoverInterfaceType = types.StringNull() + } + if value := res.Get("action"); value.Exists() { + data.Action = types.StringValue(value.String()) + } else { + data.Action = types.StringNull() + } +} + +// End of section. //template:end fromBody + +// Section below is generated&owned by "gen/generator.go". //template:begin fromBodyPartial + +// fromBodyPartial reads values from a gjson.Result into a tfstate model. It ignores null attributes in order to +// uncouple the provider from the exact values that the backend API might summon to replace nulls. (Such behavior might +// easily change across versions of the backend API.) For List/Set/Map attributes, the func only updates the +// "managed" elements, instead of all elements. +func (data *DeviceHAPair) fromBodyPartial(ctx context.Context, res gjson.Result) { + if value := res.Get("name"); value.Exists() && !data.Name.IsNull() { + data.Name = types.StringValue(value.String()) + } else { + data.Name = types.StringNull() + } + if value := res.Get("primary.id"); value.Exists() && !data.PrimaryDeviceId.IsNull() { + data.PrimaryDeviceId = types.StringValue(value.String()) + } else { + data.PrimaryDeviceId = types.StringNull() + } + if value := res.Get("secondary.id"); value.Exists() && !data.SecondaryDeviceId.IsNull() { + data.SecondaryDeviceId = types.StringValue(value.String()) + } else { + data.SecondaryDeviceId = types.StringNull() + } + if value := res.Get("ftdHABootstrap.isEncryptionEnabled"); value.Exists() && !data.IsEncryptionEnabled.IsNull() { + data.IsEncryptionEnabled = types.BoolValue(value.Bool()) + } else { + data.IsEncryptionEnabled = types.BoolNull() + } + if value := res.Get("ftdHABootstrap.sharedKey"); value.Exists() && !data.SharedKey.IsNull() { + data.SharedKey = types.StringValue(value.String()) + } else { + data.SharedKey = types.StringNull() + } + if value := res.Get("ftdHABootstrap.lanFailover.standbyIP"); value.Exists() && !data.LanFailoverStandbyIp.IsNull() { + data.LanFailoverStandbyIp = types.StringValue(value.String()) + } else { + data.LanFailoverStandbyIp = types.StringNull() + } + if value := res.Get("ftdHABootstrap.lanFailover.activeIP"); value.Exists() && !data.LanFailoverActiveIp.IsNull() { + data.LanFailoverActiveIp = types.StringValue(value.String()) + } else { + data.LanFailoverActiveIp = types.StringNull() + } + if value := res.Get("ftdHABootstrap.lanFailover.logicalName"); value.Exists() && !data.LanFailoverName.IsNull() { + data.LanFailoverName = types.StringValue(value.String()) + } else { + data.LanFailoverName = types.StringNull() + } + if value := res.Get("ftdHABootstrap.lanFailover.subnetMask"); value.Exists() && !data.LanFailoverNetmask.IsNull() { + data.LanFailoverNetmask = types.StringValue(value.String()) + } else { + data.LanFailoverNetmask = types.StringNull() + } + if value := res.Get("ftdHABootstrap.lanFailover.useIPv6Address"); value.Exists() && !data.LanFailoverIpv6Addr.IsNull() { + data.LanFailoverIpv6Addr = types.BoolValue(value.Bool()) + } else if data.LanFailoverIpv6Addr.ValueBool() != false { + data.LanFailoverIpv6Addr = types.BoolNull() + } + if value := res.Get("ftdHABootstrap.lanFailover.interfaceObject.name"); value.Exists() && !data.LanFailoverInterfaceName.IsNull() { + data.LanFailoverInterfaceName = types.StringValue(value.String()) + } else { + data.LanFailoverInterfaceName = types.StringNull() + } + if value := res.Get("ftdHABootstrap.statefulFailover.standbyIP"); value.Exists() && !data.StatefulFailoverStandbyIp.IsNull() { + data.StatefulFailoverStandbyIp = types.StringValue(value.String()) + } else { + data.StatefulFailoverStandbyIp = types.StringNull() + } + if value := res.Get("ftdHABootstrap.statefulFailover.activeIP"); value.Exists() && !data.StatefulFailoverActiveIp.IsNull() { + data.StatefulFailoverActiveIp = types.StringValue(value.String()) + } else { + data.StatefulFailoverActiveIp = types.StringNull() + } + if value := res.Get("ftdHABootstrap.statefulFailover.logicalName"); value.Exists() && !data.StatefulFailoverName.IsNull() { + data.StatefulFailoverName = types.StringValue(value.String()) + } else { + data.StatefulFailoverName = types.StringNull() + } + if value := res.Get("ftdHABootstrap.statefulFailover.subnetMask"); value.Exists() && !data.StatefulFailoverSubnetMask.IsNull() { + data.StatefulFailoverSubnetMask = types.StringValue(value.String()) + } else { + data.StatefulFailoverSubnetMask = types.StringNull() + } + if value := res.Get("ftdHABootstrap.statefulFailover.useIPv6Address"); value.Exists() && !data.StatefulFailoverIpv6Addr.IsNull() { + data.StatefulFailoverIpv6Addr = types.BoolValue(value.Bool()) + } else { + data.StatefulFailoverIpv6Addr = types.BoolNull() + } + if value := res.Get("ftdHABootstrap.statefulFailover.interfaceObject.name"); value.Exists() && !data.StatefulFailoverInterfaceName.IsNull() { + data.StatefulFailoverInterfaceName = types.StringValue(value.String()) + } else { + data.StatefulFailoverInterfaceName = types.StringNull() + } + if value := res.Get("ftdHABootstrap.statefulFailover.interfaceObject.id"); value.Exists() && !data.StatefulFailoverInterfaceId.IsNull() { + data.StatefulFailoverInterfaceId = types.StringValue(value.String()) + } else { + data.StatefulFailoverInterfaceId = types.StringNull() + } + if value := res.Get("ftdHABootstrap.statefulFailover.interfaceObject.type"); value.Exists() && !data.StatefulFailoverInterfaceType.IsNull() { + data.StatefulFailoverInterfaceType = types.StringValue(value.String()) + } else { + data.StatefulFailoverInterfaceType = types.StringNull() + } + if value := res.Get("action"); value.Exists() && !data.Action.IsNull() { + data.Action = types.StringValue(value.String()) + } else { + data.Action = types.StringNull() + } +} + +// End of section. //template:end fromBodyPartial + +// Section below is generated&owned by "gen/generator.go". //template:begin fromBodyUnknowns + +// fromBodyUnknowns updates the Unknown Computed tfstate values from a JSON. +// Known values are not changed (usual for Computed attributes with UseStateForUnknown or with Default). +func (data *DeviceHAPair) fromBodyUnknowns(ctx context.Context, res gjson.Result) { +} + +// End of section. //template:end fromBodyUnknowns + +// Section below is generated&owned by "gen/generator.go". //template:begin Clone + +// End of section. //template:end Clone + +// Section below is generated&owned by "gen/generator.go". //template:begin toBodyNonBulk + +// End of section. //template:end toBodyNonBulk + +// toBodyPutDelete generates minimal required body to brek the HA. +func (data DeviceHAPair) toBodyPutDelete(ctx context.Context, state DeviceHAPair) string { + body := "" + if data.Id.ValueString() != "" { + body, _ = sjson.Set(body, "id", data.Id.ValueString()) + } + + body, _ = sjson.Set(body, "action", "HABREAK") + body, _ = sjson.Set(body, "forceBreak", true) + return body +} +func (data DeviceHAPair) toBodyPutUpdate(ctx context.Context, state DeviceHAPair) string { + body := "" + if data.Id.ValueString() != "" { + body, _ = sjson.Set(body, "id", data.Id.ValueString()) + } + if data.Name.ValueString() != "" { + body, _ = sjson.Set(body, "name", data.Name.ValueString()) + } + if data.Action.ValueString() == "SWITCH" { + body, _ = sjson.Set(body, "action", "SWITCH") + } + return body +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 33662261..2f758dc1 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -295,6 +295,7 @@ func (p *FmcProvider) Resources(ctx context.Context) []func() resource.Resource NewAccessControlPolicyResource, NewDeviceResource, NewDeviceEtherChannelInterfaceResource, + NewDeviceHAPairResource, NewDeviceIPv4StaticRouteResource, NewDeviceIPv6StaticRouteResource, NewDevicePhysicalInterfaceResource, @@ -346,6 +347,7 @@ func (p *FmcProvider) DataSources(ctx context.Context) []func() datasource.DataS NewAccessControlPolicyDataSource, NewDeviceDataSource, NewDeviceEtherChannelInterfaceDataSource, + NewDeviceHAPairDataSource, NewDeviceIPv4StaticRouteDataSource, NewDeviceIPv6StaticRouteDataSource, NewDevicePhysicalInterfaceDataSource, diff --git a/internal/provider/resource_fmc_device_ha_pair.go b/internal/provider/resource_fmc_device_ha_pair.go new file mode 100644 index 00000000..b48991d3 --- /dev/null +++ b/internal/provider/resource_fmc_device_ha_pair.go @@ -0,0 +1,432 @@ +// Copyright © 2023 Cisco Systems, Inc. and its affiliates. +// All rights reserved. +// +// Licensed under the Mozilla Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://mozilla.org/MPL/2.0/ +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: MPL-2.0 + +package provider + +// Section below is generated&owned by "gen/generator.go". //template:begin imports +import ( + "context" + "fmt" + "net/url" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/netascode/go-fmc" + "github.com/netascode/terraform-provider-fmc/internal/provider/helpers" +) + +// End of section. //template:end imports + +// Section below is generated&owned by "gen/generator.go". //template:begin model + +// Ensure provider defined types fully satisfy framework interfaces +var ( + _ resource.Resource = &DeviceHAPairResource{} + _ resource.ResourceWithImportState = &DeviceHAPairResource{} +) + +func NewDeviceHAPairResource() resource.Resource { + return &DeviceHAPairResource{} +} + +type DeviceHAPairResource struct { + client *fmc.Client +} + +func (r *DeviceHAPairResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_device_ha_pair" +} + +func (r *DeviceHAPairResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + // This description is used by the documentation generator and the language server. + MarkdownDescription: helpers.NewAttributeDescription("This resource can manage a Device HA Pair.").String, + + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + MarkdownDescription: "The id of the object", + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "domain": schema.StringAttribute{ + MarkdownDescription: "The name of the FMC domain", + Optional: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "name": schema.StringAttribute{ + MarkdownDescription: helpers.NewAttributeDescription("The name of the High Availability Pair.").String, + Required: true, + }, + "primary_device_id": schema.StringAttribute{ + MarkdownDescription: helpers.NewAttributeDescription("ID of primary FTD in the HA Pair.").String, + Required: true, + }, + "secondary_device_id": schema.StringAttribute{ + MarkdownDescription: helpers.NewAttributeDescription("ID of secondary FTD in the HA Pair.").String, + Required: true, + }, + "is_encryption_enabled": schema.BoolAttribute{ + MarkdownDescription: helpers.NewAttributeDescription("Boolean field to enable encryption").String, + Optional: true, + }, + "use_same_link_for_failovers": schema.BoolAttribute{ + MarkdownDescription: helpers.NewAttributeDescription("Boolean field to enable same link for failovers").String, + Required: true, + }, + "shared_key": schema.StringAttribute{ + MarkdownDescription: helpers.NewAttributeDescription("Pass the unique shared key if needed.").String, + Optional: true, + }, + "enc_key_generation_scheme": schema.StringAttribute{ + MarkdownDescription: helpers.NewAttributeDescription("Select the encyption key generation scheme.").AddStringEnumDescription("AUTO", "CUSTOM").String, + Optional: true, + Validators: []validator.String{ + stringvalidator.OneOf("AUTO", "CUSTOM"), + }, + }, + "lan_failover_standby_ip": schema.StringAttribute{ + MarkdownDescription: helpers.NewAttributeDescription("").String, + Required: true, + }, + "lan_failover_active_ip": schema.StringAttribute{ + MarkdownDescription: helpers.NewAttributeDescription("").String, + Required: true, + }, + "lan_failover_name": schema.StringAttribute{ + MarkdownDescription: helpers.NewAttributeDescription("").String, + Required: true, + }, + "lan_failover_netmask": schema.StringAttribute{ + MarkdownDescription: helpers.NewAttributeDescription("").String, + Optional: true, + }, + "lan_failover_ipv6_addr": schema.BoolAttribute{ + MarkdownDescription: helpers.NewAttributeDescription("").AddDefaultValueDescription("false").String, + Optional: true, + Computed: true, + Default: booldefault.StaticBool(false), + }, + "lan_failover_interface_name": schema.StringAttribute{ + MarkdownDescription: helpers.NewAttributeDescription("Name of physical interface").String, + Optional: true, + }, + "lan_failover_interface_id": schema.StringAttribute{ + MarkdownDescription: helpers.NewAttributeDescription("ID of physical interface.").String, + Required: true, + }, + "lan_failover_interface_type": schema.StringAttribute{ + MarkdownDescription: helpers.NewAttributeDescription("Type of physical interface.").String, + Required: true, + }, + "stateful_failover_standby_ip": schema.StringAttribute{ + MarkdownDescription: helpers.NewAttributeDescription("").String, + Optional: true, + }, + "stateful_failover_active_ip": schema.StringAttribute{ + MarkdownDescription: helpers.NewAttributeDescription("").String, + Optional: true, + }, + "stateful_failover_name": schema.StringAttribute{ + MarkdownDescription: helpers.NewAttributeDescription("").String, + Optional: true, + }, + "stateful_failover_subnet_mask": schema.StringAttribute{ + MarkdownDescription: helpers.NewAttributeDescription("").String, + Optional: true, + }, + "stateful_failover_ipv6_addr": schema.BoolAttribute{ + MarkdownDescription: helpers.NewAttributeDescription("").String, + Optional: true, + }, + "stateful_failover_interface_name": schema.StringAttribute{ + MarkdownDescription: helpers.NewAttributeDescription("Name of physical interface").String, + Optional: true, + }, + "stateful_failover_interface_id": schema.StringAttribute{ + MarkdownDescription: helpers.NewAttributeDescription("ID of physical interface.").String, + Optional: true, + }, + "stateful_failover_interface_type": schema.StringAttribute{ + MarkdownDescription: helpers.NewAttributeDescription("Type of physical interface.").String, + Optional: true, + }, + "action": schema.StringAttribute{ + MarkdownDescription: helpers.NewAttributeDescription("FTD HA PUT operation action. Specifically used for manual switch. HA Break will be triggered when you run terraform destroy").AddStringEnumDescription("SWITCH").String, + Optional: true, + Validators: []validator.String{ + stringvalidator.OneOf("SWITCH"), + }, + }, + }, + } +} + +func (r *DeviceHAPairResource) Configure(_ context.Context, req resource.ConfigureRequest, _ *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + r.client = req.ProviderData.(*FmcProviderData).Client +} + +// End of section. //template:end model + +func (r *DeviceHAPairResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan DeviceHAPair + + // Read plan + diags := req.Plan.Get(ctx, &plan) + if resp.Diagnostics.Append(diags...); resp.Diagnostics.HasError() { + return + } + + // Set request domain if provided + reqMods := [](func(*fmc.Req)){} + if !plan.Domain.IsNull() && plan.Domain.ValueString() != "" { + reqMods = append(reqMods, fmc.DomainName(plan.Domain.ValueString())) + } + + tflog.Debug(ctx, fmt.Sprintf("%s: Beginning Create", plan.Id.ValueString())) + + // Create object + body := plan.toBody(ctx, DeviceHAPair{}) + res, err := r.client.Post(plan.getPath(), body, reqMods...) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to configure object (POST/PUT), got error: %s, %s", err, res.String())) + return + } + // Adding code to poll object + taskID := res.Get("metadata.task.id").String() + tflog.Debug(ctx, fmt.Sprintf("%s: Async task initiated successfully", taskID)) + + const atom time.Duration = 5 * time.Second + // We need device's UUID, but it only shows after the task succeeds. Poll the task. + for i := time.Duration(0); i < 5*time.Minute; i += atom { + task, err := r.client.Get("/api/fmc_config/v1/domain/{DOMAIN_UUID}/job/taskstatuses/"+url.QueryEscape(taskID), reqMods...) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to read object (GET), got error: %s, %s", err, task.String())) + return + } + stat := strings.ToUpper(task.Get("status").String()) + if stat == "FAILED" { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("API task for the new device failed: %s, %s", task.Get("message"), task.Get("description"))) + return + } + if stat != "PENDING" && stat != "RUNNING" && stat != "IN_PROGRESS" { + break + } + time.Sleep(atom) + } + + check, err := r.client.Get(plan.getPath()) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to read object (GET), got error: %s, %s", err, check)) + return + } + name := "items.#(name==" + url.QueryEscape(plan.Name.ValueString()) + ").id" + id := check.Get(name).String() + plan.Id = types.StringValue(id) + if plan.Id.ValueString() == "" { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("No device named %q: %s", plan.Name.ValueString(), check)) + return + } + + // Ending code to poll object + // plan.Id = types.StringValue(res.Get("id").String()) + plan.fromBodyUnknowns(ctx, res) + + tflog.Debug(ctx, fmt.Sprintf("%s: Create finished successfully", plan.Id.ValueString())) + + diags = resp.State.Set(ctx, &plan) + resp.Diagnostics.Append(diags...) + + helpers.SetFlagImporting(ctx, false, resp.Private, &resp.Diagnostics) +} + +// Section below is generated&owned by "gen/generator.go". //template:begin read + +func (r *DeviceHAPairResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state DeviceHAPair + + // Read state + diags := req.State.Get(ctx, &state) + if resp.Diagnostics.Append(diags...); resp.Diagnostics.HasError() { + return + } + + // Set request domain if provided + reqMods := [](func(*fmc.Req)){} + if !state.Domain.IsNull() && state.Domain.ValueString() != "" { + reqMods = append(reqMods, fmc.DomainName(state.Domain.ValueString())) + } + + tflog.Debug(ctx, fmt.Sprintf("%s: Beginning Read", state.Id.String())) + + urlPath := state.getPath() + "/" + url.QueryEscape(state.Id.ValueString()) + res, err := r.client.Get(urlPath, reqMods...) + + if err != nil && strings.Contains(err.Error(), "StatusCode 404") { + resp.State.RemoveResource(ctx) + return + } else if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to retrieve object (GET), got error: %s, %s", err, res.String())) + return + } + + imp, diags := helpers.IsFlagImporting(ctx, req) + if resp.Diagnostics.Append(diags...); resp.Diagnostics.HasError() { + return + } + + // After `terraform import` we switch to a full read. + if imp { + state.fromBody(ctx, res) + } else { + state.fromBodyPartial(ctx, res) + } + + tflog.Debug(ctx, fmt.Sprintf("%s: Read finished successfully", state.Id.ValueString())) + + diags = resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) + + helpers.SetFlagImporting(ctx, false, resp.Private, &resp.Diagnostics) +} + +// End of section. //template:end read + +func (r *DeviceHAPairResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan, state DeviceHAPair + + // Read plan + diags := req.Plan.Get(ctx, &plan) + if resp.Diagnostics.Append(diags...); resp.Diagnostics.HasError() { + return + } + + // Read state + diags = req.State.Get(ctx, &state) + if resp.Diagnostics.Append(diags...); resp.Diagnostics.HasError() { + return + } + + // Set request domain if provided + reqMods := [](func(*fmc.Req)){} + if !plan.Domain.IsNull() && plan.Domain.ValueString() != "" { + reqMods = append(reqMods, fmc.DomainName(plan.Domain.ValueString())) + } + + tflog.Debug(ctx, fmt.Sprintf("%s: Beginning Update", plan.Id.ValueString())) + + body := plan.toBodyPutUpdate(ctx, state) + res, err := r.client.Put(plan.getPath()+"/"+url.QueryEscape(plan.Id.ValueString()), body, reqMods...) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to configure object (PUT), got error: %s, %s", err, res.String())) + return + } + + tflog.Debug(ctx, fmt.Sprintf("%s: Update finished successfully", plan.Id.ValueString())) + + diags = resp.State.Set(ctx, &plan) + resp.Diagnostics.Append(diags...) +} + +func (r *DeviceHAPairResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state DeviceHAPair + + // Read state + diags := req.State.Get(ctx, &state) + if resp.Diagnostics.Append(diags...); resp.Diagnostics.HasError() { + return + } + + // Set request domain if provided + reqMods := [](func(*fmc.Req)){} + if !state.Domain.IsNull() && state.Domain.ValueString() != "" { + reqMods = append(reqMods, fmc.DomainName(state.Domain.ValueString())) + } + // Start of HA Break code + body := state.toBodyPutDelete(ctx, DeviceHAPair{}) + res, err := r.client.Put(state.getPath()+"/"+url.QueryEscape(state.Id.ValueString()), body, reqMods...) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to remove object configuration phase 1 (PUT), got error: %s, %s", err, res.String())) + return + } + // End of HA Break code + + // Adding code to poll object + taskID := res.Get("metadata.task.id").String() + tflog.Debug(ctx, fmt.Sprintf("%s: Async task initiated successfully", taskID)) + + const atom time.Duration = 5 * time.Second + // We need device's UUID, but it only shows after the task succeeds. Poll the task. + for i := time.Duration(0); i < 5*time.Minute; i += atom { + task, err := r.client.Get("/api/fmc_config/v1/domain/{DOMAIN_UUID}/job/taskstatuses/"+url.QueryEscape(taskID), reqMods...) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Failed to read object (GET), got error: %s, %s", err, task.String())) + return + } + stat := strings.ToUpper(task.Get("status").String()) + if stat == "FAILED" { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("API task for the new device failed: %s, %s", task.Get("message"), task.Get("description"))) + return + } + if stat != "PENDING" && stat != "RUNNING" && stat != "IN_PROGRESS" { + break + } + time.Sleep(atom) + } + tflog.Debug(ctx, fmt.Sprintf("%s: Delete finished successfully", state.Id.ValueString())) + + resp.State.RemoveResource(ctx) +} + +// Section below is generated&owned by "gen/generator.go". //template:begin import + +func (r *DeviceHAPairResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) + + helpers.SetFlagImporting(ctx, true, resp.Private, &resp.Diagnostics) +} + +// End of section. //template:end import + +// Section below is generated&owned by "gen/generator.go". //template:begin createSubresources + +// End of section. //template:end createSubresources + +// Section below is generated&owned by "gen/generator.go". //template:begin deleteSubresources + +// End of section. //template:end deleteSubresources + +// Section below is generated&owned by "gen/generator.go". //template:begin updateSubresources + +// End of section. //template:end updateSubresources diff --git a/internal/provider/resource_fmc_device_ha_pair_test.go b/internal/provider/resource_fmc_device_ha_pair_test.go new file mode 100644 index 00000000..11864b3e --- /dev/null +++ b/internal/provider/resource_fmc_device_ha_pair_test.go @@ -0,0 +1,137 @@ +// Copyright © 2023 Cisco Systems, Inc. and its affiliates. +// All rights reserved. +// +// Licensed under the Mozilla Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://mozilla.org/MPL/2.0/ +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: MPL-2.0 + +package provider + +// Section below is generated&owned by "gen/generator.go". //template:begin imports +import ( + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +// End of section. //template:end imports + +// Section below is generated&owned by "gen/generator.go". //template:begin testAcc + +func TestAccFmcDeviceHAPair(t *testing.T) { + if os.Getenv("TF_VAR_device_id") == "" && os.Getenv("TF_VAR_device_2_id") == "" { + t.Skip("skipping test, set environment variable TF_VAR_device_id or TF_VAR_device_2_id") + } + var checks []resource.TestCheckFunc + checks = append(checks, resource.TestCheckResourceAttr("fmc_device_ha_pair.test", "name", "FTD_HA")) + checks = append(checks, resource.TestCheckResourceAttr("fmc_device_ha_pair.test", "is_encryption_enabled", "false")) + checks = append(checks, resource.TestCheckResourceAttr("fmc_device_ha_pair.test", "shared_key", "cisco123")) + checks = append(checks, resource.TestCheckResourceAttr("fmc_device_ha_pair.test", "lan_failover_standby_ip", "1.1.1.2")) + checks = append(checks, resource.TestCheckResourceAttr("fmc_device_ha_pair.test", "lan_failover_active_ip", "1.1.1.1")) + checks = append(checks, resource.TestCheckResourceAttr("fmc_device_ha_pair.test", "lan_failover_name", "LAN-INTERFACE")) + checks = append(checks, resource.TestCheckResourceAttr("fmc_device_ha_pair.test", "lan_failover_netmask", "255.255.255.0")) + checks = append(checks, resource.TestCheckResourceAttr("fmc_device_ha_pair.test", "lan_failover_ipv6_addr", "false")) + checks = append(checks, resource.TestCheckResourceAttr("fmc_device_ha_pair.test", "lan_failover_interface_name", "GigabitEthernet0/0")) + checks = append(checks, resource.TestCheckResourceAttr("fmc_device_ha_pair.test", "stateful_failover_standby_ip", "10.10.10.2")) + checks = append(checks, resource.TestCheckResourceAttr("fmc_device_ha_pair.test", "stateful_failover_active_ip", "10.10.10.1")) + checks = append(checks, resource.TestCheckResourceAttr("fmc_device_ha_pair.test", "stateful_failover_name", "Stateful-INTERFACE")) + checks = append(checks, resource.TestCheckResourceAttr("fmc_device_ha_pair.test", "stateful_failover_subnet_mask", "255.255.255.0")) + checks = append(checks, resource.TestCheckResourceAttr("fmc_device_ha_pair.test", "stateful_failover_ipv6_addr", "false")) + checks = append(checks, resource.TestCheckResourceAttr("fmc_device_ha_pair.test", "stateful_failover_interface_name", "GigabitEthernet0/0")) + checks = append(checks, resource.TestCheckResourceAttr("fmc_device_ha_pair.test", "stateful_failover_interface_id", "76d24097-hj7r-7786-a4d0-a8c07ac08470")) + checks = append(checks, resource.TestCheckResourceAttr("fmc_device_ha_pair.test", "stateful_failover_interface_type", "PhysicalInterface")) + + var steps []resource.TestStep + if os.Getenv("SKIP_MINIMUM_TEST") == "" { + steps = append(steps, resource.TestStep{ + Config: testAccFmcDeviceHAPairPrerequisitesConfig + testAccFmcDeviceHAPairConfig_minimum(), + }) + } + steps = append(steps, resource.TestStep{ + Config: testAccFmcDeviceHAPairPrerequisitesConfig + testAccFmcDeviceHAPairConfig_all(), + Check: resource.ComposeTestCheckFunc(checks...), + }) + steps = append(steps, resource.TestStep{ + ResourceName: "fmc_device_ha_pair.test", + ImportState: true, + }) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: steps, + }) +} + +// End of section. //template:end testAcc + +// Section below is generated&owned by "gen/generator.go". //template:begin testPrerequisites + +const testAccFmcDeviceHAPairPrerequisitesConfig = ` +variable "device_id" { default = null } // tests will set $TF_VAR_device_id +` + +// End of section. //template:end testPrerequisites + +// Section below is generated&owned by "gen/generator.go". //template:begin testAccConfigMinimal + +func testAccFmcDeviceHAPairConfig_minimum() string { + config := `resource "fmc_device_ha_pair" "test" {` + "\n" + config += ` name = "FTD_HA"` + "\n" + config += ` primary_device_id = var.device_id` + "\n" + config += ` secondary_device_id = var.device_2_id` + "\n" + config += ` use_same_link_for_failovers = false` + "\n" + config += ` lan_failover_standby_ip = "1.1.1.2"` + "\n" + config += ` lan_failover_active_ip = "1.1.1.1"` + "\n" + config += ` lan_failover_name = "LAN-INTERFACE"` + "\n" + config += ` lan_failover_interface_id = "757kdgh5-41c4-4558-a4d0-a8c07ac08470"` + "\n" + config += ` lan_failover_interface_type = "PhysicalInterface"` + "\n" + config += `}` + "\n" + return config +} + +// End of section. //template:end testAccConfigMinimal + +// Section below is generated&owned by "gen/generator.go". //template:begin testAccConfigAll + +func testAccFmcDeviceHAPairConfig_all() string { + config := `resource "fmc_device_ha_pair" "test" {` + "\n" + config += ` name = "FTD_HA"` + "\n" + config += ` primary_device_id = var.device_id` + "\n" + config += ` secondary_device_id = var.device_2_id` + "\n" + config += ` is_encryption_enabled = false` + "\n" + config += ` use_same_link_for_failovers = false` + "\n" + config += ` shared_key = "cisco123"` + "\n" + config += ` enc_key_generation_scheme = "CUSTOM"` + "\n" + config += ` lan_failover_standby_ip = "1.1.1.2"` + "\n" + config += ` lan_failover_active_ip = "1.1.1.1"` + "\n" + config += ` lan_failover_name = "LAN-INTERFACE"` + "\n" + config += ` lan_failover_netmask = "255.255.255.0"` + "\n" + config += ` lan_failover_ipv6_addr = false` + "\n" + config += ` lan_failover_interface_name = "GigabitEthernet0/0"` + "\n" + config += ` lan_failover_interface_id = "757kdgh5-41c4-4558-a4d0-a8c07ac08470"` + "\n" + config += ` lan_failover_interface_type = "PhysicalInterface"` + "\n" + config += ` stateful_failover_standby_ip = "10.10.10.2"` + "\n" + config += ` stateful_failover_active_ip = "10.10.10.1"` + "\n" + config += ` stateful_failover_name = "Stateful-INTERFACE"` + "\n" + config += ` stateful_failover_subnet_mask = "255.255.255.0"` + "\n" + config += ` stateful_failover_ipv6_addr = false` + "\n" + config += ` stateful_failover_interface_name = "GigabitEthernet0/0"` + "\n" + config += ` stateful_failover_interface_id = "76d24097-hj7r-7786-a4d0-a8c07ac08470"` + "\n" + config += ` stateful_failover_interface_type = "PhysicalInterface"` + "\n" + config += `}` + "\n" + return config +} + +// End of section. //template:end testAccConfigAll