Repository: ansible/ansible-modules-extras

Issue:
vmware_guest - set static IP for virtual machine
<!--- Verify first that your issue/request is not already reported in GitHub -->
##### ISSUE TYPE

<!--- Pick one below and delete the rest: -->
- Feature Idea
##### COMPONENT NAME

<!--- Name of the plugin/module/task -->

vmware_guest
##### SUMMARY

<!--- Explain the problem briefly -->

Would like to be able to set the IP address of a VM when cloning from a template.  Is this already possible?  How are other people solving this??



Discussion:
<!-- @jbilbro: Thanks for filing this feature idea! -->

@jctanner ping, this issue is waiting for your response.
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)

<!--- boilerplate: issue_bug_new --->

Have you considered using vmware_vm_shell module (https://docs.ansible.com/ansible/vmware_vm_shell_module.html) for this purpose? If you have VMware Tools / open-vm-tools installed, you should be able to pass commands to your OS to make sure your IP configuration matches the desired state. 

I'm working on a PR for this feature now. Will update shortly.


Resolving patch:
diff --git a/cloud/vmware/vmware_guest.py b/cloud/vmware/vmware_guest.py
--- a/cloud/vmware/vmware_guest.py
+++ b/cloud/vmware/vmware_guest.py
@@ -89,6 +89,31 @@
         description:
             - The esxi hostname where the VM will run.
         required: True
+   customize:
+        description:
+           - Should customization spec be run
+        required: False
+        version_added: "2.3"
+   ips:
+        description:
+           - IP Addresses to set
+        required: False
+        version_added: "2.3"
+   networks:    
+        description:
+          - Network to use should include VM network name and gateway
+        required: False
+        version_added: "2.3"
+   dns_servers:
+        description:
+          - DNS servers to use
+        required: False
+        version_added: "2.3"
+   domain: 
+        description:
+          - Domain to use while customizing
+        required: False
+        version_added: "2.3"
 extends_documentation_fragment: vmware.documentation    
 '''
 
@@ -126,6 +151,28 @@
       register: deploy
 
 #
+# Clone Template and customize
+#
+   - name: Clone template and customize
+     vmware_guest:
+       hostname: "192.168.1.209"
+       username: "administrator@vsphere.local"
+       password: "vmware"
+       validate_certs: False
+       name: testvm-2
+       datacenter: datacenter1
+       cluster: cluster
+       validate_certs: False
+       template: template_el7
+       customize: True
+       domain: "example.com"
+       dns_servers: ['192.168.1.1','192.168.1.2']
+       ips: "192.168.1.100"
+       networks:
+         '192.168.1.0/24':
+           network: 'VM Network'
+           gateway: '192.168.1.1' 
+#
 # Gather facts only
 #
     - name: gather the VM facts
@@ -163,6 +210,7 @@
 import os
 import string
 import time
+from netaddr import IPNetwork, IPAddress
 
 from ansible.module_utils.urls import fetch_url
 
@@ -672,6 +720,101 @@ def deploy_template(self, poweron=False, wait_for_ip=False):
                 clonespec_kwargs['config'].memoryMB = \
                     int(self.params['hardware']['memory_mb'])
 
+        # lets try and assign a static ip addresss
+        if self.params['customize'] is True:
+            ip_settings = list()
+            if self.params['ips'] and self.params['network']:
+                for ip_string in self.params['ips']:
+                    ip = IPAddress(self.params['ips'])
+                    for network in self.params['networks']:
+                        if ip in IPNetwork(network):
+                            self.params['networks'][network]['ip'] = str(ip)
+                            ipnet = IPNetwork(network)
+                            self.params['networks'][network]['subnet_mask'] = str(
+                                ipnet.netmask
+                            )
+                            ip_settings.append(self.params['networks'][network])
+
+            key = 0
+            network = get_obj(self.content, [vim.Network], ip_settings[key]['network'])
+            datacenter = get_obj(self.content, [vim.Datacenter], self.params['datacenter'])
+            # get the folder where VMs are kept for this datacenter
+            destfolder = datacenter.vmFolder
+
+            cluster = get_obj(self.content, [vim.ClusterComputeResource],self.params['cluster'])
+
+            devices = []
+            adaptermaps = []
+
+            try:
+                for device in template.config.hardware.device:
+                    if hasattr(device, 'addressType'):
+                        nic = vim.vm.device.VirtualDeviceSpec()
+                        nic.operation = vim.vm.device.VirtualDeviceSpec.Operation.remove
+                        nic.device = device
+                        devices.append(nic)
+            except:
+                pass
+
+                # single device support
+            nic = vim.vm.device.VirtualDeviceSpec()
+            nic.operation = vim.vm.device.VirtualDeviceSpec.Operation.add
+            nic.device = vim.vm.device.VirtualVmxnet3()
+            nic.device.wakeOnLanEnabled = True
+            nic.device.addressType = 'assigned'
+            nic.device.deviceInfo = vim.Description()
+            nic.device.deviceInfo.label = 'Network Adapter %s' % (key + 1)
+
+            nic.device.deviceInfo.summary = ip_settings[key]['network']
+            nic.device.backing = vim.vm.device.VirtualEthernetCard.NetworkBackingInfo()
+            nic.device.backing.network = get_obj(self.content, [vim.Network], ip_settings[key]['network'])
+
+            nic.device.backing.deviceName = ip_settings[key]['network']
+            nic.device.connectable = vim.vm.device.VirtualDevice.ConnectInfo()
+            nic.device.connectable.startConnected = True
+            nic.device.connectable.allowGuestControl = True
+            devices.append(nic)
+            
+            # Update the spec with the added NIC
+            clonespec_kwargs['config'].deviceChange = devices
+
+            guest_map = vim.vm.customization.AdapterMapping()
+            guest_map.adapter = vim.vm.customization.IPSettings()
+            guest_map.adapter.ip = vim.vm.customization.FixedIp()
+            guest_map.adapter.ip.ipAddress = str(ip_settings[key]['ip'])
+            guest_map.adapter.subnetMask = str(ip_settings[key]['subnet_mask'])
+
+            try:
+                guest_map.adapter.gateway = ip_settings[key]['gateway']
+            except:
+                pass
+
+            try:
+                guest_map.adapter.dnsDomain = self.params['domain']
+            except:
+                pass
+
+            adaptermaps.append(guest_map)
+
+            # DNS settings
+            globalip = vim.vm.customization.GlobalIPSettings()
+            globalip.dnsServerList = self.params['dns_servers']
+            globalip.dnsSuffixList = str(self.params['domain'])
+
+            # Hostname settings
+            ident = vim.vm.customization.LinuxPrep()
+            ident.domain = str(self.params['domain'])
+            ident.hostName = vim.vm.customization.FixedName()
+            ident.hostName.name = self.params['name']
+
+            customspec = vim.vm.customization.Specification()
+            customspec.nicSettingMap = adaptermaps
+            customspec.globalIPSettings = globalip
+            customspec.identity = ident
+
+            clonespec = vim.vm.CloneSpec(**clonespec_kwargs)
+            clonespec.customization = customspec
+
         clonespec = vim.vm.CloneSpec(**clonespec_kwargs)
         task = template.Clone(folder=destfolder, name=self.params['name'], spec=clonespec)
         self.wait_for_task(task)
@@ -932,7 +1075,12 @@ def main():
             datacenter=dict(required=False, type='str', default=None),
             esxi_hostname=dict(required=False, type='str', default=None),
             cluster=dict(required=False, type='str', default=None),
-            wait_for_ip_address=dict(required=False, type='bool', default=True)
+            wait_for_ip_address=dict(required=False, type='bool', default=True),
+            customize=dict(required=False, type='bool', default=False),
+            ips=dict(required=False, type='str', default=None),
+            dns_servers=dict(required=False, type='list', default=None),
+            domain=dict(required=False, type='str', default=None),
+            networks=dict(required=False, type='dict', default={})
         ),
         supports_check_mode=True,
         mutually_exclusive=[],


Test patch: