Skip to content

Support requesting a specific IP address #291

@synergiator

Description

@synergiator

Problem

(an existing problem, analysis by Opus 4.6R )

The cloudstack_ipaddress resource always lets CloudStack auto-select the
next free IP. There is no way to request a particular address.

This blocks a common enterprise pattern: an operator dedicates an intranet
VLAN/IP range to an account (via cloudstack_vlan_ip_range with account +
domain_id), then the tenant must acquire a specific IP from that range
into a VPC — for example because firewall rules, DNS records, or routing
tables on the corporate side are pre-provisioned for that exact address.

The CloudStack API (associateIpAddress) already accepts an optional
ipaddress parameter, and the Go SDK (cloudstack-go/v2) already exposes
AssociateIpAddressParams.SetIpaddress(string). The Terraform provider
simply never calls it.

Analysis

Layer Status
CloudStack API (associateIpAddress) ipaddress parameter supported
Go SDK v2.18.1 (AssociateIpAddressParams) SetIpaddress(string) exists
Terraform provider cloudstack_ipaddress ip_address is Computed-only — never sent to the API

The fix is small and self-contained:

  1. Change the ip_address schema field from Computed-only to
    Optional + Computed + ForceNew.
  2. In the Create function, if the user supplied ip_address, call
    p.SetIpaddress(...) before the API call.
  3. No changes to Read/Delete — ip_address is already read back from the
    API response.

What happens if not implemented

  • Users who need a deterministic IP in a VPC (intranet routing, pre-provisioned
    firewall/DNS) must fall back to a local-exec provisioner or an external
    script calling cmk / raw HTTP after terraform apply.
  • The IP is created outside Terraform state, so it cannot be referenced by
    other resources, is invisible to terraform plan, and will not be destroyed
    on terraform destroy.
  • Drift detection is impossible: Terraform shows a Computed ip_address that
    may differ from the one the post-script allocated.

Files to touch

cloudstack/resource_cloudstack_ipaddress.go   (schema + create function)
cloudstack/resource_cloudstack_ipaddress_test.go  (acceptance test for specific IP)
website/docs/r/ipaddress.html.markdown         (document the new field)

Code diffs

cloudstack/resource_cloudstack_ipaddress.go

1. Schema — make ip_address optional + force-new

                        "ip_address": {
-                               Type:     schema.TypeString,
-                               Computed: true,
+                               Type:     schema.TypeString,
+                               Optional: true,
+                               Computed: true,
+                               ForceNew: true,
                        },

Full context (lines 70-73):

// BEFORE
                        "ip_address": {
                                Type:     schema.TypeString,
                                Computed: true,
                        },

// AFTER
                        "ip_address": {
                                Type:     schema.TypeString,
                                Optional: true,
                                Computed: true,
                                ForceNew: true,
                        },

2. Create function — pass the requested IP to the API

Insert after the p.SetVpcid block (line 110) and before the zone block
(line 112):

        if vpcid, ok := d.GetOk("vpc_id"); ok {
                // Set the vpcid
                p.SetVpcid(vpcid.(string))
        }
 
+       if ipaddress, ok := d.GetOk("ip_address"); ok {
+               p.SetIpaddress(ipaddress.(string))
+       }
+
        if zone, ok := d.GetOk("zone"); ok {

Full context (lines 107-121):

// BEFORE
        if vpcid, ok := d.GetOk("vpc_id"); ok {
                // Set the vpcid
                p.SetVpcid(vpcid.(string))
        }

        if zone, ok := d.GetOk("zone"); ok {

// AFTER
        if vpcid, ok := d.GetOk("vpc_id"); ok {
                // Set the vpcid
                p.SetVpcid(vpcid.(string))
        }

        if ipaddress, ok := d.GetOk("ip_address"); ok {
                p.SetIpaddress(ipaddress.(string))
        }

        if zone, ok := d.GetOk("zone"); ok {

No changes needed in resourceCloudStackIPAddressRead — line 168 already
reads back the allocated address:

        d.Set("ip_address", ip.Ipaddress)

No changes needed in resourceCloudStackIPAddressDelete.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions