I would attack this with a script which works like this:
- Ingest pairs of IPs. Old first, then new.
- Log in to the management via the API.
- For each IP pair, create an object for the new IP if one doesn't exist. Save the new object's UUID. Find all existing objects with the old IP and save their UUIDs.
- For each old object, run 'where-used'.
- For each resulting access rule, if it's in the source, use the rule UUID and layer UUID to call 'set access-rule source.add' with the new object's UUID.
- Repeat for the destination, calling 'set access-rule destination.add'.
- For each resulting group, use the group UUID to call 'set group members.add' with the new object's UUID.
- After dealing with each pair, publish.
- After dealing with all pairs, log out.
NAT rules are more complex, as they can't have multiple objects in fields. I think I would look for the old object in each field, one at a time, then copy the other fields to a new NAT rule which I add to the policy package immediately below the old NAT rule. If you don't use them in NAT rules directly, then you can skip all that.
This also wouldn't handle other objects-which-reference-objects situations like Access Roles. Should get you >90% of the way there, though.