-
Notifications
You must be signed in to change notification settings - Fork 58
Support requesting a specific IP address #291
Description
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:
- Change the
ip_addressschema field fromComputed-only to
Optional + Computed + ForceNew. - In the Create function, if the user supplied
ip_address, call
p.SetIpaddress(...)before the API call. - No changes to Read/Delete —
ip_addressis 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 alocal-execprovisioner or an external
script callingcmk/ raw HTTP afterterraform apply. - The IP is created outside Terraform state, so it cannot be referenced by
other resources, is invisible toterraform plan, and will not be destroyed
onterraform destroy. - Drift detection is impossible: Terraform shows a Computed
ip_addressthat
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.