Skip to content

Support requesting a specific IP address #291

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions