diff --git a/lib/ansible/plugins/inventory/nmap.py b/lib/ansible/plugins/inventory/nmap.py
new file mode 100644
index 00000000000..90bdf7f20d0
--- /dev/null
+++ b/lib/ansible/plugins/inventory/nmap.py
@@ -0,0 +1,158 @@
+# Copyright (c) 2017 Ansible Project
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+DOCUMENTATION = '''
+    name: nmap
+    plugin_type: inventory
+    version_added: "2.5"
+    short_description: Uses nmap to find hosts to target
+    description:
+        - Uses a YAML configuration file with a valid YAML extension.
+    extends_documentation_fragment:
+      - constructed
+      - inventory_cache
+    requirements:
+      - nmap CLI installed
+    options:
+        address:
+            description: Network IP or range of IPs to scan, you can use a simple range (10.2.2.15-25) or CIDR notation.
+            required: True
+        exclude:
+            description: list of addresses to exclude
+            type: list
+        ports:
+            description: Enable/disable scanning for open ports
+            type: boolean
+            default: True
+        ipv4:
+            description: use IPv4 type addresses
+            type: boolean
+            default: True
+        ipv6:
+            description: use IPv6 type addresses
+            type: boolean
+            default: True
+    notes:
+        - At least one of ipv4 or ipv6 is required to be True, both can be True, but they cannot both be False.
+        - 'TODO: add OS fingerprinting'
+'''
+EXAMPLES = '''
+    # inventory.config file in YAML format
+    plugin: nmap
+    strict: False
+    network: 192.168.0.0/24
+'''
+
+import os
+import re
+
+from subprocess import Popen, PIPE
+
+from ansible import constants as C
+from ansible.errors import AnsibleParserError
+from ansible.module_utils._text import to_native
+from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable
+
+
+class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
+
+    NAME = 'nmap'
+    find_host = re.compile(r'^Nmap scan report for ([\w,.,-]+) \(([\w,.,:,\[,\]]+)\)')
+    find_port = re.compile(r'^(\d+)/(\w+)\s+(\w+)\s+(\w+)')
+
+    def __init__(self):
+
+        self._nmap = None
+        for path in os.environ.get('PATH').split(':'):
+            candidate = os.path.join(path, 'nmap')
+            if os.path.exists(candidate):
+                self._nmap = candidate
+                break
+
+        super(InventoryModule, self).__init__()
+
+    def verify_file(self, path):
+
+        valid = False
+        if super(InventoryModule, self).verify_file(path):
+            file_name, ext = os.path.splitext(path)
+
+            if not ext or ext in C.YAML_FILENAME_EXTENSIONS:
+                valid = True
+
+        return valid
+
+    def parse(self, inventory, loader, path, cache=False):
+
+        if self._nmap is None:
+            raise AnsibleParserError('nmap inventory plugin requires the nmap cli tool to work')
+
+        super(InventoryModule, self).parse(inventory, loader, path, cache=cache)
+
+        self._read_config_data(path)
+
+        # setup command
+        cmd = [self._nmap]
+        if not self._options['ports']:
+            cmd.append('-sP')
+
+        if self._options['ipv4'] and not self._options['ipv6']:
+            cmd.append('-4')
+        elif self._options['ipv6'] and not self._options['ipv4']:
+            cmd.append('-6')
+        elif not self._options['ipv6'] and not self._options['ipv4']:
+            raise AnsibleParserError('One of ipv4 or ipv6 must be enabled for this plugin')
+
+        if self._options['exclude']:
+            cmd.append('--exclude')
+            cmd.append(','.join(self._options['exclude']))
+
+        cmd.append(self._options['address'])
+        try:
+            # execute
+            p = Popen(cmd, stdout=PIPE, stderr=PIPE)
+            stdout, stderr = p.communicate()
+            if p.returncode != 0:
+                raise AnsibleParserError('Failed to run nmap, rc=%s: %s' % (p.returncode, to_native(stderr)))
+
+            # parse results
+            host = None
+            ip = None
+            ports = []
+            for line in stdout.splitlines():
+                hits = self.find_host.match(line)
+                if hits:
+                    if host is not None:
+                        self.inventory.set_variable(host, 'ports', ports)
+
+                    # if dns only shows arpa, just use ip instead as hostname
+                    if hits.group(1).endswith('.in-addr.arpa'):
+                        host = hits.group(2)
+                    else:
+                        host = hits.group(1)
+
+                    ip = hits.group(2)
+
+                    if host is not None:
+                        # update inventory
+                        self.inventory.add_host(host)
+                        self.inventory.set_variable(host, 'ip', ip)
+                        ports = []
+                    continue
+
+                host_ports = self.find_port.match(line)
+                if host is not None and host_ports:
+                    ports.append({'port': host_ports.group(1), 'protocol': host_ports.group(2), 'state': host_ports.group(3), 'service': host_ports.group(4)})
+                    continue
+
+                # TODO: parse more data, OS?
+
+            # if any lefotvers
+            if host and ports:
+                self.inventory.set_variable(host, 'ports', ports)
+
+        except Exception as e:
+            raise AnsibleParserError("failed to parse %s: %s " % (to_native(path), to_native(e)))