diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index b3e56e5..204873f 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -46,4 +46,4 @@ jobs:
args: release --clean
env:
GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}
- GITHUB_TOKEN: ${{ secrets.GH_PAT }}
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 88f9b4d..8bda3b4 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -44,6 +44,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
+ - uses: opentofu/setup-opentofu@v1
with:
go-version-file: "go.mod"
cache: true
diff --git a/.gitignore b/.gitignore
index 83a87b3..175effe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,6 +29,7 @@ website/node_modules
test/.terraform.lock.hcl
website/vendor
test/*.tfplan
+test/*tfplan
# Test exclusions
!command/test-fixtures/**/*.tfstate
diff --git a/docs/data-sources/available_ip_address.md b/docs/data-sources/available_ip_address.md
new file mode 100644
index 0000000..712e81c
--- /dev/null
+++ b/docs/data-sources/available_ip_address.md
@@ -0,0 +1,28 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "nautobot_available_ip_address Data Source - terraform-provider-nautobot"
+subcategory: ""
+description: |-
+ This data source retrieves an available IP address from a given prefix in Nautobot
+---
+
+# nautobot_available_ip_address (Data Source)
+
+This data source retrieves an available IP address from a given prefix in Nautobot
+
+
+
+
+## Schema
+
+### Required
+
+- `prefix_id` (String) The ID of the prefix from which to retrieve an available IP.
+
+### Read-Only
+
+- `address` (String) The available IP address.
+- `id` (String) The ID of this resource.
+- `ip_version` (Number) The version of the IP address (4 or 6).
+
+
diff --git a/docs/data-sources/cluster.md b/docs/data-sources/cluster.md
new file mode 100644
index 0000000..8eb3e6e
--- /dev/null
+++ b/docs/data-sources/cluster.md
@@ -0,0 +1,34 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "nautobot_cluster Data Source - terraform-provider-nautobot"
+subcategory: ""
+description: |-
+ Retrieves information about a specific cluster in Nautobot.
+---
+
+# nautobot_cluster (Data Source)
+
+Retrieves information about a specific cluster in Nautobot.
+
+
+
+
+## Schema
+
+### Required
+
+- `name` (String) The name of the cluster.
+
+### Read-Only
+
+- `cluster_group_id` (String) The ID of the cluster group.
+- `cluster_type_id` (String) The ID of the cluster type.
+- `comments` (String) Comments or notes about the cluster.
+- `created` (String) The creation date of the cluster.
+- `id` (String) The UUID of the cluster.
+- `last_updated` (String) The last update date of the cluster.
+- `location_id` (String) The ID of the location associated with the cluster.
+- `tags_ids` (List of String) The IDs of the tags associated with the cluster.
+- `tenant_id` (String) The ID of the tenant associated with the cluster.
+
+
diff --git a/docs/data-sources/cluster_type.md b/docs/data-sources/cluster_type.md
new file mode 100644
index 0000000..8b12080
--- /dev/null
+++ b/docs/data-sources/cluster_type.md
@@ -0,0 +1,34 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "nautobot_cluster_type Data Source - terraform-provider-nautobot"
+subcategory: ""
+description: |-
+ Retrieves information about a specific cluster type in Nautobot.
+---
+
+# nautobot_cluster_type (Data Source)
+
+Retrieves information about a specific cluster type in Nautobot.
+
+
+
+
+## Schema
+
+### Required
+
+- `name` (String) The name of the cluster type to retrieve.
+
+### Read-Only
+
+- `created` (String) The date the cluster type was created.
+- `description` (String) The description of the cluster type.
+- `display` (String) Human-friendly display value for the cluster type.
+- `id` (String) The UUID of the cluster type.
+- `last_updated` (String) The date the cluster type was last updated.
+- `natural_slug` (String) Natural slug for the cluster type.
+- `notes_url` (String) Notes URL for the cluster type.
+- `object_type` (String) Object type of the cluster type.
+- `url` (String) URL of the cluster type.
+
+
diff --git a/docs/data-sources/cluster_types.md b/docs/data-sources/cluster_types.md
new file mode 100644
index 0000000..c2efe7e
--- /dev/null
+++ b/docs/data-sources/cluster_types.md
@@ -0,0 +1,39 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "nautobot_cluster_types Data Source - terraform-provider-nautobot"
+subcategory: ""
+description: |-
+ Retrieves information about cluster types in Nautobot.
+---
+
+# nautobot_cluster_types (Data Source)
+
+Retrieves information about cluster types in Nautobot.
+
+
+
+
+## Schema
+
+### Read-Only
+
+- `cluster_types` (List of Object) (see [below for nested schema](#nestedatt--cluster_types))
+- `id` (String) The ID of this resource.
+
+
+### Nested Schema for `cluster_types`
+
+Read-Only:
+
+- `created` (String)
+- `description` (String)
+- `display` (String)
+- `id` (String)
+- `last_updated` (String)
+- `name` (String)
+- `natural_slug` (String)
+- `notes_url` (String)
+- `object_type` (String)
+- `url` (String)
+
+
diff --git a/docs/data-sources/clusters.md b/docs/data-sources/clusters.md
new file mode 100644
index 0000000..65eef22
--- /dev/null
+++ b/docs/data-sources/clusters.md
@@ -0,0 +1,39 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "nautobot_clusters Data Source - terraform-provider-nautobot"
+subcategory: ""
+description: |-
+ Retrieves information about clusters in Nautobot.
+---
+
+# nautobot_clusters (Data Source)
+
+Retrieves information about clusters in Nautobot.
+
+
+
+
+## Schema
+
+### Read-Only
+
+- `clusters` (List of Object) (see [below for nested schema](#nestedatt--clusters))
+- `id` (String) The ID of this resource.
+
+
+### Nested Schema for `clusters`
+
+Read-Only:
+
+- `cluster_group_id` (String)
+- `cluster_type_id` (String)
+- `comments` (String)
+- `created` (String)
+- `id` (String)
+- `last_updated` (String)
+- `location_id` (String)
+- `name` (String)
+- `tags_ids` (List of String)
+- `tenant_id` (String)
+
+
diff --git a/docs/data-sources/manufacturer.md b/docs/data-sources/manufacturer.md
new file mode 100644
index 0000000..18fee26
--- /dev/null
+++ b/docs/data-sources/manufacturer.md
@@ -0,0 +1,34 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "nautobot_manufacturer Data Source - terraform-provider-nautobot"
+subcategory: ""
+description: |-
+ Retrieves information about a specific manufacturer in Nautobot.
+---
+
+# nautobot_manufacturer (Data Source)
+
+Retrieves information about a specific manufacturer in Nautobot.
+
+
+
+
+## Schema
+
+### Required
+
+- `name` (String) The name of the manufacturer to retrieve.
+
+### Read-Only
+
+- `created` (String) Manufacturer's creation date.
+- `description` (String) Manufacturer's description.
+- `display` (String) Human friendly display value for the manufacturer.
+- `id` (String) Manufacturer's UUID.
+- `last_updated` (String) Manufacturer's last update.
+- `natural_slug` (String) Natural slug for the manufacturer.
+- `notes_url` (String) Notes URL for the manufacturer.
+- `object_type` (String) Object type of the manufacturer.
+- `url` (String) URL of the manufacturer.
+
+
diff --git a/docs/data-sources/manufacturers.md b/docs/data-sources/manufacturers.md
index 499a2ca..f8830e6 100644
--- a/docs/data-sources/manufacturers.md
+++ b/docs/data-sources/manufacturers.md
@@ -26,17 +26,14 @@ Manufacturer data source in the Terraform provider Nautobot.
Read-Only:
- `created` (String)
-- `custom_fields` (Map of String)
- `description` (String)
-- `devicetype_count` (Number)
- `display` (String)
- `id` (String)
-- `inventoryitem_count` (Number)
- `last_updated` (String)
- `name` (String)
+- `natural_slug` (String)
- `notes_url` (String)
-- `platform_count` (Number)
-- `slug` (String)
+- `object_type` (String)
- `url` (String)
diff --git a/docs/data-sources/prefix.md b/docs/data-sources/prefix.md
new file mode 100644
index 0000000..570cc82
--- /dev/null
+++ b/docs/data-sources/prefix.md
@@ -0,0 +1,35 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "nautobot_prefix Data Source - terraform-provider-nautobot"
+subcategory: ""
+description: |-
+ Retrieves information about a Prefix in Nautobot by its associated VLAN ID.
+---
+
+# nautobot_prefix (Data Source)
+
+Retrieves information about a Prefix in Nautobot by its associated VLAN ID.
+
+
+
+
+## Schema
+
+### Required
+
+- `vlan_id` (String) The UUID of the VLAN to retrieve the prefix for.
+
+### Read-Only
+
+- `created` (String) The creation date of the prefix.
+- `description` (String) Description of the prefix.
+- `id` (String) The UUID of the prefix.
+- `last_updated` (String) The last update date of the prefix.
+- `namespace_id` (String) The ID of the namespace associated with the prefix.
+- `prefix` (String) The prefix.
+- `rir_id` (String) The ID of the RIR associated with the prefix.
+- `role_id` (String) The ID of the role associated with the prefix.
+- `status` (String) The status of the prefix.
+- `tenant_id` (String) The ID of the tenant associated with the prefix.
+
+
diff --git a/docs/data-sources/prefixes.md b/docs/data-sources/prefixes.md
new file mode 100644
index 0000000..e096489
--- /dev/null
+++ b/docs/data-sources/prefixes.md
@@ -0,0 +1,39 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "nautobot_prefixes Data Source - terraform-provider-nautobot"
+subcategory: ""
+description: |-
+ Retrieves information about all prefixes in Nautobot.
+---
+
+# nautobot_prefixes (Data Source)
+
+Retrieves information about all prefixes in Nautobot.
+
+
+
+
+## Schema
+
+### Read-Only
+
+- `id` (String) The ID of this resource.
+- `prefixes` (List of Object) (see [below for nested schema](#nestedatt--prefixes))
+
+
+### Nested Schema for `prefixes`
+
+Read-Only:
+
+- `created` (String)
+- `description` (String)
+- `id` (String)
+- `last_updated` (String)
+- `namespace_id` (String)
+- `prefix` (String)
+- `rir_id` (String)
+- `role_id` (String)
+- `status` (String)
+- `tenant_id` (String)
+
+
diff --git a/docs/data-sources/virtual_machine.md b/docs/data-sources/virtual_machine.md
new file mode 100644
index 0000000..3ffce5c
--- /dev/null
+++ b/docs/data-sources/virtual_machine.md
@@ -0,0 +1,40 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "nautobot_virtual_machine Data Source - terraform-provider-nautobot"
+subcategory: ""
+description: |-
+ Retrieves information about a specific virtual machine in Nautobot.
+---
+
+# nautobot_virtual_machine (Data Source)
+
+Retrieves information about a specific virtual machine in Nautobot.
+
+
+
+
+## Schema
+
+### Required
+
+- `name` (String) The name of the virtual machine to retrieve.
+
+### Read-Only
+
+- `cluster_id` (String) The ID of the cluster associated with the virtual machine.
+- `comments` (String) Comments or notes about the virtual machine.
+- `created` (String) The creation date of the virtual machine.
+- `disk` (Number) The disk size in GB.
+- `id` (String) The UUID of the virtual machine.
+- `last_updated` (String) The last update date of the virtual machine.
+- `memory` (Number) The amount of memory in MB.
+- `platform_id` (String) The ID of the platform associated with the virtual machine.
+- `primary_ip4_id` (String) The ID of the primary IPv4 address.
+- `primary_ip6_id` (String) The ID of the primary IPv6 address.
+- `role_id` (String) The ID of the role associated with the virtual machine.
+- `status` (String) The name of the status of the virtual machine.
+- `tags_ids` (List of String) The IDs of the tags associated with the virtual machine.
+- `tenant_id` (String) The ID of the tenant associated with the virtual machine.
+- `vcpus` (Number) The number of virtual CPUs.
+
+
diff --git a/docs/data-sources/virtual_machines.md b/docs/data-sources/virtual_machines.md
new file mode 100644
index 0000000..d4e4efb
--- /dev/null
+++ b/docs/data-sources/virtual_machines.md
@@ -0,0 +1,45 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "nautobot_virtual_machines Data Source - terraform-provider-nautobot"
+subcategory: ""
+description: |-
+ Retrieves information about virtual machines in Nautobot.
+---
+
+# nautobot_virtual_machines (Data Source)
+
+Retrieves information about virtual machines in Nautobot.
+
+
+
+
+## Schema
+
+### Read-Only
+
+- `id` (String) The ID of this resource.
+- `virtual_machines` (List of Object) (see [below for nested schema](#nestedatt--virtual_machines))
+
+
+### Nested Schema for `virtual_machines`
+
+Read-Only:
+
+- `cluster_id` (String)
+- `comments` (String)
+- `created` (String)
+- `disk` (Number)
+- `id` (String)
+- `last_updated` (String)
+- `memory` (Number)
+- `name` (String)
+- `platform_id` (String)
+- `primary_ip4_id` (String)
+- `primary_ip6_id` (String)
+- `role_id` (String)
+- `status` (String)
+- `tags_ids` (List of String)
+- `tenant_id` (String)
+- `vcpus` (Number)
+
+
diff --git a/docs/data-sources/vlan.md b/docs/data-sources/vlan.md
new file mode 100644
index 0000000..a60b8bc
--- /dev/null
+++ b/docs/data-sources/vlan.md
@@ -0,0 +1,36 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "nautobot_vlan Data Source - terraform-provider-nautobot"
+subcategory: ""
+description: |-
+ Retrieves information about a specific VLAN in Nautobot.
+---
+
+# nautobot_vlan (Data Source)
+
+Retrieves information about a specific VLAN in Nautobot.
+
+
+
+
+## Schema
+
+### Required
+
+- `name` (String) The name of the VLAN to retrieve.
+
+### Read-Only
+
+- `created` (String) The creation date of the VLAN.
+- `description` (String) Description of the VLAN.
+- `id` (String) The UUID of the VLAN.
+- `last_updated` (String) The last update date of the VLAN.
+- `locations` (List of String) The IDs of the locations associated with the VLAN.
+- `role_id` (String) The ID of the role associated with the VLAN.
+- `status` (String) The status of the VLAN.
+- `tags_ids` (List of String) The IDs of the tags associated with the VLAN.
+- `tenant_id` (String) The ID of the tenant associated with the VLAN.
+- `vid` (Number) The ID (VID) of the VLAN.
+- `vlan_group_id` (String) The ID of the VLAN group.
+
+
diff --git a/docs/data-sources/vlans.md b/docs/data-sources/vlans.md
new file mode 100644
index 0000000..f372336
--- /dev/null
+++ b/docs/data-sources/vlans.md
@@ -0,0 +1,41 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "nautobot_vlans Data Source - terraform-provider-nautobot"
+subcategory: ""
+description: |-
+ Retrieves information about all VLANs in Nautobot.
+---
+
+# nautobot_vlans (Data Source)
+
+Retrieves information about all VLANs in Nautobot.
+
+
+
+
+## Schema
+
+### Read-Only
+
+- `id` (String) The ID of this resource.
+- `vlans` (List of Object) (see [below for nested schema](#nestedatt--vlans))
+
+
+### Nested Schema for `vlans`
+
+Read-Only:
+
+- `created` (String)
+- `description` (String)
+- `id` (String)
+- `last_updated` (String)
+- `locations` (List of String)
+- `name` (String)
+- `role_id` (String)
+- `status` (String)
+- `tags_ids` (List of String)
+- `tenant_id` (String)
+- `vid` (Number)
+- `vlan_group_id` (String)
+
+
diff --git a/docs/resources/available_ip_address.md b/docs/resources/available_ip_address.md
new file mode 100644
index 0000000..ea72f07
--- /dev/null
+++ b/docs/resources/available_ip_address.md
@@ -0,0 +1,33 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "nautobot_available_ip_address Resource - terraform-provider-nautobot"
+subcategory: ""
+description: |-
+ This object allocates and manages an available IP address in Nautobot
+---
+
+# nautobot_available_ip_address (Resource)
+
+This object allocates and manages an available IP address in Nautobot
+
+
+
+
+## Schema
+
+### Required
+
+- `prefix_id` (String) ID of the prefix to allocate the IP address from.
+- `status` (String) Status of the allocated IP address.
+
+### Optional
+
+- `dns_name` (String) DNS name associated with the IP address.
+
+### Read-Only
+
+- `address` (String) Allocated IP address.
+- `id` (String) The ID of this resource.
+- `ip_version` (Number) IP version of the allocated IP address (4 or 6).
+
+
diff --git a/docs/resources/cluster.md b/docs/resources/cluster.md
new file mode 100644
index 0000000..e4827c4
--- /dev/null
+++ b/docs/resources/cluster.md
@@ -0,0 +1,37 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "nautobot_cluster Resource - terraform-provider-nautobot"
+subcategory: ""
+description: |-
+ This object manages a cluster in Nautobot
+---
+
+# nautobot_cluster (Resource)
+
+This object manages a cluster in Nautobot
+
+
+
+
+## Schema
+
+### Required
+
+- `cluster_type_id` (String) ID of the Cluster's type. This can be sourced from the cluster_type resource or data source.
+- `name` (String) Cluster's name.
+
+### Optional
+
+- `cluster_group_id` (String) ID of the Cluster's group.
+- `comments` (String) Comments or notes about the cluster.
+- `location_id` (String) ID of the Location of the cluster.
+- `tags_ids` (List of String) IDs of the Tags associated with the cluster.
+- `tenant_id` (String) ID of the Tenant associated with the cluster.
+
+### Read-Only
+
+- `created` (String) Creation date of the cluster.
+- `id` (String) The ID of this resource.
+- `last_updated` (String) Last update date of the cluster.
+
+
diff --git a/docs/resources/cluster_type.md b/docs/resources/cluster_type.md
new file mode 100644
index 0000000..2e8df33
--- /dev/null
+++ b/docs/resources/cluster_type.md
@@ -0,0 +1,37 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "nautobot_cluster_type Resource - terraform-provider-nautobot"
+subcategory: ""
+description: |-
+ This object manages a cluster type in Nautobot.
+---
+
+# nautobot_cluster_type (Resource)
+
+This object manages a cluster type in Nautobot.
+
+
+
+
+## Schema
+
+### Required
+
+- `name` (String) Cluster type's name.
+
+### Optional
+
+- `description` (String) Description for the cluster type.
+
+### Read-Only
+
+- `created` (String) Creation date of the cluster type.
+- `display` (String) Human-friendly display value for the cluster type.
+- `id` (String) Cluster type's UUID.
+- `last_updated` (String) Last update date of the cluster type.
+- `natural_slug` (String) Natural slug for the cluster type.
+- `notes_url` (String) Notes URL for the cluster type.
+- `object_type` (String) Object type of the cluster type.
+- `url` (String) URL of the cluster type.
+
+
diff --git a/docs/resources/manufacturer.md b/docs/resources/manufacturer.md
index a5db2fd..dffa9fd 100644
--- a/docs/resources/manufacturer.md
+++ b/docs/resources/manufacturer.md
@@ -21,20 +21,17 @@ This object manages a manufacturer in Nautobot
### Optional
-- `custom_fields` (Map of String) Manufacturer custom fields.
- `description` (String) Manufacturer's description.
-- `display` (String) Manufacturer's display name.
-- `notes_url` (String) Notes for manufacturer.
-- `slug` (String) Manufacturer's slug.
-- `url` (String) Manufacturer's URL.
### Read-Only
- `created` (String) Manufacturer's creation date.
-- `devicetype_count` (Number) Manufacturer's device count.
+- `display` (String) Manufacturer's display name.
- `id` (String) Manufacturer's UUID.
-- `inventoryitem_count` (Number) Manufacturer's inventory item count.
-- `last_updated` (String) Manufacturer's last update.
-- `platform_count` (Number) Manufacturer's platform count.
+- `last_updated` (String) Manufacturer's last update date.
+- `natural_slug` (String) Natural slug for the manufacturer.
+- `notes_url` (String) Notes URL for the manufacturer.
+- `object_type` (String) Object type of the manufacturer.
+- `url` (String) Manufacturer's URL.
diff --git a/docs/resources/virtual_machine.md b/docs/resources/virtual_machine.md
new file mode 100644
index 0000000..2ef7ee2
--- /dev/null
+++ b/docs/resources/virtual_machine.md
@@ -0,0 +1,52 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "nautobot_virtual_machine Resource - terraform-provider-nautobot"
+subcategory: ""
+description: |-
+ This object manages a virtual machine in Nautobot
+---
+
+# nautobot_virtual_machine (Resource)
+
+This object manages a virtual machine in Nautobot
+
+
+
+
+## Schema
+
+### Required
+
+- `cluster_id` (String) Cluster where the virtual machine belongs.
+- `name` (String) Virtual Machine's name.
+- `status` (String) Status of the virtual machine.
+
+### Optional
+
+- `comments` (String) Comments or notes about the virtual machine.
+- `disk` (Number) Disk size in GB.
+- `memory` (Number) Amount of memory in MB.
+- `platform_id` (String) Platform or OS installed on the virtual machine.
+- `primary_ip4_id` (String) Primary IPv4 address.
+- `primary_ip6_id` (String) Primary IPv6 address.
+- `role_id` (String) Role of the virtual machine.
+- `software_image_files` (Block List) Software image files associated with the software version. (see [below for nested schema](#nestedblock--software_image_files))
+- `software_version_id` (String) Software version installed on the virtual machine.
+- `tags_ids` (List of String) Tags associated with the virtual machine.
+- `tenant_id` (String) Tenant associated with the virtual machine.
+- `vcpus` (Number) Number of virtual CPUs.
+
+### Read-Only
+
+- `created` (String) Creation date of the virtual machine.
+- `id` (String) The ID of this resource.
+- `last_updated` (String) Last update date of the virtual machine.
+
+
+### Nested Schema for `software_image_files`
+
+Read-Only:
+
+- `id` (String) The ID of this resource.
+
+
diff --git a/docs/resources/vm_interface.md b/docs/resources/vm_interface.md
new file mode 100644
index 0000000..3c8cef0
--- /dev/null
+++ b/docs/resources/vm_interface.md
@@ -0,0 +1,41 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "nautobot_vm_interface Resource - terraform-provider-nautobot"
+subcategory: ""
+description: |-
+ This object manages a VM Interface in Nautobot
+---
+
+# nautobot_vm_interface (Resource)
+
+This object manages a VM Interface in Nautobot
+
+
+
+
+## Schema
+
+### Required
+
+- `name` (String) Name of the VM interface.
+- `status` (String) Status of the VM interface.
+- `virtual_machine_id` (String) ID of the virtual machine to which the interface belongs.
+
+### Optional
+
+- `description` (String) Description of the interface.
+- `enabled` (Boolean) Whether the interface is enabled.
+- `ip_addresses` (List of String) List of IP addresses to assign to the VM interface.
+- `mac_address` (String) MAC address of the interface.
+- `mode` (String) Mode of the interface.
+- `mtu` (Number) MTU size of the interface.
+- `tags_ids` (List of String) Tags associated with the interface.
+- `untagged_vlan_id` (String) Untagged VLAN ID associated with the interface.
+
+### Read-Only
+
+- `created` (String) Creation date of the interface.
+- `id` (String) The ID of this resource.
+- `last_updated` (String) Last updated date of the interface.
+
+
diff --git a/docs/resources/vm_primary_ip.md b/docs/resources/vm_primary_ip.md
new file mode 100644
index 0000000..a095212
--- /dev/null
+++ b/docs/resources/vm_primary_ip.md
@@ -0,0 +1,31 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "nautobot_vm_primary_ip Resource - terraform-provider-nautobot"
+subcategory: ""
+description: |-
+ This resource sets an IP address as the primary IPv4 or IPv6 for a virtual machine in Nautobot
+---
+
+# nautobot_vm_primary_ip (Resource)
+
+This resource sets an IP address as the primary IPv4 or IPv6 for a virtual machine in Nautobot
+
+
+
+
+## Schema
+
+### Required
+
+- `virtual_machine_id` (String) ID of the virtual machine.
+
+### Optional
+
+- `primary_ip4_id` (String) ID of the primary IPv4 address.
+- `primary_ip6_id` (String) ID of the primary IPv6 address.
+
+### Read-Only
+
+- `id` (String) The ID of this resource.
+
+
diff --git a/go.mod b/go.mod
index 221dc8f..32f275c 100644
--- a/go.mod
+++ b/go.mod
@@ -1,16 +1,12 @@
module github.com/nautobot/terraform-provider-nautobot
-go 1.21
-
-toolchain go1.21.13
+go 1.23
require (
- github.com/deepmap/oapi-codegen v1.12.4
- github.com/google/uuid v1.3.0
github.com/hashicorp/terraform-plugin-docs v0.13.0
github.com/hashicorp/terraform-plugin-log v0.8.0
github.com/hashicorp/terraform-plugin-sdk/v2 v2.26.1
- github.com/nautobot/go-nautobot v1.5.8-beta
+ github.com/nautobot/go-nautobot/v2 v2.3.2-beta
github.com/tidwall/gjson v1.14.4
)
@@ -19,13 +15,13 @@ require (
github.com/Masterminds/semver/v3 v3.1.1 // indirect
github.com/Masterminds/sprig/v3 v3.2.2 // indirect
github.com/agext/levenshtein v1.2.3 // indirect
- github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/armon/go-radix v1.0.0 // indirect
github.com/bgentry/speakeasy v0.1.0 // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-cmp v0.5.9 // indirect
+ github.com/google/uuid v1.3.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-checkpoint v0.5.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
@@ -47,7 +43,7 @@ require (
github.com/huandu/xstrings v1.3.2 // indirect
github.com/imdario/mergo v0.3.13 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
- github.com/mattn/go-isatty v0.0.18 // indirect
+ github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mitchellh/cli v1.1.5 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
@@ -65,13 +61,14 @@ require (
github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect
github.com/vmihailenco/tagparser v0.1.2 // indirect
github.com/zclconf/go-cty v1.13.1 // indirect
- golang.org/x/crypto v0.7.0 // indirect
- golang.org/x/mod v0.9.0 // indirect
- golang.org/x/net v0.8.0 // indirect
- golang.org/x/sys v0.6.0 // indirect
- golang.org/x/text v0.8.0 // indirect
+ golang.org/x/crypto v0.13.0 // indirect
+ golang.org/x/mod v0.12.0 // indirect
+ golang.org/x/net v0.15.0 // indirect
+ golang.org/x/sys v0.12.0 // indirect
+ golang.org/x/text v0.13.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230323212658-478b75c54725 // indirect
google.golang.org/grpc v1.54.0 // indirect
- google.golang.org/protobuf v1.30.0 // indirect
+ google.golang.org/protobuf v1.31.0 // indirect
+ gopkg.in/validator.v2 v2.0.1 // indirect
)
diff --git a/go.sum b/go.sum
index 21b772c..5c4af6e 100644
--- a/go.sum
+++ b/go.sum
@@ -10,14 +10,11 @@ github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ=
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
-github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
-github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
-github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec=
github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
@@ -27,13 +24,10 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
-github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/deepmap/oapi-codegen v1.12.4 h1:pPmn6qI9MuOtCz82WY2Xaw46EQjgvxednXXrP7g5Q2s=
-github.com/deepmap/oapi-codegen v1.12.4/go.mod h1:3lgHGMu6myQ2vqbbTXH2H1o4eXFTGnFiDaOaKKl5yas=
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
@@ -67,8 +61,8 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
-github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
+github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -127,7 +121,6 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=
github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
-github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck=
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -151,8 +144,8 @@ github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
-github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
-github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
+github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mitchellh/cli v1.1.5 h1:OxRIeJXpAMztws/XHlN2vu6imG5Dpq+j61AzAX5fLng=
github.com/mitchellh/cli v1.1.5/go.mod h1:v8+iFts2sPIKUV1ltktPXMCC8fumSKFItNcD2cLtRR4=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
@@ -169,8 +162,8 @@ github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
-github.com/nautobot/go-nautobot v1.5.8-beta h1:bBLMfg1GA4ms2tRTFLcWz5Lhz+r1KTCGL+ohBPy6u6c=
-github.com/nautobot/go-nautobot v1.5.8-beta/go.mod h1:XeWVogQH4iHksB8xkL6x2r3FCAZglKtqRZatt3yJPGk=
+github.com/nautobot/go-nautobot/v2 v2.3.2-beta h1:w3sDcyt5CDSSWkKWUow08loxo6o72WjoDyclwkllotQ=
+github.com/nautobot/go-nautobot/v2 v2.3.2-beta/go.mod h1:T0emSo3w4TabGb4Wwb1o3NOEgbaVL1MLMDI9rFWaxtg=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
@@ -195,18 +188,16 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
-github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
-github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
-github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
@@ -235,12 +226,12 @@ golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
-golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
-golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
+golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
+golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
-golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
+golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -249,8 +240,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
-golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
-golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
+golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
+golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -274,8 +265,9 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
+golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
@@ -284,8 +276,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
-golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
+golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
@@ -301,14 +293,16 @@ google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag=
google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
-google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
+google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/validator.v2 v2.0.1 h1:xF0KWyGWXm/LM2G1TrEjqOu4pa6coO9AlWSf3msVfDY=
+gopkg.in/validator.v2 v2.0.1/go.mod h1:lIUZBlB3Im4s/eYp39Ry/wkR02yOPhZ9IwIRBjuPuG8=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/internal/provider/data_source_available_ip_address.go b/internal/provider/data_source_available_ip_address.go
new file mode 100644
index 0000000..1a8f821
--- /dev/null
+++ b/internal/provider/data_source_available_ip_address.go
@@ -0,0 +1,78 @@
+package provider
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ nb "github.com/nautobot/go-nautobot/v2"
+)
+
+func dataSourceAvailableIP() *schema.Resource {
+ return &schema.Resource{
+ Description: "This data source retrieves an available IP address from a given prefix in Nautobot",
+
+ ReadContext: dataSourceAvailableIPRead,
+
+ Schema: map[string]*schema.Schema{
+ "prefix_id": {
+ Description: "The ID of the prefix from which to retrieve an available IP.",
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "ip_version": {
+ Description: "The version of the IP address (4 or 6).",
+ Type: schema.TypeInt,
+ Computed: true,
+ },
+ "address": {
+ Description: "The available IP address.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ },
+ }
+}
+
+func dataSourceAvailableIPRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ c := meta.(*apiClient).Client
+ t := meta.(*apiClient).Token.token
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ prefixID := d.Get("prefix_id").(string)
+
+ // Fetch the available IPs from the given prefix
+ availableIPs, _, err := c.IpamAPI.IpamPrefixesAvailableIpsList(auth, prefixID).Execute()
+ if err != nil {
+ return diag.Errorf("failed to retrieve available IPs from prefix %s: %s", prefixID, err.Error())
+ }
+
+ // Check if there are available IPs
+ if len(availableIPs) == 0 {
+ return diag.Errorf("no available IP addresses found for prefix %s", prefixID)
+ }
+
+ // Use the first available IP from the list
+ availableIP := availableIPs[0]
+
+ // Set values in Terraform state
+ d.Set("ip_version", availableIP.IpVersion)
+ d.Set("address", availableIP.Address)
+
+ // Set resource ID to the available IP address
+ d.SetId(fmt.Sprintf("%s-%d", availableIP.Address, availableIP.IpVersion))
+
+ return nil
+}
diff --git a/internal/provider/data_source_cluster.go b/internal/provider/data_source_cluster.go
new file mode 100644
index 0000000..ecde8d8
--- /dev/null
+++ b/internal/provider/data_source_cluster.go
@@ -0,0 +1,166 @@
+package provider
+
+import (
+ "context"
+ "time"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ nb "github.com/nautobot/go-nautobot/v2"
+)
+
+func dataSourceCluster() *schema.Resource {
+ return &schema.Resource{
+ Description: "Retrieves information about a specific cluster in Nautobot.",
+
+ ReadContext: dataSourceClusterRead,
+
+ Schema: map[string]*schema.Schema{
+ "name": {
+ Description: "The name of the cluster.",
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "id": {
+ Description: "The UUID of the cluster.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "cluster_type_id": {
+ Description: "The ID of the cluster type.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "cluster_group_id": {
+ Description: "The ID of the cluster group.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "tenant_id": {
+ Description: "The ID of the tenant associated with the cluster.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "location_id": {
+ Description: "The ID of the location associated with the cluster.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "tags_ids": {
+ Description: "The IDs of the tags associated with the cluster.",
+ Type: schema.TypeList,
+ Computed: true,
+ Elem: &schema.Schema{
+ Type: schema.TypeString,
+ },
+ },
+ "comments": {
+ Description: "Comments or notes about the cluster.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "created": {
+ Description: "The creation date of the cluster.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "last_updated": {
+ Description: "The last update date of the cluster.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ },
+ }
+}
+
+func dataSourceClusterRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ var diags diag.Diagnostics
+
+ c := meta.(*apiClient).Client
+ t := meta.(*apiClient).Token.token
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ // Fetch the cluster name from Terraform configuration
+ clusterName := d.Get("name").(string)
+
+ // Fetch clusters by name
+ rsp, _, err := c.VirtualizationAPI.VirtualizationClustersList(auth).Name([]string{clusterName}).Execute()
+ if err != nil {
+ return diag.Errorf("failed to get cluster with name %s: %s", clusterName, err.Error())
+ }
+
+ // Ensure at least one result is returned
+ if len(rsp.Results) == 0 {
+ return diag.Errorf("no cluster found with name %s", clusterName)
+ }
+
+ cluster := rsp.Results[0]
+
+ d.SetId(cluster.Id)
+
+ // Set basic fields
+ d.Set("id", cluster.Id)
+ d.Set("name", cluster.Name)
+ d.Set("comments", cluster.Comments)
+
+ // Convert created and last updated fields to strings
+ createdStr := ""
+ if cluster.Created.IsSet() && cluster.Created.Get() != nil {
+ createdStr = cluster.Created.Get().Format(time.RFC3339)
+ }
+ d.Set("created", createdStr)
+
+ lastUpdatedStr := ""
+ if cluster.LastUpdated.IsSet() && cluster.LastUpdated.Get() != nil {
+ lastUpdatedStr = cluster.LastUpdated.Get().Format(time.RFC3339)
+ }
+ d.Set("last_updated", lastUpdatedStr)
+
+ // Handle cluster_type_id
+ if cluster.ClusterType.Id != nil && cluster.ClusterType.Id.String != nil {
+ d.Set("cluster_type_id", *cluster.ClusterType.Id.String)
+ }
+
+ // Handle cluster_group_id
+ if cluster.ClusterGroup.IsSet() {
+ if clusterGroup := cluster.ClusterGroup.Get(); clusterGroup != nil && clusterGroup.Id != nil {
+ d.Set("cluster_group_id", *clusterGroup.Id.String)
+ }
+ }
+
+ // Handle tenant_id
+ if cluster.Tenant.IsSet() {
+ if tenant := cluster.Tenant.Get(); tenant != nil && tenant.Id != nil {
+ d.Set("tenant_id", *tenant.Id.String)
+ }
+ }
+
+ // Handle location_id
+ if cluster.Location.IsSet() {
+ if location := cluster.Location.Get(); location != nil && location.Id != nil {
+ d.Set("location_id", *location.Id.String)
+ }
+ }
+
+ // Handle tags
+ var tags []string
+ for _, tag := range cluster.Tags {
+ if tag.Id != nil && tag.Id.String != nil {
+ tags = append(tags, *tag.Id.String)
+ }
+ }
+ d.Set("tags_ids", tags)
+
+ return diags
+}
diff --git a/internal/provider/data_source_cluster_type.go b/internal/provider/data_source_cluster_type.go
new file mode 100644
index 0000000..241bc25
--- /dev/null
+++ b/internal/provider/data_source_cluster_type.go
@@ -0,0 +1,131 @@
+package provider
+
+import (
+ "context"
+ "time"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ nb "github.com/nautobot/go-nautobot/v2"
+)
+
+func dataSourceClusterType() *schema.Resource {
+ return &schema.Resource{
+ Description: "Retrieves information about a specific cluster type in Nautobot.",
+
+ ReadContext: dataSourceClusterTypeRead,
+
+ Schema: map[string]*schema.Schema{
+ "name": {
+ Description: "The name of the cluster type to retrieve.",
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "id": {
+ Description: "The UUID of the cluster type.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "object_type": {
+ Description: "Object type of the cluster type.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "display": {
+ Description: "Human-friendly display value for the cluster type.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "url": {
+ Description: "URL of the cluster type.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "natural_slug": {
+ Description: "Natural slug for the cluster type.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "description": {
+ Description: "The description of the cluster type.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "created": {
+ Description: "The date the cluster type was created.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "last_updated": {
+ Description: "The date the cluster type was last updated.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "notes_url": {
+ Description: "Notes URL for the cluster type.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ },
+ }
+}
+
+func dataSourceClusterTypeRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ var diags diag.Diagnostics
+
+ c := meta.(*apiClient).Client
+ t := meta.(*apiClient).Token.token
+
+ // Get the cluster type name from the Terraform configuration
+ clusterTypeName := d.Get("name").(string)
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ // Fetch cluster types by name
+ rsp, _, err := c.VirtualizationAPI.VirtualizationClusterTypesList(auth).Name([]string{clusterTypeName}).Execute()
+ if err != nil {
+ return diag.Errorf("failed to get cluster types with name %s: %s", clusterTypeName, err.Error())
+ }
+
+ if len(rsp.Results) == 0 {
+ return diag.Errorf("no cluster type found with name %s", clusterTypeName)
+ }
+
+ clusterType := rsp.Results[0]
+
+ d.SetId(clusterType.Id)
+
+ createdStr := ""
+ if clusterType.Created.IsSet() && clusterType.Created.Get() != nil {
+ createdStr = clusterType.Created.Get().Format(time.RFC3339)
+ }
+
+ lastUpdatedStr := ""
+ if clusterType.LastUpdated.IsSet() && clusterType.LastUpdated.Get() != nil {
+ lastUpdatedStr = clusterType.LastUpdated.Get().Format(time.RFC3339)
+ }
+
+ // Set the fields directly in the resource data
+ d.Set("id", clusterType.Id)
+ d.Set("object_type", clusterType.ObjectType)
+ d.Set("display", clusterType.Display)
+ d.Set("url", clusterType.Url)
+ d.Set("natural_slug", clusterType.NaturalSlug)
+ d.Set("name", clusterType.Name)
+ d.Set("description", clusterType.Description)
+ d.Set("created", createdStr)
+ d.Set("last_updated", lastUpdatedStr)
+ d.Set("notes_url", clusterType.NotesUrl)
+
+ return diags
+}
diff --git a/internal/provider/data_source_cluster_types.go b/internal/provider/data_source_cluster_types.go
new file mode 100644
index 0000000..54eb4a7
--- /dev/null
+++ b/internal/provider/data_source_cluster_types.go
@@ -0,0 +1,144 @@
+package provider
+
+import (
+ "context"
+ "strconv"
+ "time"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ nb "github.com/nautobot/go-nautobot/v2"
+)
+
+func dataSourceClusterTypes() *schema.Resource {
+ return &schema.Resource{
+ Description: "Retrieves information about cluster types in Nautobot.",
+
+ ReadContext: dataSourceClusterTypesRead,
+
+ Schema: map[string]*schema.Schema{
+ "cluster_types": {
+ Type: schema.TypeList,
+ Computed: true,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "id": {
+ Description: "The UUID of the cluster type.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "object_type": {
+ Description: "Object type of the cluster type.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "display": {
+ Description: "Human-friendly display value for the cluster type.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "url": {
+ Description: "URL of the cluster type.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "natural_slug": {
+ Description: "Natural slug for the cluster type.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "name": {
+ Description: "The name of the cluster type.",
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "description": {
+ Description: "The description of the cluster type.",
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ "created": {
+ Description: "The date the cluster type was created.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "last_updated": {
+ Description: "The date the cluster type was last updated.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "notes_url": {
+ Description: "Notes URL for the cluster type.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ },
+ },
+ },
+ },
+ }
+}
+
+func dataSourceClusterTypesRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ var diags diag.Diagnostics
+
+ c := meta.(*apiClient).Client
+ s := meta.(*apiClient).Server
+ t := meta.(*apiClient).Token.token
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ rsp, _, err := c.VirtualizationAPI.VirtualizationClusterTypesList(auth).Execute()
+ if err != nil {
+ return diag.Errorf("failed to get cluster types list from %s: %s", s, err.Error())
+ }
+
+ results := rsp.Results
+ list := make([]map[string]interface{}, 0)
+
+ // Iterate over the results and map each cluster type to the format expected by Terraform
+ for _, clusterType := range results {
+ createdStr := ""
+ if clusterType.Created.IsSet() && clusterType.Created.Get() != nil {
+ createdStr = clusterType.Created.Get().Format(time.RFC3339)
+ }
+
+ lastUpdatedStr := ""
+ if clusterType.LastUpdated.IsSet() && clusterType.LastUpdated.Get() != nil {
+ lastUpdatedStr = clusterType.LastUpdated.Get().Format(time.RFC3339)
+ }
+
+ itemMap := map[string]interface{}{
+ "id": clusterType.Id,
+ "object_type": clusterType.ObjectType,
+ "display": clusterType.Display,
+ "url": clusterType.Url,
+ "natural_slug": clusterType.NaturalSlug,
+ "name": clusterType.Name,
+ "description": clusterType.Description,
+ "created": createdStr,
+ "last_updated": lastUpdatedStr,
+ "notes_url": clusterType.NotesUrl,
+ }
+ list = append(list, itemMap)
+ }
+
+ if err := d.Set("cluster_types", list); err != nil {
+ return diag.FromErr(err)
+ }
+
+ // Always run
+ d.SetId(strconv.FormatInt(time.Now().Unix(), 10))
+
+ return diags
+}
diff --git a/internal/provider/data_source_clusters.go b/internal/provider/data_source_clusters.go
new file mode 100644
index 0000000..7260800
--- /dev/null
+++ b/internal/provider/data_source_clusters.go
@@ -0,0 +1,182 @@
+package provider
+
+import (
+ "context"
+ "strconv"
+ "time"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ nb "github.com/nautobot/go-nautobot/v2"
+)
+
+func dataSourceClusters() *schema.Resource {
+ return &schema.Resource{
+ Description: "Retrieves information about clusters in Nautobot.",
+
+ ReadContext: dataSourceClustersRead,
+
+ Schema: map[string]*schema.Schema{
+ "clusters": {
+ Type: schema.TypeList,
+ Computed: true,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "id": {
+ Description: "The UUID of the cluster.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "name": {
+ Description: "The name of the cluster.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "cluster_type_id": {
+ Description: "The ID of the cluster type.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "cluster_group_id": {
+ Description: "The ID of the cluster group.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "tenant_id": {
+ Description: "The ID of the tenant associated with the cluster.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "location_id": {
+ Description: "The ID of the location associated with the cluster.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "tags_ids": {
+ Description: "The IDs of the tags associated with the cluster.",
+ Type: schema.TypeList,
+ Computed: true,
+ Elem: &schema.Schema{
+ Type: schema.TypeString,
+ },
+ },
+ "comments": {
+ Description: "Comments or notes about the cluster.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "created": {
+ Description: "The creation date of the cluster.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "last_updated": {
+ Description: "The last update date of the cluster.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ },
+ },
+ },
+ },
+ }
+}
+
+func dataSourceClustersRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ var diags diag.Diagnostics
+
+ c := meta.(*apiClient).Client
+ s := meta.(*apiClient).Server
+ t := meta.(*apiClient).Token.token
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ // Fetch clusters list
+ rsp, _, err := c.VirtualizationAPI.VirtualizationClustersList(auth).Execute()
+ if err != nil {
+ return diag.Errorf("failed to get clusters list from %s: %s", s, err.Error())
+ }
+
+ results := rsp.Results
+ list := make([]map[string]interface{}, 0)
+
+ // Iterate over the results and map each cluster to the format expected by Terraform
+ for _, cluster := range results {
+ createdStr := ""
+ if cluster.Created.IsSet() && cluster.Created.Get() != nil {
+ createdStr = cluster.Created.Get().Format(time.RFC3339)
+ }
+
+ lastUpdatedStr := ""
+ if cluster.LastUpdated.IsSet() && cluster.LastUpdated.Get() != nil {
+ lastUpdatedStr = cluster.LastUpdated.Get().Format(time.RFC3339)
+ }
+
+ // Prepare itemMap with mandatory fields
+ itemMap := map[string]interface{}{
+ "id": cluster.Id,
+ "name": cluster.Name,
+ "comments": cluster.Comments,
+ "created": createdStr,
+ "last_updated": lastUpdatedStr,
+ }
+
+ // Extract cluster_type_id safely
+ if cluster.ClusterType.Id != nil && cluster.ClusterType.Id.String != nil {
+ itemMap["cluster_type_id"] = *cluster.ClusterType.Id.String
+ }
+
+ // Handle nullable ClusterGroup
+ if cluster.ClusterGroup.IsSet() {
+ if clusterGroup := cluster.ClusterGroup.Get(); clusterGroup != nil && clusterGroup.Id != nil {
+ itemMap["cluster_group_id"] = *clusterGroup.Id.String
+ }
+ }
+
+ // Handle nullable Tenant
+ if cluster.Tenant.IsSet() {
+ if tenant := cluster.Tenant.Get(); tenant != nil && tenant.Id != nil {
+ itemMap["tenant_id"] = *tenant.Id.String
+ }
+ }
+
+ // Handle nullable Location
+ if cluster.Location.IsSet() {
+ if location := cluster.Location.Get(); location != nil && location.Id != nil {
+ itemMap["location_id"] = *location.Id.String
+ }
+ }
+
+ // Handle Tags
+ var tags []string
+ for _, tag := range cluster.Tags {
+ if tag.Id != nil && tag.Id.String != nil {
+ tags = append(tags, *tag.Id.String)
+ }
+ }
+ itemMap["tags_ids"] = tags
+
+ // Add the cluster to the list
+ list = append(list, itemMap)
+ }
+
+ // Set the clusters list in the resource data
+ if err := d.Set("clusters", list); err != nil {
+ return diag.FromErr(err)
+ }
+
+ // Always run
+ d.SetId(strconv.FormatInt(time.Now().Unix(), 10))
+
+ return diags
+}
diff --git a/internal/provider/data_source_graphql.go b/internal/provider/data_source_graphql.go
index b65d068..0e69a18 100644
--- a/internal/provider/data_source_graphql.go
+++ b/internal/provider/data_source_graphql.go
@@ -44,8 +44,8 @@ type reqBody struct {
func dataSourceGraphQLRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
var diags diag.Diagnostics
- c := meta.(*apiClient).BaseClient
- s := fmt.Sprintf("%sgraphql/", meta.(*apiClient).Server)
+ c := meta.(*apiClient).Client
+ s := fmt.Sprintf("%s/graphql/", meta.(*apiClient).Server)
t := meta.(*apiClient).Token
query := d.Get("query").(string)
@@ -58,7 +58,7 @@ func dataSourceGraphQLRead(ctx context.Context, d *schema.ResourceData, meta int
// Add the authorization header to our request.
t.Intercept(ctx, req)
- rsp, err := c.Client.Do(req)
+ rsp, err := c.GetConfig().HTTPClient.Do(req)
if err != nil {
return diag.Errorf("failed to successfully call %s: %s", s, err.Error())
}
diff --git a/internal/provider/data_source_manufacturer.go b/internal/provider/data_source_manufacturer.go
new file mode 100644
index 0000000..cd68eaa
--- /dev/null
+++ b/internal/provider/data_source_manufacturer.go
@@ -0,0 +1,131 @@
+package provider
+
+import (
+ "context"
+ "time"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ nb "github.com/nautobot/go-nautobot/v2"
+)
+
+func dataSourceManufacturer() *schema.Resource {
+ return &schema.Resource{
+ Description: "Retrieves information about a specific manufacturer in Nautobot.",
+
+ ReadContext: dataSourceManufacturerRead,
+
+ Schema: map[string]*schema.Schema{
+ "name": {
+ Description: "The name of the manufacturer to retrieve.",
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "id": {
+ Description: "Manufacturer's UUID.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "object_type": {
+ Description: "Object type of the manufacturer.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "display": {
+ Description: "Human friendly display value for the manufacturer.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "url": {
+ Description: "URL of the manufacturer.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "natural_slug": {
+ Description: "Natural slug for the manufacturer.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "description": {
+ Description: "Manufacturer's description.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "created": {
+ Description: "Manufacturer's creation date.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "last_updated": {
+ Description: "Manufacturer's last update.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "notes_url": {
+ Description: "Notes URL for the manufacturer.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ },
+ }
+}
+
+func dataSourceManufacturerRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ var diags diag.Diagnostics
+
+ c := meta.(*apiClient).Client
+ t := meta.(*apiClient).Token.token
+
+ // Get the manufacturer name from the Terraform configuration
+ manufacturerName := d.Get("name").(string)
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ // Fetch manufacturer by name
+ rsp, _, err := c.DcimAPI.DcimManufacturersList(auth).Name([]string{manufacturerName}).Execute()
+ if err != nil {
+ return diag.Errorf("failed to get manufacturer with name %s: %s", manufacturerName, err.Error())
+ }
+
+ if len(rsp.Results) == 0 {
+ return diag.Errorf("no manufacturer found with name %s", manufacturerName)
+ }
+
+ manufacturer := rsp.Results[0]
+
+ d.SetId(manufacturer.Id)
+
+ createdStr := ""
+ if manufacturer.Created.IsSet() && manufacturer.Created.Get() != nil {
+ createdStr = manufacturer.Created.Get().Format(time.RFC3339)
+ }
+
+ lastUpdatedStr := ""
+ if manufacturer.LastUpdated.IsSet() && manufacturer.LastUpdated.Get() != nil {
+ lastUpdatedStr = manufacturer.LastUpdated.Get().Format(time.RFC3339)
+ }
+
+ // Set the fields directly in the resource data
+ d.Set("id", manufacturer.Id)
+ d.Set("object_type", manufacturer.ObjectType)
+ d.Set("display", manufacturer.Display)
+ d.Set("url", manufacturer.Url)
+ d.Set("natural_slug", manufacturer.NaturalSlug)
+ d.Set("name", manufacturer.Name)
+ d.Set("description", manufacturer.Description)
+ d.Set("created", createdStr)
+ d.Set("last_updated", lastUpdatedStr)
+ d.Set("notes_url", manufacturer.NotesUrl)
+
+ return diags
+}
diff --git a/internal/provider/data_source_manufacturers.go b/internal/provider/data_source_manufacturers.go
index cd0e9a1..42e2799 100644
--- a/internal/provider/data_source_manufacturers.go
+++ b/internal/provider/data_source_manufacturers.go
@@ -2,16 +2,12 @@ package provider
import (
"context"
- "encoding/json"
"strconv"
- "strings"
"time"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
- "github.com/tidwall/gjson"
-
- nb "github.com/nautobot/go-nautobot/pkg/nautobot"
+ nb "github.com/nautobot/go-nautobot/v2"
)
func dataSourceManufacturers() *schema.Resource {
@@ -26,44 +22,28 @@ func dataSourceManufacturers() *schema.Resource {
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
- "created": {
- Description: "Manufacturer's creation date.",
+ "id": {
+ Description: "Manufacturer's UUID.",
Type: schema.TypeString,
Computed: true,
},
- "description": {
- Description: "Manufacturer's description.",
+ "object_type": {
+ Description: "Object type of the Manufacturer.",
Type: schema.TypeString,
- Optional: true,
- },
- "custom_fields": {
- Description: "Manufacturer custom fields.",
- Type: schema.TypeMap,
- Optional: true,
- },
- "devicetype_count": {
- Description: "Manufacturer's device count.",
- Type: schema.TypeInt,
Computed: true,
},
"display": {
- Description: "Manufacturer's display name.",
+ Description: "Human friendly display value for the Manufacturer.",
Type: schema.TypeString,
- Optional: true,
Computed: true,
},
- "id": {
- Description: "Manufacturer's UUID.",
+ "url": {
+ Description: "URL of the Manufacturer.",
Type: schema.TypeString,
Computed: true,
},
- "inventoryitem_count": {
- Description: "Manufacturer's inventory item count.",
- Type: schema.TypeInt,
- Computed: true,
- },
- "last_updated": {
- Description: "Manufacturer's last update.",
+ "natural_slug": {
+ Description: "Natural slug for the Manufacturer.",
Type: schema.TypeString,
Computed: true,
},
@@ -72,25 +52,23 @@ func dataSourceManufacturers() *schema.Resource {
Type: schema.TypeString,
Required: true,
},
- "notes_url": {
- Description: "Notes for manufacturer.",
+ "description": {
+ Description: "Manufacturer's description.",
Type: schema.TypeString,
Optional: true,
- Computed: true,
},
- "platform_count": {
- Description: "Manufacturer's platform count.",
- Type: schema.TypeInt,
+ "created": {
+ Description: "Manufacturer's creation date.",
+ Type: schema.TypeString,
Computed: true,
},
- "slug": {
- Description: "Manufacturer's slug.",
+ "last_updated": {
+ Description: "Manufacturer's last update.",
Type: schema.TypeString,
- Optional: true,
Computed: true,
},
- "url": {
- Description: "Manufacturer's URL.",
+ "notes_url": {
+ Description: "Notes URL for the Manufacturer.",
Type: schema.TypeString,
Optional: true,
Computed: true,
@@ -108,23 +86,51 @@ func dataSourceManufacturersRead(ctx context.Context, d *schema.ResourceData, me
c := meta.(*apiClient).Client
s := meta.(*apiClient).Server
-
- rsp, err := c.DcimManufacturersListWithResponse(
+ t := meta.(*apiClient).Token.token
+ auth := context.WithValue(
ctx,
- &nb.DcimManufacturersListParams{})
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+ rsp, _, err := c.DcimAPI.DcimManufacturersList(auth).Execute()
if err != nil {
return diag.Errorf("failed to get manufacturers list from %s: %s", s, err.Error())
}
- results := gjson.Get(string(rsp.Body), "results")
- resultsReader := strings.NewReader(results.String())
+ results := rsp.Results
list := make([]map[string]interface{}, 0)
- err = json.NewDecoder(resultsReader).Decode(&list)
- if err != nil {
- return diag.Errorf("failed to decode manufacturers list from %s: %s", s, err.Error())
+ // Iterate over the results and map each manufacturer to the format expected by Terraform
+ for _, manufacturer := range results {
+ createdStr := ""
+ if manufacturer.Created.IsSet() && manufacturer.Created.Get() != nil {
+ createdStr = manufacturer.Created.Get().Format(time.RFC3339)
+ }
+
+ lastUpdatedStr := ""
+ if manufacturer.LastUpdated.IsSet() && manufacturer.LastUpdated.Get() != nil {
+ lastUpdatedStr = manufacturer.LastUpdated.Get().Format(time.RFC3339)
+ }
+ itemMap := map[string]interface{}{
+ "id": manufacturer.Id,
+ "object_type": manufacturer.ObjectType,
+ "display": manufacturer.Display,
+ "url": manufacturer.Url,
+ "natural_slug": manufacturer.NaturalSlug,
+ "name": manufacturer.Name,
+ "description": manufacturer.Description,
+ "created": createdStr,
+ "last_updated": lastUpdatedStr,
+ "notes_url": manufacturer.NotesUrl,
+ }
+ list = append(list, itemMap)
}
if err := d.Set("manufacturers", list); err != nil {
diff --git a/internal/provider/data_source_prefix.go b/internal/provider/data_source_prefix.go
new file mode 100644
index 0000000..1470651
--- /dev/null
+++ b/internal/provider/data_source_prefix.go
@@ -0,0 +1,170 @@
+package provider
+
+import (
+ "context"
+ "time"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ nb "github.com/nautobot/go-nautobot/v2"
+)
+
+func dataSourcePrefix() *schema.Resource {
+ return &schema.Resource{
+ Description: "Retrieves information about a Prefix in Nautobot by its associated VLAN ID.",
+
+ ReadContext: dataSourcePrefixRead,
+
+ Schema: map[string]*schema.Schema{
+ "vlan_id": {
+ Description: "The UUID of the VLAN to retrieve the prefix for.",
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "id": {
+ Description: "The UUID of the prefix.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "prefix": {
+ Description: "The prefix.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "description": {
+ Description: "Description of the prefix.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "status": {
+ Description: "The status of the prefix.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "role_id": {
+ Description: "The ID of the role associated with the prefix.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "tenant_id": {
+ Description: "The ID of the tenant associated with the prefix.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "rir_id": {
+ Description: "The ID of the RIR associated with the prefix.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "namespace_id": {
+ Description: "The ID of the namespace associated with the prefix.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "created": {
+ Description: "The creation date of the prefix.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "last_updated": {
+ Description: "The last update date of the prefix.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ },
+ }
+}
+
+func dataSourcePrefixRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ var diags diag.Diagnostics
+
+ c := meta.(*apiClient).Client
+ t := meta.(*apiClient).Token.token
+
+ // Get the VLAN ID from the Terraform configuration
+ vlanID := d.Get("vlan_id").(string)
+
+ // Prepare the VLAN ID as a []*string slice
+ vlanIDList := []*string{&vlanID}
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ // Fetch prefixes by VLAN ID
+ rsp, _, err := c.IpamAPI.IpamPrefixesList(auth).VlanId(vlanIDList).Execute()
+ if err != nil {
+ return diag.Errorf("failed to get prefix for VLAN ID %s: %s", vlanID, err.Error())
+ }
+
+ if len(rsp.Results) == 0 {
+ return diag.Errorf("no prefix found for VLAN ID %s", vlanID)
+ }
+
+ prefix := rsp.Results[0]
+
+ d.SetId(prefix.Id)
+
+ createdStr := ""
+ if prefix.Created.IsSet() && prefix.Created.Get() != nil {
+ createdStr = prefix.Created.Get().Format(time.RFC3339)
+ }
+
+ lastUpdatedStr := ""
+ if prefix.LastUpdated.IsSet() && prefix.LastUpdated.Get() != nil {
+ lastUpdatedStr = prefix.LastUpdated.Get().Format(time.RFC3339)
+ }
+
+ // Set the fields directly in the resource data
+ d.Set("id", prefix.Id)
+ d.Set("prefix", prefix.Prefix)
+ d.Set("description", prefix.Description)
+ d.Set("created", createdStr)
+ d.Set("last_updated", lastUpdatedStr)
+
+ // Handle nullable status
+ if prefix.Status.Id != nil && prefix.Status.Id.String != nil {
+ statusID := *prefix.Status.Id.String
+ statusName, err := getStatusName(ctx, c, t, statusID)
+ if err != nil {
+ return diag.Errorf("failed to get status name for ID %s: %s", statusID, err.Error())
+ }
+ d.Set("status", statusName)
+ }
+
+ // Handle nullable Tenant
+ if prefix.Tenant.IsSet() {
+ if tenant := prefix.Tenant.Get(); tenant != nil && tenant.Id != nil && tenant.Id.String != nil {
+ d.Set("tenant_id", *tenant.Id.String)
+ }
+ }
+
+ // Handle nullable Role
+ if prefix.Role.IsSet() {
+ if role := prefix.Role.Get(); role != nil && role.Id != nil && role.Id.String != nil {
+ d.Set("role_id", *role.Id.String)
+ }
+ }
+
+ // Handle nullable RIR
+ if prefix.Rir.IsSet() {
+ if rir := prefix.Rir.Get(); rir != nil && rir.Id != nil && rir.Id.String != nil {
+ d.Set("rir_id", *rir.Id.String)
+ }
+ }
+
+ // Handle nullable Namespace (without using IsSet)
+ if prefix.Namespace != nil && prefix.Namespace.Id != nil && prefix.Namespace.Id.String != nil {
+ d.Set("namespace_id", *prefix.Namespace.Id.String)
+ }
+
+ return diags
+}
diff --git a/internal/provider/data_source_prefixes.go b/internal/provider/data_source_prefixes.go
new file mode 100644
index 0000000..4b719f0
--- /dev/null
+++ b/internal/provider/data_source_prefixes.go
@@ -0,0 +1,169 @@
+package provider
+
+import (
+ "context"
+ "strconv"
+ "time"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ nb "github.com/nautobot/go-nautobot/v2"
+)
+
+func dataSourcePrefixes() *schema.Resource {
+ return &schema.Resource{
+ Description: "Retrieves information about all prefixes in Nautobot.",
+
+ ReadContext: dataSourcePrefixesRead,
+
+ Schema: map[string]*schema.Schema{
+ "prefixes": {
+ Type: schema.TypeList,
+ Computed: true,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "id": {
+ Description: "The UUID of the prefix.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "prefix": {
+ Description: "The prefix.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "description": {
+ Description: "Description of the prefix.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "status": {
+ Description: "The status of the prefix.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "role_id": {
+ Description: "The ID of the role associated with the prefix.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "tenant_id": {
+ Description: "The ID of the tenant associated with the prefix.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "rir_id": {
+ Description: "The ID of the RIR associated with the prefix.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "namespace_id": {
+ Description: "The ID of the namespace associated with the prefix.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "created": {
+ Description: "The creation date of the prefix.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "last_updated": {
+ Description: "The last update date of the prefix.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ },
+ },
+ },
+ },
+ }
+}
+
+func dataSourcePrefixesRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ var diags diag.Diagnostics
+
+ c := meta.(*apiClient).Client
+ t := meta.(*apiClient).Token.token
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ rsp, _, err := c.IpamAPI.IpamPrefixesList(auth).Execute()
+ if err != nil {
+ return diag.Errorf("failed to get prefixes: %s", err.Error())
+ }
+
+ results := rsp.Results
+ list := make([]map[string]interface{}, 0)
+
+ for _, prefix := range results {
+ createdStr := ""
+ if prefix.Created.IsSet() && prefix.Created.Get() != nil {
+ createdStr = prefix.Created.Get().Format(time.RFC3339)
+ }
+
+ lastUpdatedStr := ""
+ if prefix.LastUpdated.IsSet() && prefix.LastUpdated.Get() != nil {
+ lastUpdatedStr = prefix.LastUpdated.Get().Format(time.RFC3339)
+ }
+
+ itemMap := map[string]interface{}{
+ "id": prefix.Id,
+ "prefix": prefix.Prefix,
+ "description": prefix.Description,
+ "created": createdStr,
+ "last_updated": lastUpdatedStr,
+ }
+
+ if prefix.Status.Id != nil && prefix.Status.Id.String != nil {
+ statusID := *prefix.Status.Id.String
+ statusName, err := getStatusName(ctx, c, t, statusID)
+ if err != nil {
+ return diag.Errorf("failed to get status name for ID %s: %s", statusID, err.Error())
+ }
+ itemMap["status"] = statusName
+ }
+
+ if prefix.Tenant.IsSet() {
+ if tenant := prefix.Tenant.Get(); tenant != nil && tenant.Id != nil && tenant.Id.String != nil {
+ itemMap["tenant_id"] = *tenant.Id.String
+ }
+ }
+
+ if prefix.Role.IsSet() {
+ if role := prefix.Role.Get(); role != nil && role.Id != nil && role.Id.String != nil {
+ itemMap["role_id"] = *role.Id.String
+ }
+ }
+
+ if prefix.Rir.IsSet() {
+ if rir := prefix.Rir.Get(); rir != nil && rir.Id != nil && rir.Id.String != nil {
+ itemMap["rir_id"] = *rir.Id.String
+ }
+ }
+
+ if prefix.Namespace != nil && prefix.Namespace.Id != nil && prefix.Namespace.Id.String != nil {
+ itemMap["namespace_id"] = *prefix.Namespace.Id.String
+ }
+
+ list = append(list, itemMap)
+ }
+
+ if err := d.Set("prefixes", list); err != nil {
+ return diag.FromErr(err)
+ }
+
+ // Set ID for the data source
+ d.SetId(strconv.FormatInt(time.Now().Unix(), 10))
+
+ return diags
+}
diff --git a/internal/provider/data_source_virtual_machine.go b/internal/provider/data_source_virtual_machine.go
new file mode 100644
index 0000000..29692a8
--- /dev/null
+++ b/internal/provider/data_source_virtual_machine.go
@@ -0,0 +1,212 @@
+package provider
+
+import (
+ "context"
+ "time"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ nb "github.com/nautobot/go-nautobot/v2"
+)
+
+func dataSourceVirtualMachine() *schema.Resource {
+ return &schema.Resource{
+ Description: "Retrieves information about a specific virtual machine in Nautobot.",
+
+ ReadContext: dataSourceVirtualMachineRead,
+
+ Schema: map[string]*schema.Schema{
+ "name": {
+ Description: "The name of the virtual machine to retrieve.",
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "id": {
+ Description: "The UUID of the virtual machine.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "cluster_id": {
+ Description: "The ID of the cluster associated with the virtual machine.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "status": {
+ Description: "The name of the status of the virtual machine.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "tenant_id": {
+ Description: "The ID of the tenant associated with the virtual machine.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "platform_id": {
+ Description: "The ID of the platform associated with the virtual machine.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "role_id": {
+ Description: "The ID of the role associated with the virtual machine.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "primary_ip4_id": {
+ Description: "The ID of the primary IPv4 address.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "primary_ip6_id": {
+ Description: "The ID of the primary IPv6 address.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "vcpus": {
+ Description: "The number of virtual CPUs.",
+ Type: schema.TypeInt,
+ Computed: true,
+ },
+ "memory": {
+ Description: "The amount of memory in MB.",
+ Type: schema.TypeInt,
+ Computed: true,
+ },
+ "disk": {
+ Description: "The disk size in GB.",
+ Type: schema.TypeInt,
+ Computed: true,
+ },
+ "comments": {
+ Description: "Comments or notes about the virtual machine.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "tags_ids": {
+ Description: "The IDs of the tags associated with the virtual machine.",
+ Type: schema.TypeList,
+ Computed: true,
+ Elem: &schema.Schema{
+ Type: schema.TypeString,
+ },
+ },
+ "created": {
+ Description: "The creation date of the virtual machine.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "last_updated": {
+ Description: "The last update date of the virtual machine.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ },
+ }
+}
+
+func dataSourceVirtualMachineRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ var diags diag.Diagnostics
+
+ c := meta.(*apiClient).Client
+ t := meta.(*apiClient).Token.token
+
+ // Get the virtual machine name from the Terraform configuration
+ vmName := d.Get("name").(string)
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ // Fetch virtual machine by name
+ rsp, _, err := c.VirtualizationAPI.VirtualizationVirtualMachinesList(auth).Name([]string{vmName}).Execute()
+ if err != nil {
+ return diag.Errorf("failed to get virtual machine with name %s: %s", vmName, err.Error())
+ }
+
+ if len(rsp.Results) == 0 {
+ return diag.Errorf("no virtual machine found with name %s", vmName)
+ }
+
+ vm := rsp.Results[0]
+
+ d.SetId(vm.Id)
+
+ createdStr := ""
+ if vm.Created.IsSet() && vm.Created.Get() != nil {
+ createdStr = vm.Created.Get().Format(time.RFC3339)
+ }
+
+ lastUpdatedStr := ""
+ if vm.LastUpdated.IsSet() && vm.LastUpdated.Get() != nil {
+ lastUpdatedStr = vm.LastUpdated.Get().Format(time.RFC3339)
+ }
+
+ // Set the fields directly in the resource data
+ d.Set("id", vm.Id)
+ d.Set("name", vm.Name)
+ d.Set("vcpus", vm.Vcpus.Get())
+ d.Set("memory", vm.Memory.Get())
+ d.Set("disk", vm.Disk.Get())
+ d.Set("comments", vm.Comments)
+ d.Set("created", createdStr)
+ d.Set("last_updated", lastUpdatedStr)
+ d.Set("tags_ids", vm.Tags)
+
+ // Extract additional fields
+ if vm.Cluster.Id != nil && vm.Cluster.Id.String != nil {
+ d.Set("cluster_id", *vm.Cluster.Id.String)
+ }
+
+ if vm.Status.Id != nil && vm.Status.Id.String != nil {
+ statusID := *vm.Status.Id.String
+ statusName, err := getStatusName(ctx, c, t, statusID)
+ if err != nil {
+ return diag.Errorf("failed to get status name for ID %s: %s", statusID, err.Error())
+ }
+ d.Set("status", statusName)
+ }
+
+ if vm.Tenant.IsSet() {
+ tenant := vm.Tenant.Get()
+ if tenant != nil && tenant.Id != nil {
+ d.Set("tenant_id", *tenant.Id.String)
+ }
+ }
+
+ if vm.Platform.IsSet() {
+ platform := vm.Platform.Get()
+ if platform != nil && platform.Id != nil {
+ d.Set("platform_id", *platform.Id.String)
+ }
+ }
+
+ if vm.Role.IsSet() {
+ role := vm.Role.Get()
+ if role != nil && role.Id != nil {
+ d.Set("role_id", *role.Id.String)
+ }
+ }
+
+ if vm.PrimaryIp4.IsSet() {
+ primaryIp4 := vm.PrimaryIp4.Get()
+ if primaryIp4 != nil && primaryIp4.Id != nil {
+ d.Set("primary_ip4_id", *primaryIp4.Id.String)
+ }
+ }
+
+ if vm.PrimaryIp6.IsSet() {
+ primaryIp6 := vm.PrimaryIp6.Get()
+ if primaryIp6 != nil && primaryIp6.Id != nil {
+ d.Set("primary_ip6_id", *primaryIp6.Id.String)
+ }
+ }
+
+ return diags
+}
diff --git a/internal/provider/data_source_virtual_machines.go b/internal/provider/data_source_virtual_machines.go
new file mode 100644
index 0000000..ebbbb1c
--- /dev/null
+++ b/internal/provider/data_source_virtual_machines.go
@@ -0,0 +1,227 @@
+package provider
+
+import (
+ "context"
+ "strconv"
+ "time"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ nb "github.com/nautobot/go-nautobot/v2"
+)
+
+func dataSourceVirtualMachines() *schema.Resource {
+ return &schema.Resource{
+ Description: "Retrieves information about virtual machines in Nautobot.",
+
+ ReadContext: dataSourceVirtualMachinesRead,
+
+ Schema: map[string]*schema.Schema{
+ "virtual_machines": {
+ Type: schema.TypeList,
+ Computed: true,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "id": {
+ Description: "The UUID of the virtual machine.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "name": {
+ Description: "The name of the virtual machine.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "cluster_id": {
+ Description: "The ID of the cluster associated with the virtual machine.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "status": {
+ Description: "The name of the status of the virtual machine.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "tenant_id": {
+ Description: "The ID of the tenant associated with the virtual machine.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "platform_id": {
+ Description: "The ID of the platform associated with the virtual machine.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "role_id": {
+ Description: "The ID of the role associated with the virtual machine.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "primary_ip4_id": {
+ Description: "The ID of the primary IPv4 address.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "primary_ip6_id": {
+ Description: "The ID of the primary IPv6 address.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "vcpus": {
+ Description: "The number of virtual CPUs.",
+ Type: schema.TypeInt,
+ Computed: true,
+ },
+ "memory": {
+ Description: "The amount of memory in MB.",
+ Type: schema.TypeInt,
+ Computed: true,
+ },
+ "disk": {
+ Description: "The disk size in GB.",
+ Type: schema.TypeInt,
+ Computed: true,
+ },
+ "comments": {
+ Description: "Comments or notes about the virtual machine.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "tags_ids": {
+ Description: "The IDs of the tags associated with the virtual machine.",
+ Type: schema.TypeList,
+ Computed: true,
+ Elem: &schema.Schema{
+ Type: schema.TypeString,
+ },
+ },
+ "created": {
+ Description: "The creation date of the virtual machine.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "last_updated": {
+ Description: "The last update date of the virtual machine.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ },
+ },
+ },
+ },
+ }
+}
+
+func dataSourceVirtualMachinesRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ var diags diag.Diagnostics
+
+ c := meta.(*apiClient).Client
+ s := meta.(*apiClient).Server
+ t := meta.(*apiClient).Token.token
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ // Fetch virtual machines list
+ rsp, _, err := c.VirtualizationAPI.VirtualizationVirtualMachinesList(auth).Execute()
+ if err != nil {
+ return diag.Errorf("failed to get virtual machines list from %s: %s", s, err.Error())
+ }
+
+ results := rsp.Results
+ list := make([]map[string]interface{}, 0)
+
+ for _, vm := range results {
+ createdStr := ""
+ if vm.Created.IsSet() && vm.Created.Get() != nil {
+ createdStr = vm.Created.Get().Format(time.RFC3339)
+ }
+
+ lastUpdatedStr := ""
+ if vm.LastUpdated.IsSet() && vm.LastUpdated.Get() != nil {
+ lastUpdatedStr = vm.LastUpdated.Get().Format(time.RFC3339)
+ }
+
+ itemMap := map[string]interface{}{
+ "id": vm.Id,
+ "name": vm.Name,
+ "vcpus": vm.Vcpus.Get(),
+ "memory": vm.Memory.Get(),
+ "disk": vm.Disk.Get(),
+ "comments": vm.Comments,
+ "created": createdStr,
+ "last_updated": lastUpdatedStr,
+ "tags_ids": vm.Tags,
+ }
+
+ // Extract cluster_id, status, and other fields
+ if vm.Cluster.Id != nil && vm.Cluster.Id.String != nil {
+ itemMap["cluster_id"] = *vm.Cluster.Id.String
+ }
+
+ if vm.Status.Id != nil && vm.Status.Id.String != nil {
+ statusID := *vm.Status.Id.String
+ statusName, err := getStatusName(ctx, c, t, statusID)
+ if err != nil {
+ return diag.Errorf("failed to get status name for ID %s: %s", statusID, err.Error())
+ }
+ itemMap["status"] = statusName
+ }
+
+ // Handle nullable fields (tenant, platform, role, etc.)
+ if vm.Tenant.IsSet() {
+ tenant := vm.Tenant.Get()
+ if tenant != nil && tenant.Id != nil {
+ itemMap["tenant_id"] = *tenant.Id.String
+ }
+ }
+
+ if vm.Platform.IsSet() {
+ platform := vm.Platform.Get()
+ if platform != nil && platform.Id != nil {
+ itemMap["platform_id"] = *platform.Id.String
+ }
+ }
+
+ if vm.Role.IsSet() {
+ role := vm.Role.Get()
+ if role != nil && role.Id != nil {
+ itemMap["role_id"] = *role.Id.String
+ }
+ }
+
+ if vm.PrimaryIp4.IsSet() {
+ primaryIp4 := vm.PrimaryIp4.Get()
+ if primaryIp4 != nil && primaryIp4.Id != nil {
+ itemMap["primary_ip4_id"] = *primaryIp4.Id.String
+ }
+ }
+
+ if vm.PrimaryIp6.IsSet() {
+ primaryIp6 := vm.PrimaryIp6.Get()
+ if primaryIp6 != nil && primaryIp6.Id != nil {
+ itemMap["primary_ip6_id"] = *primaryIp6.Id.String
+ }
+ }
+
+ list = append(list, itemMap)
+ }
+
+ if err := d.Set("virtual_machines", list); err != nil {
+ return diag.FromErr(err)
+ }
+
+ // Set ID for the data source
+ d.SetId(strconv.FormatInt(time.Now().Unix(), 10))
+
+ return diags
+}
diff --git a/internal/provider/data_source_vlan.go b/internal/provider/data_source_vlan.go
new file mode 100644
index 0000000..faf850b
--- /dev/null
+++ b/internal/provider/data_source_vlan.go
@@ -0,0 +1,191 @@
+package provider
+
+import (
+ "context"
+ "time"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ nb "github.com/nautobot/go-nautobot/v2"
+)
+
+func dataSourceVLAN() *schema.Resource {
+ return &schema.Resource{
+ Description: "Retrieves information about a specific VLAN in Nautobot.",
+
+ ReadContext: dataSourceVLANRead,
+
+ Schema: map[string]*schema.Schema{
+ "name": {
+ Description: "The name of the VLAN to retrieve.",
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "id": {
+ Description: "The UUID of the VLAN.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "vid": {
+ Description: "The ID (VID) of the VLAN.",
+ Type: schema.TypeInt,
+ Computed: true,
+ },
+ "description": {
+ Description: "Description of the VLAN.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "vlan_group_id": {
+ Description: "The ID of the VLAN group.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "status": {
+ Description: "The status of the VLAN.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "tenant_id": {
+ Description: "The ID of the tenant associated with the VLAN.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "role_id": {
+ Description: "The ID of the role associated with the VLAN.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "locations": {
+ Description: "The IDs of the locations associated with the VLAN.",
+ Type: schema.TypeList,
+ Computed: true,
+ Elem: &schema.Schema{
+ Type: schema.TypeString,
+ },
+ },
+ "tags_ids": {
+ Description: "The IDs of the tags associated with the VLAN.",
+ Type: schema.TypeList,
+ Computed: true,
+ Elem: &schema.Schema{
+ Type: schema.TypeString,
+ },
+ },
+ "created": {
+ Description: "The creation date of the VLAN.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "last_updated": {
+ Description: "The last update date of the VLAN.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ },
+ }
+}
+
+func dataSourceVLANRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ var diags diag.Diagnostics
+
+ c := meta.(*apiClient).Client
+ t := meta.(*apiClient).Token.token
+
+ // Get the VLAN name from the Terraform configuration
+ vlanName := d.Get("name").(string)
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ // Fetch VLAN by name
+ rsp, _, err := c.IpamAPI.IpamVlansList(auth).Name([]string{vlanName}).Execute()
+ if err != nil {
+ return diag.Errorf("failed to get VLAN with name %s: %s", vlanName, err.Error())
+ }
+
+ if len(rsp.Results) == 0 {
+ return diag.Errorf("no VLAN found with name %s", vlanName)
+ }
+
+ vlan := rsp.Results[0]
+
+ d.SetId(vlan.Id)
+
+ createdStr := ""
+ if vlan.Created.IsSet() && vlan.Created.Get() != nil {
+ createdStr = vlan.Created.Get().Format(time.RFC3339)
+ }
+
+ lastUpdatedStr := ""
+ if vlan.LastUpdated.IsSet() && vlan.LastUpdated.Get() != nil {
+ lastUpdatedStr = vlan.LastUpdated.Get().Format(time.RFC3339)
+ }
+
+ // Set the fields directly in the resource data
+ d.Set("id", vlan.Id)
+ d.Set("vid", vlan.Vid)
+ d.Set("name", vlan.Name)
+ d.Set("description", vlan.Description)
+ d.Set("created", createdStr)
+ d.Set("last_updated", lastUpdatedStr)
+
+ // Handle nullable VlanGroup
+ if vlan.VlanGroup.IsSet() {
+ if vlanGroup := vlan.VlanGroup.Get(); vlanGroup != nil && vlanGroup.Id != nil {
+ d.Set("vlan_group_id", *vlanGroup.Id)
+ }
+ }
+
+ if vlan.Status.Id != nil && vlan.Status.Id.String != nil {
+ statusID := *vlan.Status.Id.String
+ statusName, err := getStatusName(ctx, c, t, statusID)
+ if err != nil {
+ return diag.Errorf("failed to get status name for ID %s: %s", statusID, err.Error())
+ }
+ d.Set("status", statusName)
+ }
+
+ // Handle nullable Tenant
+ if vlan.Tenant.IsSet() {
+ if tenant := vlan.Tenant.Get(); tenant != nil && tenant.Id != nil && tenant.Id.String != nil {
+ d.Set("tenant_id", *tenant.Id.String)
+ }
+ }
+
+ // Handle nullable Role
+ if vlan.Role.IsSet() {
+ if role := vlan.Role.Get(); role != nil && role.Id != nil && role.Id.String != nil {
+ d.Set("role_id", *role.Id.String)
+ }
+ }
+
+ // Handle locations
+ var locations []string
+ for _, location := range vlan.Locations {
+ if location.Id != nil && location.Id.String != nil {
+ locations = append(locations, *location.Id.String)
+ }
+ }
+ d.Set("locations", locations)
+
+ // Handle Tags
+ var tags []string
+ for _, tag := range vlan.Tags {
+ if tag.Id != nil && tag.Id.String != nil {
+ tags = append(tags, *tag.Id.String)
+ }
+ }
+ d.Set("tags_ids", tags)
+
+ return diags
+}
diff --git a/internal/provider/data_source_vlans.go b/internal/provider/data_source_vlans.go
new file mode 100644
index 0000000..1bd2fa3
--- /dev/null
+++ b/internal/provider/data_source_vlans.go
@@ -0,0 +1,209 @@
+package provider
+
+import (
+ "context"
+ "strconv"
+ "time"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ nb "github.com/nautobot/go-nautobot/v2"
+)
+
+func dataSourceVLANs() *schema.Resource {
+ return &schema.Resource{
+ Description: "Retrieves information about all VLANs in Nautobot.",
+
+ ReadContext: dataSourceVLANsRead,
+
+ Schema: map[string]*schema.Schema{
+ "vlans": {
+ Type: schema.TypeList,
+ Computed: true,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "id": {
+ Description: "The UUID of the VLAN.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "vid": {
+ Description: "The ID (VID) of the VLAN.",
+ Type: schema.TypeInt,
+ Computed: true,
+ },
+ "name": {
+ Description: "The name of the VLAN.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "description": {
+ Description: "Description of the VLAN.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "vlan_group_id": {
+ Description: "The ID of the VLAN group.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "status": {
+ Description: "The status of the VLAN.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "tenant_id": {
+ Description: "The ID of the tenant associated with the VLAN.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "role_id": {
+ Description: "The ID of the role associated with the VLAN.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "locations": {
+ Description: "The IDs of the locations associated with the VLAN.",
+ Type: schema.TypeList,
+ Computed: true,
+ Elem: &schema.Schema{
+ Type: schema.TypeString,
+ },
+ },
+ "tags_ids": {
+ Description: "The IDs of the tags associated with the VLAN.",
+ Type: schema.TypeList,
+ Computed: true,
+ Elem: &schema.Schema{
+ Type: schema.TypeString,
+ },
+ },
+ "created": {
+ Description: "The creation date of the VLAN.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "last_updated": {
+ Description: "The last update date of the VLAN.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ },
+ },
+ },
+ },
+ }
+}
+
+func dataSourceVLANsRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ var diags diag.Diagnostics
+
+ c := meta.(*apiClient).Client
+ t := meta.(*apiClient).Token.token
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ // Fetch VLANs list
+ rsp, _, err := c.IpamAPI.IpamVlansList(auth).Execute()
+ if err != nil {
+ return diag.Errorf("failed to get VLANs list: %s", err.Error())
+ }
+
+ results := rsp.Results
+ list := make([]map[string]interface{}, 0)
+
+ // Iterate over the results and map each VLAN to the format expected by Terraform
+ for _, vlan := range results {
+ createdStr := ""
+ if vlan.Created.IsSet() && vlan.Created.Get() != nil {
+ createdStr = vlan.Created.Get().Format(time.RFC3339)
+ }
+
+ lastUpdatedStr := ""
+ if vlan.LastUpdated.IsSet() && vlan.LastUpdated.Get() != nil {
+ lastUpdatedStr = vlan.LastUpdated.Get().Format(time.RFC3339)
+ }
+
+ // Prepare itemMap with mandatory fields
+ itemMap := map[string]interface{}{
+ "id": vlan.Id,
+ "vid": vlan.Vid,
+ "name": vlan.Name,
+ "description": vlan.Description,
+ "created": createdStr,
+ "last_updated": lastUpdatedStr,
+ }
+
+ // Handle nullable VlanGroup
+ if vlan.VlanGroup.IsSet() {
+ if vlanGroup := vlan.VlanGroup.Get(); vlanGroup != nil && vlanGroup.Id != nil {
+ itemMap["vlan_group_id"] = *vlanGroup.Id
+ }
+ }
+
+ // Fetch status name from the status ID
+ if vlan.Status.Id != nil && vlan.Status.Id.String != nil {
+ statusID := *vlan.Status.Id.String
+ statusName, err := getStatusName(ctx, c, t, statusID)
+ if err != nil {
+ return diag.Errorf("failed to get status name for ID %s: %s", statusID, err.Error())
+ }
+ itemMap["status"] = statusName
+ }
+
+ // Handle nullable Tenant
+ if vlan.Tenant.IsSet() {
+ if tenant := vlan.Tenant.Get(); tenant != nil && tenant.Id != nil && tenant.Id.String != nil {
+ itemMap["tenant_id"] = *tenant.Id.String
+ }
+ }
+
+ // Handle nullable Role
+ if vlan.Role.IsSet() {
+ if role := vlan.Role.Get(); role != nil && role.Id != nil && role.Id.String != nil {
+ itemMap["role_id"] = *role.Id.String
+ }
+ }
+
+ // Handle locations
+ var locations []string
+ for _, location := range vlan.Locations {
+ if location.Id != nil && location.Id.String != nil {
+ locations = append(locations, *location.Id.String)
+ }
+ }
+ itemMap["locations"] = locations
+
+ // Handle Tags
+ var tags []string
+ for _, tag := range vlan.Tags {
+ if tag.Id != nil && tag.Id.String != nil {
+ tags = append(tags, *tag.Id.String)
+ }
+ }
+ itemMap["tags_ids"] = tags
+
+ // Add the VLAN to the list
+ list = append(list, itemMap)
+ }
+
+ // Set the VLANs list in the resource data
+ if err := d.Set("vlans", list); err != nil {
+ return diag.FromErr(err)
+ }
+
+ // Always run
+ d.SetId(strconv.FormatInt(time.Now().Unix(), 10))
+
+ return diags
+}
diff --git a/internal/provider/patch.go b/internal/provider/patch.go
index 4225732..64586d2 100644
--- a/internal/provider/patch.go
+++ b/internal/provider/patch.go
@@ -4,10 +4,8 @@ import (
"context"
"fmt"
"net/http"
- "time"
- "github.com/deepmap/oapi-codegen/pkg/types"
- nb "github.com/nautobot/go-nautobot/pkg/nautobot"
+ nb "github.com/nautobot/go-nautobot/v2"
)
func NewSecurityProviderNautobotToken(t string) (*SecurityProviderNautobotToken, error) {
@@ -25,62 +23,61 @@ func (s *SecurityProviderNautobotToken) Intercept(ctx context.Context, req *http
return nil
}
-// PaginatedSiteList defines model for PaginatedSiteList.
-type PaginatedSiteList struct {
- Count *int `json:"count,omitempty"`
- Next *string `json:"next"`
- Previous *string `json:"previous"`
- Results *[]Site `json:"results,omitempty"`
+func stringPtr(s string) *string {
+ return &s
}
-// Mixin to add `status` choice field to model serializers.
-type Site struct {
- // 32-bit autonomous system number
- Asn *int64 `json:"asn"`
- CircuitCount *int `json:"circuit_count,omitempty"`
- Comments *string `json:"comments,omitempty"`
- ContactEmail *string `json:"contact_email,omitempty"`
- ContactName *string `json:"contact_name,omitempty"`
- ContactPhone *string `json:"contact_phone,omitempty"`
- Created *types.Date `json:"created,omitempty"`
- CustomFields *nb.CustomFieldChoice `json:"custom_fields,omitempty"`
- Description *string `json:"description,omitempty"`
- DeviceCount *int `json:"device_count,omitempty"`
+func int32Ptr(i int) *int32 {
+ val := int32(i)
+ return &val
+}
+
+func getStatusName(ctx context.Context, c *nb.APIClient, token string, statusID string) (string, error) {
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: token,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ // Fetch the status using the status ID
+ status, _, err := c.ExtrasAPI.ExtrasStatusesRetrieve(auth, statusID).Execute()
+ if err != nil {
+ return "", err
+ }
+
+ // No need to dereference, just check if the string is empty
+ if status.Name != "" {
+ return status.Name, nil
+ }
+
+ return "", fmt.Errorf("status name not found for ID %s", statusID)
+}
- // Human friendly display value
- Display *string `json:"display,omitempty"`
+func getStatusID(ctx context.Context, c *nb.APIClient, token string, statusName string) (string, error) {
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: token,
+ Prefix: "Token",
+ },
+ },
+ )
- // Local facility ID or description
- Facility *string `json:"facility,omitempty"`
- Id *types.UUID `json:"id,omitempty"`
- LastUpdated *time.Time `json:"last_updated,omitempty"`
+ statuses, _, err := c.ExtrasAPI.ExtrasStatusesList(auth).Name([]string{statusName}).Execute()
+ if err != nil {
+ return "", err
+ }
- // GPS coordinate (latitude)
- Latitude *string `json:"latitude"`
+ if len(statuses.Results) == 0 {
+ return "", fmt.Errorf("status %s not found", statusName)
+ }
- // GPS coordinate (longitude)
- Longitude *string `json:"longitude"`
- Name string `json:"name"`
- PhysicalAddress *string `json:"physical_address,omitempty"`
- PrefixCount *int `json:"prefix_count,omitempty"`
- RackCount *int `json:"rack_count,omitempty"`
- Region *struct {
- // Embedded struct due to allOf(#/components/schemas/NestedRegion)
- nb.NestedRegion `yaml:",inline"`
- } `json:"region"`
- ShippingAddress *string `json:"shipping_address,omitempty"`
- Slug *string `json:"slug,omitempty"`
- Status struct {
- Label *nb.SiteStatusLabel `json:"label,omitempty"`
- Value *nb.SiteStatusValue `json:"value,omitempty"`
- } `json:"status"`
- Tags *[]nb.TagSerializerField `json:"tags,omitempty"`
- Tenant *struct {
- // Embedded struct due to allOf(#/components/schemas/NestedTenant)
- nb.NestedTenant `yaml:",inline"`
- } `json:"tenant"`
- TimeZone *string `json:"time_zone"`
- Url *string `json:"url,omitempty"`
- VirtualmachineCount *int `json:"virtualmachine_count,omitempty"`
- VlanCount *int `json:"vlan_count,omitempty"`
+ return statuses.Results[0].Id, nil
}
diff --git a/internal/provider/provider.go b/internal/provider/provider.go
index 9f3212c..9ce7667 100644
--- a/internal/provider/provider.go
+++ b/internal/provider/provider.go
@@ -7,7 +7,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
- nb "github.com/nautobot/go-nautobot/pkg/nautobot"
+ nb "github.com/nautobot/go-nautobot/v2"
)
func init() {
@@ -52,11 +52,29 @@ func New(version string) func() *schema.Provider {
},
},
DataSourcesMap: map[string]*schema.Resource{
- "nautobot_manufacturers": dataSourceManufacturers(),
- "nautobot_graphql": dataSourceGraphQL(),
+ "nautobot_available_ip_address": dataSourceAvailableIP(),
+ "nautobot_cluster": dataSourceCluster(),
+ "nautobot_clusters": dataSourceClusters(),
+ "nautobot_cluster_type": dataSourceClusterType(),
+ "nautobot_cluster_types": dataSourceClusterTypes(),
+ "nautobot_manufacturer": dataSourceManufacturer(),
+ "nautobot_manufacturers": dataSourceManufacturers(),
+ "nautobot_graphql": dataSourceGraphQL(),
+ "nautobot_prefix": dataSourcePrefix(),
+ "nautobot_prefixes": dataSourcePrefixes(),
+ "nautobot_virtual_machine": dataSourceVirtualMachine(),
+ "nautobot_virtual_machines": dataSourceVirtualMachines(),
+ "nautobot_vlan": dataSourceVLAN(),
+ "nautobot_vlans": dataSourceVLANs(),
},
ResourcesMap: map[string]*schema.Resource{
- "nautobot_manufacturer": resourceManufacturer(),
+ "nautobot_available_ip_address": resourceAvailableIPAddress(),
+ "nautobot_cluster": resourceCluster(),
+ "nautobot_cluster_type": resourceClusterType(),
+ "nautobot_manufacturer": resourceManufacturer(),
+ "nautobot_virtual_machine": resourceVirtualMachine(),
+ "nautobot_vm_interface": resourceVMInterface(),
+ "nautobot_vm_primary_ip": resourcePrimaryIPAddressForVM(),
},
}
@@ -70,10 +88,9 @@ func New(version string) func() *schema.Provider {
// you would need to setup to communicate with the upstream
// API.
type apiClient struct {
- Client *nb.ClientWithResponses
- Server string
- Token *SecurityProviderNautobotToken
- BaseClient *nb.Client
+ Client *nb.APIClient
+ Server string
+ Token *SecurityProviderNautobotToken
}
func configure(
@@ -82,6 +99,8 @@ func configure(
) func(context.Context, *schema.ResourceData) (interface{}, diag.Diagnostics) {
return func(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) {
serverURL := d.Get("url").(string)
+ config := nb.NewConfiguration()
+ config.Servers[0].URL = serverURL
_, hasToken := d.GetOk("token")
var diags diag.Diagnostics = nil
@@ -96,30 +115,12 @@ func configure(
d.Get("token").(string),
)
- c, err := nb.NewClientWithResponses(
- serverURL,
- nb.WithRequestEditorFn(token.Intercept),
- )
- if err != nil {
- diags = diag.FromErr(err)
- diags[0].Severity = diag.Error
- return &apiClient{Server: serverURL}, diags
- }
- bc, err := nb.NewClient(
- serverURL,
- nb.WithRequestEditorFn(token.Intercept),
- )
- if err != nil {
- diags = diag.FromErr(err)
- diags[0].Severity = diag.Error
- return &apiClient{Server: serverURL}, diags
- }
+ c := nb.NewAPIClient(config)
return &apiClient{
- Client: c,
- Server: serverURL,
- Token: token,
- BaseClient: bc,
+ Client: c,
+ Server: serverURL,
+ Token: token,
}, diags
}
}
diff --git a/internal/provider/resource_available_ip_address.go b/internal/provider/resource_available_ip_address.go
new file mode 100644
index 0000000..d54bca4
--- /dev/null
+++ b/internal/provider/resource_available_ip_address.go
@@ -0,0 +1,211 @@
+package provider
+
+import (
+ "context"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ nb "github.com/nautobot/go-nautobot/v2"
+)
+
+func resourceAvailableIPAddress() *schema.Resource {
+ return &schema.Resource{
+ Description: "This object allocates and manages an available IP address in Nautobot",
+
+ CreateContext: resourceAvailableIPAddressCreate,
+ ReadContext: resourceAvailableIPAddressRead,
+ UpdateContext: resourceAvailableIPAddressUpdate,
+ DeleteContext: resourceAvailableIPAddressDelete,
+
+ Schema: map[string]*schema.Schema{
+ "prefix_id": {
+ Description: "ID of the prefix to allocate the IP address from.",
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "address": {
+ Description: "Allocated IP address.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "ip_version": {
+ Description: "IP version of the allocated IP address (4 or 6).",
+ Type: schema.TypeInt,
+ Computed: true,
+ },
+ "dns_name": {
+ Description: "DNS name associated with the IP address.",
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ "status": {
+ Description: "Status of the allocated IP address.",
+ Type: schema.TypeString,
+ Required: true,
+ },
+ },
+ }
+}
+
+func resourceAvailableIPAddressCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ c := meta.(*apiClient).Client
+ t := meta.(*apiClient).Token.token
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ prefixID := d.Get("prefix_id").(string)
+
+ // Convert status name to ID
+ statusName := d.Get("status").(string)
+ statusID, err := getStatusID(ctx, c, t, statusName)
+ if err != nil {
+ return diag.Errorf("failed to get status ID for %s: %s", statusName, err.Error())
+ }
+
+ // Prepare the IP allocation request
+ ipRequest := nb.IPAllocationRequest{
+ Status: nb.BulkWritableCableRequestStatus{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: &statusID,
+ },
+ },
+ }
+
+ if v, ok := d.GetOk("dns_name"); ok {
+ dns_name := v.(string)
+ ipRequest.DnsName = &dns_name
+ }
+
+ // Allocate the IP (this automatically chooses the first available IP from the prefix)
+ rsp, _, err := c.IpamAPI.IpamPrefixesAvailableIpsCreate(auth, prefixID).IPAllocationRequest([]nb.IPAllocationRequest{ipRequest}).Execute()
+ if err != nil {
+ return diag.Errorf("failed to allocate IP address: %s", err.Error())
+ }
+
+ // Set resource data (assuming a single result, adjust if needed)
+ d.SetId(rsp[0].Id)
+ d.Set("address", rsp[0].Address)
+ d.Set("ip_version", rsp[0].IpVersion)
+ d.Set("dns_name", rsp[0].DnsName)
+
+ return resourceAvailableIPAddressRead(ctx, d, meta)
+}
+
+func resourceAvailableIPAddressRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ c := meta.(*apiClient).Client
+ t := meta.(*apiClient).Token.token
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ // Fetch the allocated IP by ID
+ ipID := d.Id()
+ ipAddress, _, err := c.IpamAPI.IpamIpAddressesRetrieve(auth, ipID).Execute()
+ if err != nil {
+ d.SetId("")
+ return diag.Errorf("failed to read IP address %s: %s", ipID, err.Error())
+ }
+
+ // Map the retrieved data back to Terraform state
+ d.Set("address", ipAddress.Address)
+ d.Set("ip_version", ipAddress.IpVersion)
+ d.Set("dns_name", ipAddress.DnsName)
+
+ return nil
+}
+
+func resourceAvailableIPAddressUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ c := meta.(*apiClient).Client
+ t := meta.(*apiClient).Token.token
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ ipID := d.Id()
+
+ var ipAddress nb.PatchedIPAddressRequest
+
+ if d.HasChange("status") {
+ statusName := d.Get("status").(string)
+ statusID, err := getStatusID(ctx, c, t, statusName)
+ if err != nil {
+ return diag.Errorf("failed to get status ID for %s: %s", statusName, err.Error())
+ }
+
+ ipAddress.Status = &nb.BulkWritableCableRequestStatus{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: &statusID,
+ },
+ }
+ }
+
+ if d.HasChange("dns_name") {
+ dnsName := d.Get("dns_name").(string)
+ ipAddress.DnsName = &dnsName
+ }
+
+ // Call the API to update the allocated IP address
+ _, _, err := c.IpamAPI.IpamIpAddressesPartialUpdate(auth, ipID).PatchedIPAddressRequest(ipAddress).Execute()
+ if err != nil {
+ return diag.Errorf("failed to update IP address %s: %s", ipID, err.Error())
+ }
+
+ return resourceAvailableIPAddressRead(ctx, d, meta)
+}
+
+func resourceAvailableIPAddressDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ c := meta.(*apiClient).Client
+ t := meta.(*apiClient).Token.token
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ // Fetch the IP address ID and delete it
+ ipID := d.Id()
+ _, err := c.IpamAPI.IpamIpAddressesDestroy(auth, ipID).Execute()
+ if err != nil {
+ return diag.Errorf("failed to delete IP address %s: %s", ipID, err.Error())
+ }
+
+ // Clear the ID from the state
+ d.SetId("")
+
+ return nil
+}
diff --git a/internal/provider/resource_cluster.go b/internal/provider/resource_cluster.go
new file mode 100644
index 0000000..4799f31
--- /dev/null
+++ b/internal/provider/resource_cluster.go
@@ -0,0 +1,357 @@
+package provider
+
+import (
+ "context"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ nb "github.com/nautobot/go-nautobot/v2"
+)
+
+func resourceCluster() *schema.Resource {
+ return &schema.Resource{
+ Description: "This object manages a cluster in Nautobot",
+
+ CreateContext: resourceClusterCreate,
+ ReadContext: resourceClusterRead,
+ UpdateContext: resourceClusterUpdate,
+ DeleteContext: resourceClusterDelete,
+
+ Schema: map[string]*schema.Schema{
+ "name": {
+ Description: "Cluster's name.",
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "comments": {
+ Description: "Comments or notes about the cluster.",
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ "cluster_type_id": {
+ Description: "ID of the Cluster's type. This can be sourced from the cluster_type resource or data source.",
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "cluster_group_id": {
+ Description: "ID of the Cluster's group.",
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ "tenant_id": {
+ Description: "ID of the Tenant associated with the cluster.",
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ "location_id": {
+ Description: "ID of the Location of the cluster.",
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ "tags_ids": {
+ Description: "IDs of the Tags associated with the cluster.",
+ Type: schema.TypeList,
+ Optional: true,
+ Elem: &schema.Schema{
+ Type: schema.TypeString,
+ },
+ },
+ "created": {
+ Description: "Creation date of the cluster.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "last_updated": {
+ Description: "Last update date of the cluster.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ },
+ }
+}
+
+func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ c := meta.(*apiClient).Client
+ t := meta.(*apiClient).Token.token
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ clusterName := d.Get("name").(string)
+ existingClusters, _, err := c.VirtualizationAPI.VirtualizationClustersList(auth).Name([]string{clusterName}).Execute()
+ if err != nil {
+ return diag.Errorf("failed to list clusters: %s", err.Error())
+ }
+
+ // If a cluster with the same name exists, use its ID and skip creation
+ if len(existingClusters.Results) > 0 {
+ d.SetId(existingClusters.Results[0].Id)
+ return resourceClusterRead(ctx, d, meta)
+ }
+
+ // Prepare ClusterRequest
+ var cluster nb.ClusterRequest
+ cluster.Name = clusterName
+ cluster.ClusterType = nb.BulkWritableCableRequestStatus{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: stringPtr(d.Get("cluster_type_id").(string)),
+ },
+ }
+
+ // Optional fields
+ if v, ok := d.GetOk("comments"); ok {
+ comments := v.(string)
+ cluster.Comments = &comments
+ }
+
+ if v, ok := d.GetOk("cluster_group_id"); ok {
+ var clusterGroup nb.NullableBulkWritableCircuitRequestTenant
+ clusterGroup.Set(&nb.BulkWritableCircuitRequestTenant{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: stringPtr(v.(string)),
+ },
+ })
+ cluster.ClusterGroup = clusterGroup
+ }
+
+ if v, ok := d.GetOk("tenant_id"); ok {
+ var tenant nb.NullableBulkWritableCircuitRequestTenant
+ tenant.Set(&nb.BulkWritableCircuitRequestTenant{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: stringPtr(v.(string)),
+ },
+ })
+ cluster.Tenant = tenant
+ }
+
+ if v, ok := d.GetOk("location_id"); ok {
+ var location nb.NullableBulkWritableCircuitRequestTenant
+ location.Set(&nb.BulkWritableCircuitRequestTenant{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: stringPtr(v.(string)),
+ },
+ })
+ cluster.Location = location
+ }
+
+ if v, ok := d.GetOk("tags_ids"); ok {
+ var tags []nb.BulkWritableCableRequestStatus
+ for _, tag := range v.([]interface{}) {
+ tags = append(tags, nb.BulkWritableCableRequestStatus{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: stringPtr(tag.(string)),
+ },
+ })
+ }
+ cluster.Tags = tags
+ }
+
+ // Create the cluster
+ rsp, _, err := c.VirtualizationAPI.VirtualizationClustersCreate(auth).ClusterRequest(cluster).Execute()
+ if err != nil {
+ return diag.Errorf("failed to create cluster: %s", err.Error())
+ }
+
+ // Set resource ID (Cluster ID)
+ d.SetId(rsp.Id)
+
+ return resourceClusterRead(ctx, d, meta)
+}
+
+func resourceClusterRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ c := meta.(*apiClient).Client
+ t := meta.(*apiClient).Token.token
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ // Fetch cluster by ID
+ clusterId := d.Id()
+ cluster, _, err := c.VirtualizationAPI.VirtualizationClustersRetrieve(auth, clusterId).Execute()
+ if err != nil {
+ return diag.Errorf("failed to read cluster: %s", err.Error())
+ }
+
+ // Map the retrieved data back to Terraform state
+ d.Set("name", cluster.Name)
+
+ // Extract cluster_type_id safely
+ if cluster.ClusterType.Id != nil && cluster.ClusterType.Id.String != nil {
+ d.Set("cluster_type_id", *cluster.ClusterType.Id.String)
+ }
+
+ // Check if comments exist before setting
+ if cluster.Comments != nil {
+ d.Set("comments", *cluster.Comments)
+ }
+
+ // Handle nullable cluster group
+ if cluster.ClusterGroup.IsSet() {
+ if clusterGroup := cluster.ClusterGroup.Get(); clusterGroup != nil && clusterGroup.Id != nil {
+ d.Set("cluster_group_id", *clusterGroup.Id.String)
+ }
+ }
+
+ // Handle nullable tenant
+ if cluster.Tenant.IsSet() {
+ if tenant := cluster.Tenant.Get(); tenant != nil && tenant.Id != nil {
+ d.Set("tenant_id", *tenant.Id.String)
+ }
+ }
+
+ // Handle nullable location
+ if cluster.Location.IsSet() {
+ if location := cluster.Location.Get(); location != nil && location.Id != nil {
+ d.Set("location_id", *location.Id.String)
+ }
+ }
+
+ // Set tags
+ if len(cluster.Tags) > 0 {
+ var tags []string
+ for _, tag := range cluster.Tags {
+ if tag.Id != nil && tag.Id.String != nil {
+ tags = append(tags, *tag.Id.String)
+ }
+ }
+ d.Set("tags_ids", tags)
+ }
+
+ d.Set("created", cluster.Created)
+ d.Set("last_updated", cluster.LastUpdated)
+
+ return nil
+}
+
+func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ c := meta.(*apiClient).Client
+ t := meta.(*apiClient).Token.token
+
+ clusterId := d.Id()
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ var cluster nb.PatchedClusterRequest
+
+ // Update the fields that have changed
+ if d.HasChange("name") {
+ name := d.Get("name").(string)
+ cluster.Name = &name // Set the pointer for the name
+ }
+ if d.HasChange("comments") {
+ comments := d.Get("comments").(string)
+ cluster.Comments = &comments
+ }
+ if d.HasChange("cluster_type_id") {
+ clusterTypeID := d.Get("cluster_type_id").(string)
+ cluster.ClusterType = &nb.BulkWritableCableRequestStatus{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: &clusterTypeID, // Pass pointer for the string value
+ },
+ }
+ }
+ if d.HasChange("cluster_group_id") {
+ clusterGroupID := d.Get("cluster_group_id").(string)
+ clusterGroup := &nb.BulkWritableCircuitRequestTenant{ // Create the cluster group as a pointer
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: &clusterGroupID, // Pass pointer for the string value
+ },
+ }
+ cluster.ClusterGroup.Set(clusterGroup) // Pass the pointer to Set()
+ }
+ if d.HasChange("tenant_id") {
+ tenantID := d.Get("tenant_id").(string)
+ tenant := &nb.BulkWritableCircuitRequestTenant{ // Create the tenant as a pointer
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: &tenantID, // Pass pointer for the string value
+ },
+ }
+ cluster.Tenant.Set(tenant) // Pass the pointer to Set()
+ }
+ if d.HasChange("location_id") {
+ locationID := d.Get("location_id").(string)
+ location := &nb.BulkWritableCircuitRequestTenant{ // Create the location as a pointer
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: &locationID, // Pass pointer for the string value
+ },
+ }
+ cluster.Location.Set(location) // Pass the pointer to Set()
+ }
+ if d.HasChange("tags_ids") {
+ var tags []nb.BulkWritableCableRequestStatus
+ for _, tag := range d.Get("tags_ids").([]interface{}) {
+ tagID := tag.(string)
+ tags = append(tags, nb.BulkWritableCableRequestStatus{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: &tagID, // Pass pointer for the string value
+ },
+ })
+ }
+ cluster.Tags = tags
+ }
+
+ // Call the API to update the cluster
+ _, _, err := c.VirtualizationAPI.VirtualizationClustersPartialUpdate(auth, clusterId).PatchedClusterRequest(cluster).Execute()
+ if err != nil {
+ return diag.Errorf("failed to update cluster: %s", err.Error())
+ }
+
+ return resourceClusterRead(ctx, d, meta)
+}
+
+func resourceClusterDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ c := meta.(*apiClient).Client
+ t := meta.(*apiClient).Token.token
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ // Delete the cluster by ID
+ clusterId := d.Id()
+ _, err := c.VirtualizationAPI.VirtualizationClustersDestroy(auth, clusterId).Execute()
+ if err != nil {
+ return diag.Errorf("failed to delete cluster: %s", err.Error())
+ }
+
+ // Clear the ID
+ d.SetId("")
+
+ return nil
+}
diff --git a/internal/provider/resource_cluster_type.go b/internal/provider/resource_cluster_type.go
new file mode 100644
index 0000000..306fafd
--- /dev/null
+++ b/internal/provider/resource_cluster_type.go
@@ -0,0 +1,229 @@
+package provider
+
+import (
+ "context"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ nb "github.com/nautobot/go-nautobot/v2"
+)
+
+func resourceClusterType() *schema.Resource {
+ return &schema.Resource{
+ Description: "This object manages a cluster type in Nautobot.",
+
+ CreateContext: resourceClusterTypeCreate,
+ ReadContext: resourceClusterTypeRead,
+ UpdateContext: resourceClusterTypeUpdate,
+ DeleteContext: resourceClusterTypeDelete,
+
+ Schema: map[string]*schema.Schema{
+ "id": {
+ Description: "Cluster type's UUID.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "object_type": {
+ Description: "Object type of the cluster type.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "display": {
+ Description: "Human-friendly display value for the cluster type.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "url": {
+ Description: "URL of the cluster type.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "natural_slug": {
+ Description: "Natural slug for the cluster type.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "name": {
+ Description: "Cluster type's name.",
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "description": {
+ Description: "Description for the cluster type.",
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ "created": {
+ Description: "Creation date of the cluster type.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "last_updated": {
+ Description: "Last update date of the cluster type.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "notes_url": {
+ Description: "Notes URL for the cluster type.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ },
+ }
+}
+
+func resourceClusterTypeCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ c := meta.(*apiClient).Client
+ t := meta.(*apiClient).Token.token
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ clusterTypeName := d.Get("name").(string)
+ existingClusterTypes, _, err := c.VirtualizationAPI.VirtualizationClusterTypesList(auth).Name([]string{clusterTypeName}).Execute()
+ if err != nil {
+ return diag.Errorf("failed to list cluster types: %s", err.Error())
+ }
+
+ // If a cluster type with the same name exists, use its ID and skip creation
+ if len(existingClusterTypes.Results) > 0 {
+ d.SetId(existingClusterTypes.Results[0].Id)
+ return resourceClusterTypeRead(ctx, d, meta)
+ }
+
+ // Prepare ClusterTypeRequest
+ var clusterType nb.ClusterTypeRequest
+ clusterType.Name = clusterTypeName
+
+ if v, ok := d.GetOk("description"); ok {
+ description := v.(string)
+ clusterType.Description = &description
+ }
+
+ // Create the cluster type using VirtualizationAPI
+ rsp, _, err := c.VirtualizationAPI.VirtualizationClusterTypesCreate(auth).ClusterTypeRequest(clusterType).Execute()
+ if err != nil {
+ return diag.Errorf("failed to create cluster type: %s", err.Error())
+ }
+
+ // Set resource ID (Cluster Type ID)
+ d.SetId(rsp.Id)
+
+ return resourceClusterTypeRead(ctx, d, meta)
+}
+
+func resourceClusterTypeRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ c := meta.(*apiClient).Client
+ t := meta.(*apiClient).Token.token
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ // Fetch cluster type by ID using VirtualizationAPI
+ clusterTypeId := d.Id()
+ clusterType, _, err := c.VirtualizationAPI.VirtualizationClusterTypesRetrieve(auth, clusterTypeId).Execute()
+ if err != nil {
+ return diag.Errorf("failed to read cluster type: %s", err.Error())
+ }
+
+ // Map the retrieved data back to Terraform state
+ d.Set("name", clusterType.Name)
+ d.Set("object_type", clusterType.ObjectType)
+ d.Set("display", clusterType.Display)
+ d.Set("url", clusterType.Url)
+ d.Set("natural_slug", clusterType.NaturalSlug)
+ if clusterType.Description != nil {
+ d.Set("description", *clusterType.Description)
+ }
+ d.Set("created", clusterType.Created)
+ d.Set("last_updated", clusterType.LastUpdated)
+ d.Set("notes_url", clusterType.NotesUrl)
+
+ return nil
+}
+
+func resourceClusterTypeUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ c := meta.(*apiClient).Client
+ t := meta.(*apiClient).Token.token
+
+ clusterTypeId := d.Id()
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ var clusterType nb.PatchedClusterTypeRequest
+
+ // Update the fields that have changed
+ if d.HasChange("name") {
+ name := d.Get("name").(string)
+ clusterType.Name = &name
+ }
+ if d.HasChange("description") {
+ description := d.Get("description").(string)
+ clusterType.Description = &description
+ }
+
+ // Call the API to update the cluster type
+ _, _, err := c.VirtualizationAPI.VirtualizationClusterTypesPartialUpdate(auth, clusterTypeId).PatchedClusterTypeRequest(clusterType).Execute()
+ if err != nil {
+ return diag.Errorf("failed to update cluster type: %s", err.Error())
+ }
+
+ return resourceClusterTypeRead(ctx, d, meta)
+}
+
+func resourceClusterTypeDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ c := meta.(*apiClient).Client
+ t := meta.(*apiClient).Token.token
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ // Delete the cluster type by ID using VirtualizationAPI
+ clusterTypeId := d.Id()
+ _, err := c.VirtualizationAPI.VirtualizationClusterTypesDestroy(auth, clusterTypeId).Execute()
+ if err != nil {
+ return diag.Errorf("failed to delete cluster type: %s", err.Error())
+ }
+
+ // Clear the ID
+ d.SetId("")
+
+ return nil
+}
diff --git a/internal/provider/resource_manufacturer.go b/internal/provider/resource_manufacturer.go
index 378443e..ad39b1a 100644
--- a/internal/provider/resource_manufacturer.go
+++ b/internal/provider/resource_manufacturer.go
@@ -2,17 +2,13 @@ package provider
import (
"context"
- "encoding/json"
- "strings"
+ "time"
- "github.com/deepmap/oapi-codegen/pkg/types"
- "github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
- "github.com/tidwall/gjson"
- nb "github.com/nautobot/go-nautobot/pkg/nautobot"
+ nb "github.com/nautobot/go-nautobot/v2"
)
func resourceManufacturer() *schema.Resource {
@@ -35,20 +31,9 @@ func resourceManufacturer() *schema.Resource {
Type: schema.TypeString,
Optional: true,
},
- "custom_fields": {
- Description: "Manufacturer custom fields.",
- Type: schema.TypeMap,
- Optional: true,
- },
- "devicetype_count": {
- Description: "Manufacturer's device count.",
- Type: schema.TypeInt,
- Computed: true,
- },
"display": {
Description: "Manufacturer's display name.",
Type: schema.TypeString,
- Optional: true,
Computed: true,
},
"id": {
@@ -56,13 +41,8 @@ func resourceManufacturer() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
- "inventoryitem_count": {
- Description: "Manufacturer's inventory item count.",
- Type: schema.TypeInt,
- Computed: true,
- },
"last_updated": {
- Description: "Manufacturer's last update.",
+ Description: "Manufacturer's last update date.",
Type: schema.TypeString,
Computed: true,
},
@@ -72,26 +52,23 @@ func resourceManufacturer() *schema.Resource {
Required: true,
},
"notes_url": {
- Description: "Notes for manufacturer.",
+ Description: "Notes URL for the manufacturer.",
Type: schema.TypeString,
- Optional: true,
Computed: true,
},
- "platform_count": {
- Description: "Manufacturer's platform count.",
- Type: schema.TypeInt,
+ "url": {
+ Description: "Manufacturer's URL.",
+ Type: schema.TypeString,
Computed: true,
},
- "slug": {
- Description: "Manufacturer's slug.",
+ "object_type": {
+ Description: "Object type of the manufacturer.",
Type: schema.TypeString,
- Optional: true,
Computed: true,
},
- "url": {
- Description: "Manufacturer's URL.",
+ "natural_slug": {
+ Description: "Natural slug for the manufacturer.",
Type: schema.TypeString,
- Optional: true,
Computed: true,
},
},
@@ -101,175 +78,136 @@ func resourceManufacturer() *schema.Resource {
func resourceManufacturerCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*apiClient).Client
s := meta.(*apiClient).Server
+ t := meta.(*apiClient).Token.token
- var m nb.ManufacturerRequest
-
- name, ok := d.GetOk("name")
- n := name.(string)
- if ok {
- m.Name = n
- }
-
- m.Description = &n
- description, ok := d.GetOk("description")
- if ok {
- t := description.(string)
- m.Description = &t
- }
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
- sl := strings.ReplaceAll(strings.ToLower(n), " ", "-")
- m.Slug = &sl
- slug, ok := d.GetOk("slug")
- if ok {
- t := slug.(string)
- m.Slug = &t
- }
+ // Check if a manufacturer with the same name already exists
+ name := d.Get("name").(string)
- rsp, err := c.DcimManufacturersCreateWithResponse(
- ctx,
- nb.DcimManufacturersCreateJSONRequestBody(m))
+ existingManufacturersResp, _, err := c.DcimAPI.DcimManufacturersList(auth).Execute()
if err != nil {
- return diag.Errorf("failed to create manufacturer %s on %s: %s", name.(string), s, err.Error())
+ return diag.Errorf("failed to check existing manufacturers on %s : %s", s, err.Error())
}
- data := string(rsp.Body)
-
- dataName := gjson.Get(data, "name.0")
-
- if dataName.String() == "manufacturer with this name already exists." {
- rsp, err := c.DcimManufacturersListWithResponse(
- ctx,
- &nb.DcimManufacturersListParams{
- NameIe: &[]string{n},
- })
- if err != nil {
- return diag.Errorf("failed to get manufacturer %s from %s: %s", n, s, err.Error())
+ // Search through the results for a manufacturer with the given name
+ for _, manufacturer := range existingManufacturersResp.Results {
+ if manufacturer.Name == name {
+ // Manufacturer already exists, set the ID and exit
+ d.SetId(manufacturer.Id)
+ return resourceManufacturerRead(ctx, d, meta)
}
- id := gjson.Get(string(rsp.Body), "results.0.id")
+ }
- d.SetId(id.String())
+ // Create a new manufacturer
+ var m nb.ManufacturerRequest
+ m.Name = name
- return resourceManufacturerRead(ctx, d, meta)
+ if v, ok := d.GetOk("description"); ok {
+ desc := v.(string)
+ m.Description = &desc
+ }
+ rsp, _, err := c.DcimAPI.DcimManufacturersCreate(auth).ManufacturerRequest(m).Execute()
+ if err != nil {
+ return diag.Errorf("failed to create manufacturer %s on %s: %s", m.Name, s, err.Error())
}
tflog.Trace(ctx, "manufacturer created", map[string]interface{}{
- "name": name.(string),
- "data": []interface{}{description, slug},
+ "name": m.Name,
})
- id := gjson.Get(data, "id")
-
- d.SetId(id.String())
+ d.SetId(rsp.Id)
return resourceManufacturerRead(ctx, d, meta)
}
func resourceManufacturerRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*apiClient).Client
+ t := meta.(*apiClient).Token.token
id := d.Get("id").(string)
- rsp, err := c.DcimManufacturersListWithResponse(
- ctx,
- &nb.DcimManufacturersListParams{
- IdIe: &[]types.UUID{uuid.MustParse(id)},
- })
-
- var diags diag.Diagnostics
- name := d.Get("name").(string)
- s := meta.(*apiClient).Server
- if err != nil {
- return diag.Errorf("failed to get manufacturer %s from %s: %s", name, s, err.Error())
- }
-
- // If the Manufacturer is in the state file, but it is not in the Nautobot platform
- // the response we get from DcimManufacturersListWithResponse is: {"count":0,"next":null,"previous":null,"results":[]}
- // When you create something in Terraform but delete it manually, Terraform should gracefully handle it.
- // We should set the ID to an empty string so Terraform "destroys" the resource in state.
- count := gjson.Get(string(rsp.Body), "count")
- if count.String() == "0" {
- d.SetId("")
- return diags
- }
- results := gjson.Get(string(rsp.Body), "results.0")
-
- resultsReader := strings.NewReader(results.String())
-
- item := make(map[string]interface{})
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
- err = json.NewDecoder(resultsReader).Decode(&item)
+ // Fetch manufacturer by ID
+ manufacturer, _, err := c.DcimAPI.DcimManufacturersRetrieve(auth, id).Execute()
if err != nil {
- return diag.Errorf("failed to decode manufacturer %s from %s: %s", name, s, err.Error())
+ return diag.Errorf("failed to get manufacturer %s: %s", id, err.Error())
}
- d.Set("name", item["name"].(string))
- d.Set("created", item["created"].(string))
- d.Set("description", item["description"].(string))
- d.Set("display", item["display"].(string))
- d.Set("id", item["id"].(string))
- d.Set("notes_url", item["notes_url"].(string))
- d.Set("slug", item["slug"].(string))
- d.Set("url", item["url"].(string))
- d.Set("last_updated", item["last_updated"].(string))
-
- switch v := item["devicetype_count"].(type) {
- case int:
- d.Set("devicetype_count", v)
- case float64:
- d.Set("devicetype_count", int(v))
- default:
- }
- switch v := item["inventoryitem_count"].(type) {
- case int:
- d.Set("inventoryitem_count", v)
- case float64:
- d.Set("inventoryitem_count", int(v))
- default:
+ // Set the Terraform state from the retrieved manufacturer data
+ d.Set("name", manufacturer.Name)
+ if manufacturer.Created.IsSet() && manufacturer.Created.Get() != nil {
+ d.Set("created", manufacturer.Created.Get().Format(time.RFC3339))
}
- switch v := item["platform_count"].(type) {
- case int:
- d.Set("platform_count", v)
- case float64:
- d.Set("platform_count", int(v))
- default:
+ if manufacturer.LastUpdated.IsSet() && manufacturer.LastUpdated.Get() != nil {
+ d.Set("last_updated", manufacturer.LastUpdated.Get().Format(time.RFC3339))
}
+ d.Set("description", manufacturer.Description)
+ d.Set("display", manufacturer.Display)
+ d.Set("id", manufacturer.Id)
+ d.Set("notes_url", manufacturer.NotesUrl)
+ d.Set("url", manufacturer.Url)
+ d.Set("object_type", manufacturer.ObjectType)
+ d.Set("natural_slug", manufacturer.NaturalSlug)
- return diags
+ return nil
}
func resourceManufacturerUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
c := meta.(*apiClient).Client
s := meta.(*apiClient).Server
+ t := meta.(*apiClient).Token.token
- name := d.Get("name").(string)
id := d.Get("id").(string)
var m nb.PatchedManufacturerRequest
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
if d.HasChange("name") {
+ name := d.Get("name").(string)
m.Name = &name
}
- desc := d.Get("description").(string)
if d.HasChange("description") {
+ desc := d.Get("description").(string)
m.Description = &desc
}
- slug := d.Get("slug").(string)
- if d.HasChange("slug") {
- m.Description = &slug
- }
-
- _, err := c.DcimManufacturersPartialUpdateWithResponse(
- ctx,
- uuid.MustParse(id),
- nb.DcimManufacturersPartialUpdateJSONRequestBody(m))
+ _, _, err := c.DcimAPI.DcimManufacturersPartialUpdate(auth, id).PatchedManufacturerRequest(m).Execute()
if err != nil {
- return diag.Errorf("failed to update manufacturer %s on %s: %s", name, s, err.Error())
+ return diag.Errorf("failed to update manufacturer %s on %s: %s", id, s, err.Error())
}
tflog.Trace(ctx, "manufacturer updated", map[string]interface{}{
- "name": name,
- "data": []string{desc, slug},
+ "id": id,
})
return resourceManufacturerRead(ctx, d, meta)
@@ -280,15 +218,24 @@ func resourceManufacturerDelete(ctx context.Context, d *schema.ResourceData, met
c := meta.(*apiClient).Client
s := meta.(*apiClient).Server
+ t := meta.(*apiClient).Token.token
id := d.Get("id").(string)
- name := d.Get("name").(string)
- _, err := c.DcimManufacturersDestroy(
+ auth := context.WithValue(
ctx,
- uuid.MustParse(id))
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ _, err := c.DcimAPI.DcimManufacturersDestroy(auth, id).Execute()
if err != nil {
- return diag.Errorf("failed to delete manufacturer %s on %s: %s", name, s, err.Error())
+ return diag.Errorf("failed to delete manufacturer %s on %s: %s", id, s, err.Error())
}
// d.SetId("") is automatically called assuming delete returns no errors, but
diff --git a/internal/provider/resource_virtual_machine.go b/internal/provider/resource_virtual_machine.go
new file mode 100644
index 0000000..892ac60
--- /dev/null
+++ b/internal/provider/resource_virtual_machine.go
@@ -0,0 +1,583 @@
+package provider
+
+import (
+ "context"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ nb "github.com/nautobot/go-nautobot/v2"
+)
+
+func resourceVirtualMachine() *schema.Resource {
+ return &schema.Resource{
+ Description: "This object manages a virtual machine in Nautobot",
+
+ CreateContext: resourceVirtualMachineCreate,
+ ReadContext: resourceVirtualMachineRead,
+ UpdateContext: resourceVirtualMachineUpdate,
+ DeleteContext: resourceVirtualMachineDelete,
+
+ Schema: map[string]*schema.Schema{
+ "name": {
+ Description: "Virtual Machine's name.",
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "cluster_id": {
+ Description: "Cluster where the virtual machine belongs.",
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "status": {
+ Description: "Status of the virtual machine.",
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "vcpus": {
+ Description: "Number of virtual CPUs.",
+ Type: schema.TypeInt,
+ Optional: true,
+ },
+ "memory": {
+ Description: "Amount of memory in MB.",
+ Type: schema.TypeInt,
+ Optional: true,
+ },
+ "disk": {
+ Description: "Disk size in GB.",
+ Type: schema.TypeInt,
+ Optional: true,
+ },
+ "comments": {
+ Description: "Comments or notes about the virtual machine.",
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ "tenant_id": {
+ Description: "Tenant associated with the virtual machine.",
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ "platform_id": {
+ Description: "Platform or OS installed on the virtual machine.",
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ "role_id": {
+ Description: "Role of the virtual machine.",
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ "primary_ip4_id": {
+ Description: "Primary IPv4 address.",
+ Type: schema.TypeString,
+ Optional: true,
+ Computed: true,
+ },
+ "primary_ip6_id": {
+ Description: "Primary IPv6 address.",
+ Type: schema.TypeString,
+ Optional: true,
+ Computed: true,
+ },
+ "software_version_id": {
+ Description: "Software version installed on the virtual machine.",
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ "software_image_files": {
+ Description: "Software image files associated with the software version.",
+ Type: schema.TypeList,
+ Optional: true,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "id": {
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ },
+ },
+ },
+ "tags_ids": {
+ Description: "Tags associated with the virtual machine.",
+ Type: schema.TypeList,
+ Optional: true,
+ Elem: &schema.Schema{
+ Type: schema.TypeString,
+ },
+ },
+ "created": {
+ Description: "Creation date of the virtual machine.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "last_updated": {
+ Description: "Last update date of the virtual machine.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ },
+ }
+}
+
+func resourceVirtualMachineCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ c := meta.(*apiClient).Client
+ t := meta.(*apiClient).Token.token
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ // Check if the VM with the same name exists
+ vmName := d.Get("name").(string)
+ existingVMs, _, err := c.VirtualizationAPI.VirtualizationVirtualMachinesList(auth).Name([]string{vmName}).Execute()
+ if err != nil {
+ return diag.Errorf("failed to list virtual machines: %s", err.Error())
+ }
+
+ // If a VM with the same name exists, use its ID and skip creation
+ if len(existingVMs.Results) > 0 {
+ d.SetId(existingVMs.Results[0].Id)
+ return resourceVirtualMachineRead(ctx, d, meta)
+ }
+
+ // Convert status name to ID
+ statusName := d.Get("status").(string)
+ statusID, err := getStatusID(ctx, c, t, statusName)
+ if err != nil {
+ return diag.Errorf("failed to get status ID for %s: %s", statusName, err.Error())
+ }
+
+ // Prepare the VirtualMachineRequest
+ var vm nb.VirtualMachineRequest
+ vm.Name = d.Get("name").(string)
+ vm.Cluster = nb.BulkWritableCableRequestStatus{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: stringPtr(d.Get("cluster_id").(string)),
+ },
+ }
+ vm.Status = nb.BulkWritableCableRequestStatus{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: stringPtr(statusID),
+ },
+ }
+
+ // Optional fields
+ if v, ok := d.GetOk("vcpus"); ok {
+ vm.Vcpus.Set(int32Ptr(v.(int)))
+ }
+ if v, ok := d.GetOk("memory"); ok {
+ vm.Memory.Set(int32Ptr(v.(int)))
+ }
+ if v, ok := d.GetOk("disk"); ok {
+ vm.Disk.Set(int32Ptr(v.(int)))
+ }
+ if v, ok := d.GetOk("comments"); ok {
+ comments := v.(string)
+ vm.Comments = &comments
+ }
+ if v, ok := d.GetOk("tenant_id"); ok {
+ tenant := v.(string)
+ var nullableTenant nb.NullableBulkWritableCircuitRequestTenant
+ nullableTenant.Set(&nb.BulkWritableCircuitRequestTenant{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: stringPtr(tenant),
+ },
+ })
+ vm.Tenant = nullableTenant
+ }
+ if v, ok := d.GetOk("platform_id"); ok {
+ platform := v.(string)
+ var nullablePlatform nb.NullableBulkWritableCircuitRequestTenant
+ nullablePlatform.Set(&nb.BulkWritableCircuitRequestTenant{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: stringPtr(platform),
+ },
+ })
+ vm.Platform = nullablePlatform
+ }
+
+ if v, ok := d.GetOk("role_id"); ok {
+ role := v.(string)
+ var nullableRole nb.NullableBulkWritableCircuitRequestTenant
+ nullableRole.Set(&nb.BulkWritableCircuitRequestTenant{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: stringPtr(role),
+ },
+ })
+ vm.Role = nullableRole
+ }
+
+ if v, ok := d.GetOk("primary_ip4_id"); ok {
+ ip4 := v.(string)
+ var nullableIP4 nb.NullablePrimaryIPv4
+ primaryIPv4 := &nb.PrimaryIPv4{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: stringPtr(ip4),
+ },
+ }
+ nullableIP4.Set(primaryIPv4)
+ vm.PrimaryIp4 = nullableIP4
+ }
+
+ if v, ok := d.GetOk("primary_ip6_id"); ok {
+ ip6 := v.(string)
+ var nullableIP6 nb.NullablePrimaryIPv6
+ primaryIPv6 := &nb.PrimaryIPv6{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: stringPtr(ip6),
+ },
+ }
+ nullableIP6.Set(primaryIPv6)
+ vm.PrimaryIp6 = nullableIP6
+ }
+
+ if v, ok := d.GetOk("software_version_id"); ok {
+ softwareVersion := v.(string)
+ var nullableSoftwareVersion nb.NullableBulkWritableVirtualMachineRequestSoftwareVersion
+ softwareVersionStruct := &nb.BulkWritableVirtualMachineRequestSoftwareVersion{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: stringPtr(softwareVersion),
+ },
+ }
+ nullableSoftwareVersion.Set(softwareVersionStruct)
+ vm.SoftwareVersion = nullableSoftwareVersion
+ }
+
+ if v, ok := d.GetOk("software_image_files"); ok {
+ var files []nb.SoftwareImageFiles
+ for _, file := range v.([]interface{}) {
+ fileData := file.(map[string]interface{})
+ files = append(files, nb.SoftwareImageFiles{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: stringPtr(fileData["id"].(string)),
+ },
+ })
+ }
+ vm.SoftwareImageFiles = files
+ }
+ if v, ok := d.GetOk("tags_ids"); ok {
+ var tags []nb.BulkWritableCableRequestStatus
+ for _, tag := range v.([]interface{}) {
+ tags = append(tags, nb.BulkWritableCableRequestStatus{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: stringPtr(tag.(string)),
+ },
+ })
+ }
+ vm.Tags = tags
+ }
+
+ // Create the virtual machine
+ rsp, _, err := c.VirtualizationAPI.VirtualizationVirtualMachinesCreate(auth).VirtualMachineRequest(vm).Execute()
+ if err != nil {
+ return diag.Errorf("failed to create virtual machine: %s", err.Error())
+ }
+
+ // Set resource ID
+ d.SetId(rsp.Id)
+
+ return resourceVirtualMachineRead(ctx, d, meta)
+}
+
+func resourceVirtualMachineRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ c := meta.(*apiClient).Client
+ t := meta.(*apiClient).Token.token
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ // Fetch virtual machine by ID
+ vmId := d.Id()
+ vm, _, err := c.VirtualizationAPI.VirtualizationVirtualMachinesRetrieve(auth, vmId).Execute()
+ if err != nil {
+ return diag.Errorf("failed to read virtual machine: %s", err.Error())
+ }
+
+ // Map the retrieved data back to Terraform state
+ d.Set("name", vm.Name)
+ d.Set("cluster_id", vm.Cluster.Id)
+ d.Set("status", vm.Status.Id)
+ d.Set("vcpus", vm.Vcpus.Get())
+ d.Set("memory", vm.Memory.Get())
+ d.Set("disk", vm.Disk.Get())
+ d.Set("comments", vm.Comments)
+
+ // Handle nullable fields using IsSet and Get methods
+ if vm.Tenant.IsSet() {
+ tenant := vm.Tenant.Get()
+ if tenant != nil && tenant.Id != nil {
+ d.Set("tenant_id", *tenant.Id.String)
+ }
+ }
+
+ if vm.Platform.IsSet() {
+ platform := vm.Platform.Get()
+ if platform != nil && platform.Id != nil {
+ d.Set("platform_id", *platform.Id.String)
+ }
+ }
+
+ if vm.Role.IsSet() {
+ role := vm.Role.Get()
+ if role != nil && role.Id != nil {
+ d.Set("role_id", *role.Id.String)
+ }
+ }
+
+ if vm.PrimaryIp4.IsSet() {
+ primaryIp4 := vm.PrimaryIp4.Get()
+ if primaryIp4 != nil && primaryIp4.Id != nil {
+ d.Set("primary_ip4_id", *primaryIp4.Id.String)
+ }
+ }
+
+ if vm.PrimaryIp6.IsSet() {
+ primaryIp6 := vm.PrimaryIp6.Get()
+ if primaryIp6 != nil && primaryIp6.Id != nil {
+ d.Set("primary_ip6_id", *primaryIp6.Id.String)
+ }
+ }
+
+ if vm.SoftwareVersion.IsSet() {
+ softwareVersion := vm.SoftwareVersion.Get()
+ if softwareVersion != nil && softwareVersion.Id != nil {
+ d.Set("software_version_id", *softwareVersion.Id.String)
+ }
+ }
+
+ var imageFiles []map[string]string
+ for _, file := range vm.SoftwareImageFiles {
+ if file.Id != nil && file.Id.String != nil {
+ imageFiles = append(imageFiles, map[string]string{
+ "id": *file.Id.String,
+ })
+ }
+ }
+
+ d.Set("software_image_files", imageFiles)
+
+ var tags []string
+ for _, tag := range vm.Tags {
+ if tag.Id != nil {
+ tags = append(tags, *tag.Id.String)
+ }
+ }
+ d.Set("tags_ids", tags)
+
+ d.Set("created", vm.Created)
+ d.Set("last_updated", vm.LastUpdated)
+
+ return nil
+}
+
+func resourceVirtualMachineUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ c := meta.(*apiClient).Client
+ t := meta.(*apiClient).Token.token
+
+ vmId := d.Id()
+
+ var vm nb.PatchedVirtualMachineRequest
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ // Update the fields that have changed
+ if d.HasChange("name") {
+ name := d.Get("name").(string)
+ vm.Name = &name
+ }
+
+ if d.HasChange("cluster_id") {
+ clusterID := d.Get("cluster_id").(string)
+ vm.Cluster = &nb.BulkWritableCableRequestStatus{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: &clusterID,
+ },
+ }
+ }
+
+ if d.HasChange("status") {
+ statusName := d.Get("status").(string)
+ statusID, err := getStatusID(ctx, c, t, statusName)
+ if err != nil {
+ return diag.Errorf("failed to get status ID for %s: %s", statusName, err.Error())
+ }
+ vm.Status = &nb.BulkWritableCableRequestStatus{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: stringPtr(statusID),
+ },
+ }
+ }
+
+ // Optional fields
+ if d.HasChange("vcpus") {
+ vm.Vcpus.Set(int32Ptr(d.Get("vcpus").(int)))
+ }
+ if d.HasChange("memory") {
+ vm.Memory.Set(int32Ptr(d.Get("memory").(int)))
+ }
+ if d.HasChange("disk") {
+ vm.Disk.Set(int32Ptr(d.Get("disk").(int)))
+ }
+ if d.HasChange("comments") {
+ comments := d.Get("comments").(string)
+ vm.Comments = &comments
+ }
+ if d.HasChange("tenant_id") {
+ tenant := d.Get("tenant_id").(string)
+ var nullableTenant nb.NullableBulkWritableCircuitRequestTenant
+ nullableTenant.Set(&nb.BulkWritableCircuitRequestTenant{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: stringPtr(tenant),
+ },
+ })
+ vm.Tenant = nullableTenant
+ }
+ if d.HasChange("platform_id") {
+ platform := d.Get("platform_id").(string)
+ var nullablePlatform nb.NullableBulkWritableCircuitRequestTenant
+ nullablePlatform.Set(&nb.BulkWritableCircuitRequestTenant{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: stringPtr(platform),
+ },
+ })
+ vm.Platform = nullablePlatform
+ }
+
+ if d.HasChange("role_id") {
+ role := d.Get("role_id").(string)
+ var nullableRole nb.NullableBulkWritableCircuitRequestTenant
+ nullableRole.Set(&nb.BulkWritableCircuitRequestTenant{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: stringPtr(role),
+ },
+ })
+ vm.Role = nullableRole
+ }
+
+ if d.HasChange("primary_ip4_id") {
+ ip4 := d.Get("primary_ip4_id").(string)
+ var nullableIP4 nb.NullablePrimaryIPv4
+ primaryIPv4 := &nb.PrimaryIPv4{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: stringPtr(ip4),
+ },
+ }
+ nullableIP4.Set(primaryIPv4)
+ vm.PrimaryIp4 = nullableIP4
+ }
+
+ if d.HasChange("primary_ip6_id") {
+ ip6 := d.Get("primary_ip6_id").(string)
+ var nullableIP6 nb.NullablePrimaryIPv6
+ primaryIPv6 := &nb.PrimaryIPv6{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: stringPtr(ip6),
+ },
+ }
+ nullableIP6.Set(primaryIPv6)
+ vm.PrimaryIp6 = nullableIP6
+ }
+
+ if d.HasChange("software_version_id") {
+ softwareVersion := d.Get("software_version_id").(string)
+ var nullableSoftwareVersion nb.NullableBulkWritableVirtualMachineRequestSoftwareVersion
+ softwareVersionStruct := &nb.BulkWritableVirtualMachineRequestSoftwareVersion{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: stringPtr(softwareVersion),
+ },
+ }
+ nullableSoftwareVersion.Set(softwareVersionStruct)
+ vm.SoftwareVersion = nullableSoftwareVersion
+ }
+
+ if d.HasChange("software_image_files") {
+ var files []nb.SoftwareImageFiles
+ for _, file := range d.Get("software_image_files").([]interface{}) {
+ fileData := file.(map[string]interface{})
+ files = append(files, nb.SoftwareImageFiles{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: stringPtr(fileData["id"].(string)),
+ },
+ })
+ }
+ vm.SoftwareImageFiles = files
+ }
+
+ if d.HasChange("tags_ids") {
+ var tags []nb.BulkWritableCableRequestStatus
+ for _, tag := range d.Get("tags_ids").([]interface{}) {
+ tagID := tag.(string)
+ tags = append(tags, nb.BulkWritableCableRequestStatus{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: &tagID,
+ },
+ })
+ }
+ vm.Tags = tags
+ }
+
+ // Call the API to update the virtual machine
+ _, _, err := c.VirtualizationAPI.VirtualizationVirtualMachinesPartialUpdate(auth, vmId).PatchedVirtualMachineRequest(vm).Execute()
+ if err != nil {
+ return diag.Errorf("failed to update virtual machine: %s", err.Error())
+ }
+
+ return resourceVirtualMachineRead(ctx, d, meta)
+}
+
+func resourceVirtualMachineDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ c := meta.(*apiClient).Client
+ t := meta.(*apiClient).Token.token
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ // Delete the virtual machine by ID
+ vmId := d.Id()
+ _, err := c.VirtualizationAPI.VirtualizationVirtualMachinesDestroy(auth, vmId).Execute()
+ if err != nil {
+ return diag.Errorf("failed to delete virtual machine: %s", err.Error())
+ }
+
+ // Clear the ID
+ d.SetId("")
+
+ return nil
+}
diff --git a/internal/provider/resource_virtual_machine_interface.go b/internal/provider/resource_virtual_machine_interface.go
new file mode 100644
index 0000000..f2d2565
--- /dev/null
+++ b/internal/provider/resource_virtual_machine_interface.go
@@ -0,0 +1,450 @@
+package provider
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ nb "github.com/nautobot/go-nautobot/v2"
+)
+
+func resourceVMInterface() *schema.Resource {
+ return &schema.Resource{
+ Description: "This object manages a VM Interface in Nautobot",
+
+ CreateContext: resourceVMInterfaceCreate,
+ ReadContext: resourceVMInterfaceRead,
+ UpdateContext: resourceVMInterfaceUpdate,
+ DeleteContext: resourceVMInterfaceDelete,
+
+ Schema: map[string]*schema.Schema{
+ "name": {
+ Description: "Name of the VM interface.",
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "mac_address": {
+ Description: "MAC address of the interface.",
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ "enabled": {
+ Description: "Whether the interface is enabled.",
+ Type: schema.TypeBool,
+ Optional: true,
+ Default: true,
+ },
+ "mtu": {
+ Description: "MTU size of the interface.",
+ Type: schema.TypeInt,
+ Optional: true,
+ },
+ "mode": {
+ Description: "Mode of the interface.",
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ "description": {
+ Description: "Description of the interface.",
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ "status": {
+ Description: "Status of the VM interface.",
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "virtual_machine_id": {
+ Description: "ID of the virtual machine to which the interface belongs.",
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "untagged_vlan_id": {
+ Description: "Untagged VLAN ID associated with the interface.",
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ "tags_ids": {
+ Description: "Tags associated with the interface.",
+ Type: schema.TypeList,
+ Optional: true,
+ Elem: &schema.Schema{
+ Type: schema.TypeString,
+ },
+ },
+ "ip_addresses": {
+ Description: "List of IP addresses to assign to the VM interface.",
+ Type: schema.TypeList,
+ Optional: true,
+ Elem: &schema.Schema{
+ Type: schema.TypeString,
+ },
+ },
+ "created": {
+ Description: "Creation date of the interface.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "last_updated": {
+ Description: "Last updated date of the interface.",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ },
+ }
+}
+
+func resourceVMInterfaceCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ c := meta.(*apiClient).Client
+ t := meta.(*apiClient).Token.token
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ // Check if the interface with the same name and virtual machine ID exists
+ interfaceName := d.Get("name").(string)
+ virtualMachineID := d.Get("virtual_machine_id").(string)
+
+ existingInterfaces, _, err := c.VirtualizationAPI.VirtualizationInterfacesList(auth).
+ Name([]string{interfaceName}).
+ VirtualMachine([]string{virtualMachineID}).
+ Execute()
+ if err != nil {
+ return diag.Errorf("failed to list VM interfaces: %s", err.Error())
+ }
+
+ if len(existingInterfaces.Results) > 0 {
+ // Interface already exists, use its ID and skip creation
+ d.SetId(existingInterfaces.Results[0].Id)
+ return resourceVMInterfaceRead(ctx, d, meta)
+ }
+
+ // Convert status name to ID
+ statusName := d.Get("status").(string)
+ statusID, err := getStatusID(ctx, c, t, statusName)
+ if err != nil {
+ return diag.Errorf("failed to get status ID for %s: %s", statusName, err.Error())
+ }
+
+ // Prepare the VMInterface request
+ var vmInterface nb.WritableVMInterfaceRequest
+ vmInterface.Name = interfaceName
+ vmInterface.Status = nb.BulkWritableCableRequestStatus{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: &statusID,
+ },
+ }
+
+ // Set optional fields
+ if v, ok := d.GetOk("mac_address"); ok {
+ vmInterface.MacAddress.Set(stringPtr(v.(string)))
+ }
+ if v, ok := d.GetOk("enabled"); ok {
+ enabled := v.(bool)
+ vmInterface.Enabled = &enabled
+ }
+ if v, ok := d.GetOk("mtu"); ok {
+ mtu := int32(v.(int))
+ vmInterface.Mtu.Set(&mtu)
+ }
+ if v, ok := d.GetOk("description"); ok {
+ desc := v.(string)
+ vmInterface.Description = &desc
+ }
+
+ // Handle virtual machine ID
+ vmInterface.VirtualMachine.Id = &nb.BulkWritableCableRequestStatusId{
+ String: &virtualMachineID,
+ }
+
+ // Create the interface
+ rsp, _, err := c.VirtualizationAPI.VirtualizationInterfacesCreate(auth).WritableVMInterfaceRequest(vmInterface).Execute()
+ if err != nil {
+ return diag.Errorf("failed to create VM interface: %s", err.Error())
+ }
+
+ // Set resource ID
+ d.SetId(rsp.Id)
+
+ // Assign IP addresses to the VM interface
+ if v, ok := d.GetOk("ip_addresses"); ok {
+ ipAddresses := v.([]interface{})
+ for _, ip := range ipAddresses {
+ err := assignIPAddressToVMInterface(ctx, c, t, ip.(string), rsp.Id)
+ if err != nil {
+ return diag.Errorf("failed to assign IP address to VM interface: %s", err.Error())
+ }
+ }
+ }
+
+ return resourceVMInterfaceRead(ctx, d, meta)
+}
+
+func resourceVMInterfaceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ c := meta.(*apiClient).Client
+ t := meta.(*apiClient).Token.token
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ // Fetch interface by ID
+ vmInterfaceId := d.Id()
+ vmInterface, _, err := c.VirtualizationAPI.VirtualizationInterfacesRetrieve(auth, vmInterfaceId).Execute()
+ if err != nil {
+ return diag.Errorf("failed to read VM interface: %s", err.Error())
+ }
+
+ // Map the retrieved data back to Terraform state
+ d.Set("name", vmInterface.Name)
+ d.Set("mac_address", vmInterface.MacAddress)
+ d.Set("enabled", vmInterface.Enabled)
+ d.Set("mtu", vmInterface.Mtu)
+ d.Set("description", vmInterface.Description)
+ d.Set("status", vmInterface.Status.Id)
+ d.Set("virtual_machine_id", vmInterface.VirtualMachine.Id)
+ d.Set("untagged_vlan_id", vmInterface.UntaggedVlan)
+ d.Set("created", vmInterface.Created)
+ d.Set("last_updated", vmInterface.LastUpdated)
+ d.Set("tags_ids", vmInterface.Tags)
+
+ // Fetch assigned IP addresses
+ assignedIPs := []string{}
+ for _, ip := range vmInterface.IpAddresses {
+ assignedIPs = append(assignedIPs, *ip.Id.String)
+ }
+ d.Set("ip_addresses", assignedIPs)
+
+ if vmInterface.Mode != nil {
+ d.Set("mode", vmInterface.Mode.Label)
+ }
+
+ return nil
+}
+
+func resourceVMInterfaceUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ c := meta.(*apiClient).Client
+ t := meta.(*apiClient).Token.token
+
+ vmInterfaceId := d.Id()
+
+ var vmInterface nb.PatchedWritableVMInterfaceRequest
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ // Update the fields that have changed
+ if d.HasChange("name") {
+ name := d.Get("name").(string)
+ vmInterface.Name = &name
+ }
+ if d.HasChange("mac_address") {
+ mac := d.Get("mac_address").(string)
+ vmInterface.MacAddress.Set(&mac)
+ }
+ if d.HasChange("enabled") {
+ enabled := d.Get("enabled").(bool)
+ vmInterface.Enabled = &enabled
+ }
+ if d.HasChange("mtu") {
+ mtu := int32(d.Get("mtu").(int))
+ vmInterface.Mtu.Set(&mtu)
+ }
+ if d.HasChange("description") {
+ description := d.Get("description").(string)
+ vmInterface.Description = &description
+ }
+ if d.HasChange("status") {
+ statusName := d.Get("status").(string)
+ statusID, err := getStatusID(ctx, c, t, statusName)
+ if err != nil {
+ return diag.Errorf("failed to get status ID for %s: %s", statusName, err.Error())
+ }
+ vmInterface.Status = &nb.BulkWritableCableRequestStatus{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: &statusID,
+ },
+ }
+ }
+ if d.HasChange("virtual_machine_id") {
+ vmID := d.Get("virtual_machine_id").(string)
+ vmInterface.VirtualMachine = &nb.BulkWritableCableRequestStatus{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: &vmID,
+ },
+ }
+ }
+
+ // Call the API to update the VM interface
+ _, _, err := c.VirtualizationAPI.VirtualizationInterfacesPartialUpdate(auth, vmInterfaceId).PatchedWritableVMInterfaceRequest(vmInterface).Execute()
+ if err != nil {
+ return diag.Errorf("failed to update VM interface: %s", err.Error())
+ }
+
+ // Update IP addresses if they have changed
+ if d.HasChange("ip_addresses") {
+ oldIPs, newIPs := d.GetChange("ip_addresses")
+
+ // Remove old IP addresses
+ for _, oldIP := range oldIPs.([]interface{}) {
+ err := removeIPAddressFromVMInterface(ctx, c, t, oldIP.(string), vmInterfaceId) // Pass vmInterfaceId here
+ if err != nil {
+ return diag.Errorf("failed to remove IP address from VM interface: %s", err.Error())
+ }
+ }
+
+ // Assign new IP addresses
+ for _, newIP := range newIPs.([]interface{}) {
+ err := assignIPAddressToVMInterface(ctx, c, t, newIP.(string), vmInterfaceId)
+ if err != nil {
+ return diag.Errorf("failed to assign IP address to VM interface: %s", err.Error())
+ }
+ }
+ }
+
+ return resourceVMInterfaceRead(ctx, d, meta)
+}
+
+func resourceVMInterfaceDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ c := meta.(*apiClient).Client
+ t := meta.(*apiClient).Token.token
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ // Delete the interface by ID
+ vmInterfaceId := d.Id()
+ _, err := c.VirtualizationAPI.VirtualizationInterfacesDestroy(auth, vmInterfaceId).Execute()
+ if err != nil {
+ return diag.Errorf("failed to delete VM interface: %s", err.Error())
+ }
+
+ // Clear the ID
+ d.SetId("")
+
+ return nil
+}
+
+// Helper function to assign an IP address to a VM interface
+func assignIPAddressToVMInterface(ctx context.Context, c *nb.APIClient, token, ipAddressID, vmInterfaceID string) error {
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: token,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ // Wrap the ipAddressID and vmInterfaceID in BulkWritableCableRequestStatusId
+ ipAddressStatusId := nb.BulkWritableCableRequestStatusId{String: &ipAddressID}
+ vmInterfaceStatusId := nb.BulkWritableCableRequestStatusId{String: &vmInterfaceID}
+
+ // Create BulkWritableCircuitRequestTenant for the VM Interface
+ vmInterfaceTenant := nb.BulkWritableCircuitRequestTenant{
+ Id: &vmInterfaceStatusId,
+ }
+
+ // Create the NullableBulkWritableCircuitRequestTenant as a value (not a pointer)
+ vmInterfaceNullableTenant := nb.NullableBulkWritableCircuitRequestTenant{}
+ vmInterfaceNullableTenant.Set(&vmInterfaceTenant)
+
+ // Prepare the request to assign the IP address to the VM interface
+ ipToInterfaceRequest := nb.IPAddressToInterfaceRequest{
+ IpAddress: nb.BulkWritableCableRequestStatus{
+ Id: &ipAddressStatusId, // Assign the IP address ID
+ },
+ // Properly set VmInterface using NullableBulkWritableCircuitRequestTenant
+ VmInterface: vmInterfaceNullableTenant, // Use the value, not a pointer
+ }
+
+ // Call the API to link the IP address with the VM interface
+ _, _, err := c.IpamAPI.IpamIpAddressToInterfaceCreate(auth).IPAddressToInterfaceRequest(ipToInterfaceRequest).Execute()
+ if err != nil {
+ return fmt.Errorf("failed to assign IP address to VM interface: %v", err)
+ }
+
+ return nil
+}
+
+// Helper function to remove an IP address from a VM interface
+func removeIPAddressFromVMInterface(ctx context.Context, c *nb.APIClient, token, ipAddressID, vmInterfaceID string) error {
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: token,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ // Retrieve the IP address object to find the related VM interface assignment
+ ipAddress, _, err := c.IpamAPI.IpamIpAddressesRetrieve(auth, ipAddressID).Execute()
+ if err != nil {
+ return fmt.Errorf("failed to retrieve IP address: %v", err)
+ }
+
+ // Look for the specific VM interface assignment in the IP address object
+ var assignmentID string
+ for _, vmInterface := range ipAddress.VmInterfaces {
+ if vmInterface.Id.String != nil && *vmInterface.Id.String == vmInterfaceID {
+ assignmentID = *vmInterface.Id.String
+ break
+ }
+ }
+
+ // If no assignment is found, return an error
+ if assignmentID == "" {
+ return fmt.Errorf("no assignment found for IP address %s and VM interface %s", ipAddressID, vmInterfaceID)
+ }
+
+ // Call IpamIpAddressToInterfaceDestroy to remove the assignment
+ _, err = c.IpamAPI.IpamIpAddressToInterfaceDestroy(auth, assignmentID).Execute()
+ if err != nil {
+ return fmt.Errorf("failed to remove IP address assignment: %v", err)
+ }
+
+ return nil
+}
diff --git a/internal/provider/resource_virtual_machine_primary_ip.go b/internal/provider/resource_virtual_machine_primary_ip.go
new file mode 100644
index 0000000..6151cdb
--- /dev/null
+++ b/internal/provider/resource_virtual_machine_primary_ip.go
@@ -0,0 +1,187 @@
+package provider
+
+import (
+ "context"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ nb "github.com/nautobot/go-nautobot/v2"
+)
+
+func resourcePrimaryIPAddressForVM() *schema.Resource {
+ return &schema.Resource{
+ Description: "This resource sets an IP address as the primary IPv4 or IPv6 for a virtual machine in Nautobot",
+
+ CreateContext: resourcePrimaryIPAddressForVMCreate,
+ ReadContext: resourcePrimaryIPAddressForVMRead,
+ UpdateContext: resourcePrimaryIPAddressForVMUpdate,
+ DeleteContext: resourcePrimaryIPAddressForVMDelete,
+
+ Schema: map[string]*schema.Schema{
+ "virtual_machine_id": {
+ Description: "ID of the virtual machine.",
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "primary_ip4_id": {
+ Description: "ID of the primary IPv4 address.",
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ "primary_ip6_id": {
+ Description: "ID of the primary IPv6 address.",
+ Type: schema.TypeString,
+ Optional: true,
+ },
+ },
+ }
+}
+
+func resourcePrimaryIPAddressForVMCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ c := meta.(*apiClient).Client
+ t := meta.(*apiClient).Token.token
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ vmID := d.Get("virtual_machine_id").(string)
+
+ // Prepare the VirtualMachineRequest to set the primary IP address
+ var vm nb.PatchedVirtualMachineRequest
+
+ if v, ok := d.GetOk("primary_ip4_id"); ok {
+ ip4 := v.(string)
+ var nullableIP4 nb.NullablePrimaryIPv4
+ primaryIPv4 := &nb.PrimaryIPv4{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: stringPtr(ip4),
+ },
+ }
+ nullableIP4.Set(primaryIPv4)
+ vm.PrimaryIp4 = nullableIP4
+ }
+
+ if v, ok := d.GetOk("primary_ip6_id"); ok {
+ ip6 := v.(string)
+ var nullableIP6 nb.NullablePrimaryIPv6
+ primaryIPv6 := &nb.PrimaryIPv6{
+ Id: &nb.BulkWritableCableRequestStatusId{
+ String: stringPtr(ip6),
+ },
+ }
+ nullableIP6.Set(primaryIPv6)
+ vm.PrimaryIp6 = nullableIP6
+ }
+
+ // Update the virtual machine with the primary IP addresses
+ _, _, err := c.VirtualizationAPI.VirtualizationVirtualMachinesPartialUpdate(auth, vmID).PatchedVirtualMachineRequest(vm).Execute()
+ if err != nil {
+ return diag.Errorf("failed to set primary IP address for virtual machine: %s", err.Error())
+ }
+
+ // Use VM ID as the resource ID
+ d.SetId(vmID)
+
+ return resourcePrimaryIPAddressForVMRead(ctx, d, meta)
+}
+
+func resourcePrimaryIPAddressForVMRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ c := meta.(*apiClient).Client
+ t := meta.(*apiClient).Token.token
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ vmID := d.Id()
+
+ // Fetch the virtual machine by ID
+ vm, _, err := c.VirtualizationAPI.VirtualizationVirtualMachinesRetrieve(auth, vmID).Execute()
+ if err != nil {
+ d.SetId("")
+ return diag.Errorf("failed to read virtual machine: %s", err.Error())
+ }
+
+ // Set primary IPs if they exist
+ if vm.PrimaryIp4.IsSet() {
+ primaryIp4 := vm.PrimaryIp4.Get()
+ if primaryIp4 != nil && primaryIp4.Id != nil {
+ d.Set("primary_ip4_id", *primaryIp4.Id.String)
+ }
+ }
+
+ if vm.PrimaryIp6.IsSet() {
+ primaryIp6 := vm.PrimaryIp6.Get()
+ if primaryIp6 != nil && primaryIp6.Id != nil {
+ d.Set("primary_ip6_id", *primaryIp6.Id.String)
+ }
+ }
+
+ d.Set("virtual_machine_id", vmID)
+
+ return nil
+}
+
+func resourcePrimaryIPAddressForVMUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ // The update function is identical to the create function since the action is the same
+ return resourcePrimaryIPAddressForVMCreate(ctx, d, meta)
+}
+
+func resourcePrimaryIPAddressForVMDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ c := meta.(*apiClient).Client
+ t := meta.(*apiClient).Token.token
+
+ // Auth context
+ auth := context.WithValue(
+ ctx,
+ nb.ContextAPIKeys,
+ map[string]nb.APIKey{
+ "tokenAuth": {
+ Key: t,
+ Prefix: "Token",
+ },
+ },
+ )
+
+ vmID := d.Id()
+
+ // Prepare the VirtualMachineRequest to remove the primary IP addresses
+ var vm nb.PatchedVirtualMachineRequest
+ var nullableIP4 nb.NullablePrimaryIPv4
+ var nullableIP6 nb.NullablePrimaryIPv6
+
+ // Set both IPs to null
+ nullableIP4.Unset()
+ nullableIP6.Unset()
+
+ vm.PrimaryIp4 = nullableIP4
+ vm.PrimaryIp6 = nullableIP6
+
+ // Update the virtual machine to unset the primary IPs
+ _, _, err := c.VirtualizationAPI.VirtualizationVirtualMachinesPartialUpdate(auth, vmID).PatchedVirtualMachineRequest(vm).Execute()
+ if err != nil {
+ return diag.Errorf("failed to remove primary IP address for virtual machine: %s", err.Error())
+ }
+
+ // Clear the ID from the state
+ d.SetId("")
+
+ return nil
+}
diff --git a/main.go b/main.go
index 0334111..3d93362 100644
--- a/main.go
+++ b/main.go
@@ -11,7 +11,7 @@ import (
// If you do not have terraform installed, you can remove the formatting command, but its suggested to
// ensure the documentation is formatted properly.
-//go:generate terraform fmt -recursive ./examples/
+//go:generate tofu fmt -recursive ./examples/
// Run the docs generation tool, check its repository for more information on how it works and how docs
// can be customized.
diff --git a/terraform-registry-manifest.json b/terraform-registry-manifest.json
index 1931b0e..fec2a56 100644
--- a/terraform-registry-manifest.json
+++ b/terraform-registry-manifest.json
@@ -1,6 +1,6 @@
{
"version": 1,
"metadata": {
- "protocol_versions": ["5.0"]
+ "protocol_versions": ["6.0"]
}
}
diff --git a/test/main.tf b/test/main.tf
index 9716e1c..8b9d715 100644
--- a/test/main.tf
+++ b/test/main.tf
@@ -8,7 +8,7 @@ terraform {
}
provider "nautobot" {
- url = "https://demo.nautobot.com/api/"
+ url = "https://demo.nautobot.com/api"
token = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
}
@@ -16,3 +16,69 @@ resource "nautobot_manufacturer" "new" {
description = "Created with Terraform"
name = "New Vendor"
}
+
+resource "nautobot_cluster_type" "new" {
+ name = "Example Cluster Type"
+ description = "This is a cluster type created via Terraform"
+}
+
+resource "nautobot_cluster" "new" {
+ name = "My New Cluster"
+ comments = "This cluster was created using Terraform."
+ cluster_type_id = nautobot_cluster_type.new.id
+
+ # Optionally add cluster group, tenant, location, etc.
+# cluster_group_id = "your-cluster-group-id"
+# tenant_id = data.nautobot_tenant.example.id # Referencing tenant data source
+# location_id = "your-location-id"
+# tags_id = ["tag1", "tag2"]
+}
+
+data "nautobot_vlan" "example" {
+ name = "pek02-106-mgmt"
+}
+
+data "nautobot_prefix" "example" {
+ depends_on = [data.nautobot_vlan.example]
+ vlan_id = data.nautobot_vlan.example.id
+}
+
+resource "nautobot_available_ip_address" "example" {
+ prefix_id = data.nautobot_prefix.example.id
+ status = "Active"
+ dns_name = "test-vm.test.com"
+}
+
+# Example virtual machine resource
+resource "nautobot_virtual_machine" "new" {
+ name = "Example VM"
+ cluster_id = nautobot_cluster.new.id
+ status = "Active"
+ vcpus = 4
+ memory = 8192 # Memory in MB (8GB)
+ disk = 100 # Disk size in GB
+ comments = "This virtual machine was created using Terraform."
+# tenant_id = "some-tenant-id" # Optional
+# platform_id = "Linux" # Optional
+# role_id = "Web Server" # Optional
+# primary_ip4_id = nautobot_available_ip_address.example.id
+# primary_ip6_id = "2001:db8::100" # Optional
+# software_version_id = "v1.0" # Optional
+
+# tags_ids = ["tag1", "tag2"] # Optional tags
+}
+
+resource "nautobot_vm_interface" "new" {
+ name = "eth0"
+ virtual_machine_id = nautobot_virtual_machine.new.id
+ status = "Active"
+ ip_addresses = [
+ nautobot_available_ip_address.example.id
+ ]
+}
+
+resource "nautobot_vm_primary_ip" "new" {
+ depends_on = [nautobot_vm_interface.new]
+ virtual_machine_id = nautobot_virtual_machine.new.id
+ primary_ip4_id = nautobot_available_ip_address.example.id
+}
\ No newline at end of file
diff --git a/test/output.tf b/test/output.tf
index cd6c8d5..999d0c1 100644
--- a/test/output.tf
+++ b/test/output.tf
@@ -1,3 +1,69 @@
+data "nautobot_cluster" "example" {
+ depends_on = [nautobot_cluster.new]
+ name = nautobot_cluster.new.name
+}
+
+output "cluster_details" {
+ value = data.nautobot_cluster.example
+}
+
+output "cluster_id" {
+ value = data.nautobot_cluster.example.id
+}
+
+data "nautobot_clusters" "example" {
+ depends_on = [nautobot_cluster.new]
+}
+
+output "clusters_details" {
+ value = data.nautobot_clusters.example.clusters[0]
+}
+
+output "clusters_id" {
+ value = data.nautobot_clusters.example.clusters[0].id
+}
+
+
+data "nautobot_cluster_type" "example" {
+ depends_on = [nautobot_cluster_type.new]
+ name = nautobot_cluster_type.new.name
+}
+
+output "cluster_type_details" {
+ value = data.nautobot_cluster_type.example
+}
+
+output "cluster_type_id" {
+ value = data.nautobot_cluster_type.example.id
+}
+
+data "nautobot_cluster_types" "example" {
+ depends_on = [nautobot_cluster_type.new]
+}
+
+output "cluster_types_details" {
+ value = data.nautobot_cluster_types.example.cluster_types[0]
+}
+
+output "cluster_types_id" {
+ value = data.nautobot_cluster_types.example.cluster_types[0].id
+}
+
+
+data "nautobot_manufacturer" "example" {
+ depends_on = [nautobot_manufacturer.new]
+ name = nautobot_manufacturer.new.name
+}
+
+output "manufacturer_details" {
+ value = data.nautobot_manufacturer.example
+}
+
+output "manufacturer_id" {
+ value = data.nautobot_manufacturer.example.id
+}
+
+
data "nautobot_manufacturers" "all" {
depends_on = [nautobot_manufacturer.new]
}
@@ -15,7 +81,45 @@ output "data_source_example" {
if manufacturer.name == var.manufacturer_name
}
}
+
+output "prefix_details" {
+ value = data.nautobot_prefix.example
+}
+
+output "prefix_id" {
+ value = data.nautobot_prefix.example.id
+}
+
+data "nautobot_prefixes" "example" {
+}
+
+output "prefixes_details" {
+ value = data.nautobot_prefixes.example.prefixes[0]
+}
+
+output "prefixes_id" {
+ value = data.nautobot_prefixes.example.prefixes[0].id
+}
+
+data "nautobot_available_ip_address" "example" {
+ prefix_id = data.nautobot_prefix.example.id
+}
+
+output "available_ip_address" {
+ value = data.nautobot_available_ip_address.example.address
+}
+
+output "available_ip_version" {
+ value = data.nautobot_available_ip_address.example.ip_version
+}
+
+output "allocated_ip" {
+ value = nautobot_available_ip_address.example.address
+}
+
+
data "nautobot_graphql" "nodes" {
+ depends_on = [nautobot_virtual_machine.new]
query = <