diff --git a/plugins/modules/files/iso_create.py b/plugins/modules/files/iso_create.py
new file mode 100644
index 0000000000..bac2f8e4e1
--- /dev/null
+++ b/plugins/modules/files/iso_create.py
@@ -0,0 +1,275 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2020, Ansible Project
+# Copyright: (c) 2020, VMware, Inc. All Rights Reserved.
+# 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 = '''
+---
+module: iso_create
+short_description: Generate ISO file with specified files or folders
+description:
+    - This module is used to generate ISO file with specified path of files.
+author:
+    - Diane Wang(@Tomorrow9) <dianew@vmware.com>
+requirements: ["pycdlib", "python >= 2.7"]
+
+options:
+   src_files:
+     description:
+     - This is a list of absolute paths of source files or folders which will be contained in the new generated ISO file.
+     - Will fail if specified file or folder in C(src_files) does not exist on local machine.
+     - 'Note: With all ISO9660 levels from 1 to 3, all file names are restricted to uppercase letters, numbers and
+       underscores (_). File names are limited to 31 characters, directory nesting is limited to 8 levels, and path
+       names are limited to 255 characters.'
+     type: list
+     required: yes
+     elements: path
+   dest_iso:
+     description:
+     - The absolute path with file name of the new generated ISO file on local machine.
+     - Will create intermediate folders when they does not exist.
+     type: path
+     required: yes
+   interchange_level:
+     description:
+     - The ISO9660 interchange level to use, it dictates the rules on the names of files.
+     - Levels and valid values C(1), C(2), C(3), C(4) are supported.
+     - The default value is level C(1), which is the most conservative, level C(3) is recommended.
+     - ISO9660 file names at interchange level C(1) cannot have more than 8 characters or 3 characters in the extension.
+     type: int
+     default: 1
+     choices: [1, 2, 3, 4]
+   vol_ident:
+     description:
+     - The volume identification string to use on the new generated ISO image.
+     type: str
+   rock_ridge:
+     description:
+     - Whether to make this ISO have the Rock Ridge extensions or not.
+     - 'Valid values are C(1.09), C(1.10) or C(1.12), means adding the specified Rock Ridge version to the ISO. If
+       unsure, set C(1.09) to ensure maximum compatibility.'
+     - If not specified, then not add Rock Ridge extension to the ISO.
+     type: str
+     choices: ['1.09', '1.10', '1.12']
+   joliet:
+     description:
+     - Support levels and valid values are C(1), C(2), or C(3). Level C(3) is by far the most common.
+     - If not specified, then no Joliet support is added.
+     type: int
+     choices: [1, 2, 3]
+   udf:
+     description:
+     - Whether to add UDF support to this ISO. If set to C(True), then version 2.60 of the UDF spec is used.
+     - If not specified or set to C(False), then no UDF support is added.
+     type: bool
+     default: False
+'''
+
+EXAMPLES = r'''
+- name: Create an ISO file
+  iso_create:
+    src_files:
+      - /root/testfile.yml
+      - /root/testfolder
+    dest_iso: /tmp/test.iso
+    interchange_level: 3
+
+- name: Create an ISO file with Rock Ridge extension
+  iso_create:
+    src_files:
+      - /root/testfile.yml
+      - /root/testfolder
+    dest_iso: /tmp/test.iso
+    rock_ridge: 1.09
+'''
+
+RETURN = r'''
+source_file:
+    description: Configured source files or directories list.
+    returned: on success
+    type: list
+    elements: path
+    sample: ["/path/to/file.txt", "/path/to/folder"]
+created_iso:
+    description: Created iso file path.
+    returned: on success
+    type: str
+    sample: "/path/to/test.iso"
+interchange_level:
+    description: Configured interchange level.
+    returned: on success
+    type: int
+    sample: 3
+vol_ident:
+    description: Configured volume identification string.
+    returned: on success
+    type: str
+    sample: "OEMDRV"
+joliet:
+    description: Configured Joliet support level.
+    returned: on success
+    type: int
+    sample: 3
+rock_ridge:
+    description: Configured Rock Ridge version.
+    returned: on success
+    type: str
+    sample: "1.09"
+udf:
+    description: Configured UDF support.
+    returned: on success
+    type: bool
+    sample: False
+'''
+
+import os
+HAS_PYCDLIB = False
+try:
+    import pycdlib
+    HAS_PYCDLIB = True
+except ImportError:
+    pass
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils._text import to_native
+
+
+def add_file(module, iso_file=None, src_file=None, file_path=None, rock_ridge=None, use_joliet=None, use_udf=None):
+    rr_name = None
+    joliet_path = None
+    udf_path = None
+    # In standard ISO interchange level 1, file names have a maximum of 8 characters, followed by a required dot,
+    # followed by a maximum 3 character extension, followed by a semicolon and a version
+    file_name = os.path.basename(file_path)
+    if '.' not in file_name:
+        file_in_iso_path = file_path.upper() + '.;1'
+    else:
+        file_in_iso_path = file_path.upper() + ';1'
+    if rock_ridge:
+        rr_name = file_name
+    if use_joliet:
+        joliet_path = file_path
+    if use_udf:
+        udf_path = file_path
+    try:
+        iso_file.add_file(src_file, iso_path=file_in_iso_path, rr_name=rr_name, joliet_path=joliet_path, udf_path=udf_path)
+    except Exception as err:
+        module.fail_json(msg="Add file %s to ISO file failed due to %s" % (src_file, to_native(err)))
+
+
+def add_directory(module, iso_file=None, dir_path=None, rock_ridge=None, use_joliet=None, use_udf=None):
+    rr_name = None
+    joliet_path = None
+    udf_path = None
+    iso_dir_path = dir_path.upper()
+    if rock_ridge:
+        rr_name = os.path.basename(dir_path)
+    if use_joliet:
+        joliet_path = iso_dir_path
+    if use_udf:
+        udf_path = iso_dir_path
+    try:
+        iso_file.add_directory(iso_path=iso_dir_path, rr_name=rr_name, joliet_path=joliet_path, udf_path=udf_path)
+    except Exception as err:
+        module.fail_json(msg="Add directory %s to ISO file failed due to %s" % (dir_path, to_native(err)))
+
+
+def main():
+    argument_spec = dict(
+        src_files=dict(type='list', required=True, elements='path'),
+        dest_iso=dict(type='path', required=True),
+        interchange_level=dict(type='int', choices=[1, 2, 3, 4], default=1),
+        vol_ident=dict(type='str'),
+        rock_ridge=dict(type='str', choices=['1.09', '1.10', '1.12']),
+        joliet=dict(type='int', choices=[1, 2, 3]),
+        udf=dict(type='bool', default=False),
+    )
+    module = AnsibleModule(
+        argument_spec=argument_spec,
+        supports_check_mode=True,
+    )
+    src_file_list = module.params.get('src_files')
+    if src_file_list and len(src_file_list) == 0:
+        module.fail_json(msg='Please specify source file and/or directory list using src_files parameter.')
+    for src_file in src_file_list:
+        if not os.path.exists(src_file):
+            module.fail_json(msg="Specified source file/directory path does not exist on local machine, %s" % src_file)
+
+    dest_iso = module.params.get('dest_iso')
+    if dest_iso and len(dest_iso) == 0:
+        module.fail_json(msg='Please specify the absolute path of the new created ISO file using dest_iso parameter.')
+
+    dest_iso_dir = os.path.dirname(dest_iso)
+    if dest_iso_dir and not os.path.exists(dest_iso_dir):
+        # will create intermediate dir for new ISO file
+        try:
+            os.makedirs(dest_iso_dir)
+        except OSError as err:
+            module.fail_json(msg='Exception caught when creating folder %s, with error %s' % (dest_iso_dir, to_native(err)))
+
+    volume_id = module.params.get('vol_ident')
+    if volume_id is None:
+        volume_id = ''
+    inter_level = module.params.get('interchange_level')
+    rock_ridge = module.params.get('rock_ridge')
+    use_joliet = module.params.get('joliet')
+    use_udf = None
+    if module.params['udf']:
+        use_udf = '2.60'
+
+    result = dict(
+        changed=False,
+        source_file=src_file_list,
+        created_iso=dest_iso,
+        interchange_level=inter_level,
+        vol_ident=volume_id,
+        rock_ridge=rock_ridge,
+        joliet=use_joliet,
+        udf=use_udf
+    )
+    if not module.check_mode:
+        iso_file = pycdlib.PyCdlib()
+        iso_file.new(interchange_level=inter_level, vol_ident=volume_id, rock_ridge=rock_ridge, joliet=use_joliet, udf=use_udf)
+
+        for src_file in src_file_list:
+            # if specify a dir then go through the dir to add files and dirs
+            if os.path.isdir(src_file):
+                dir_list = []
+                file_list = []
+                src_file = src_file.rstrip('/')
+                dir_name = os.path.basename(src_file)
+                add_directory(module, iso_file=iso_file, dir_path='/' + dir_name, rock_ridge=rock_ridge,
+                              use_joliet=use_joliet, use_udf=use_udf)
+
+                # get dir list and file list
+                for path, dirs, files in os.walk(src_file):
+                    for filename in files:
+                        file_list.append(os.path.join(path, filename))
+                    for dir in dirs:
+                        dir_list.append(os.path.join(path, dir))
+                for new_dir in dir_list:
+                    add_directory(module, iso_file=iso_file, dir_path=new_dir.split(os.path.dirname(src_file))[1],
+                                  rock_ridge=rock_ridge, use_joliet=use_joliet, use_udf=use_udf)
+                for new_file in file_list:
+                    add_file(module, iso_file=iso_file, src_file=new_file,
+                             file_path=new_file.split(os.path.dirname(src_file))[1], rock_ridge=rock_ridge,
+                             use_joliet=use_joliet, use_udf=use_udf)
+            # if specify a file then add this file directly to the '/' path in ISO
+            else:
+                add_file(module, iso_file=iso_file, src_file=src_file, file_path='/' + os.path.basename(src_file),
+                         rock_ridge=rock_ridge, use_joliet=use_joliet, use_udf=use_udf)
+
+        iso_file.write(dest_iso)
+        iso_file.close()
+
+    result['changed'] = True
+    module.exit_json(**result)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/plugins/modules/iso_create.py b/plugins/modules/iso_create.py
new file mode 120000
index 0000000000..897a8d99be
--- /dev/null
+++ b/plugins/modules/iso_create.py
@@ -0,0 +1 @@
+./files/iso_create.py
\ No newline at end of file
diff --git a/tests/integration/targets/iso_create/aliases b/tests/integration/targets/iso_create/aliases
new file mode 100644
index 0000000000..9d694de923
--- /dev/null
+++ b/tests/integration/targets/iso_create/aliases
@@ -0,0 +1,4 @@
+shippable/posix/group1
+destructive
+skip/aix
+skip/python2.6
diff --git a/tests/integration/targets/iso_create/files/test1.cfg b/tests/integration/targets/iso_create/files/test1.cfg
new file mode 100644
index 0000000000..1c6d0d0d86
--- /dev/null
+++ b/tests/integration/targets/iso_create/files/test1.cfg
@@ -0,0 +1,56 @@
+#version=DEVEL
+# System authorization information
+auth --enableshadow --passalgo=sha512
+# Use CDROM installation media
+cdrom
+# Use graphical install
+graphical
+# Run the Setup Agent on first boot
+firstboot --enable
+ignoredisk --only-use=sda
+# Keyboard layouts
+keyboard --vckeymap=us --xlayouts='us'
+# System language
+lang en_US.UTF-8
+# Network information
+network  --bootproto=dhcp --device=ens192 --ipv6=auto --no-activate
+network  --hostname=localhost.localdomain
+# System services
+services --enabled="chronyd"
+# System timezone
+timezone America/New_York --isUtc
+# X Window System configuration information
+xconfig  --startxonboot
+# System bootloader configuration
+bootloader --append=" crashkernel=auto" --location=mbr --boot-drive=sda
+autopart --type=lvm
+# Partition clearing information
+clearpart --none --initlabel
+#firewall --disable
+services --disabled=firewalld
+eula --agreed
+# Reboot when the install is finished.
+reboot
+
+%packages
+@^graphical-server-environment
+@base
+@core
+@desktop-debugging
+@dial-up
+@fonts
+@gnome-desktop
+@guest-agents
+@guest-desktop-agents
+@hardware-monitoring
+@input-methods
+@internet-browser
+@multimedia
+@print-client
+@x11
+chrony
+kexec-tools
+open-vm-tools-desktop
+%end
+%addon com_redhat_kdump --enable --reserve-mb='auto'
+%end
diff --git a/tests/integration/targets/iso_create/files/test_dir/test2.cfg b/tests/integration/targets/iso_create/files/test_dir/test2.cfg
new file mode 100644
index 0000000000..1c6d0d0d86
--- /dev/null
+++ b/tests/integration/targets/iso_create/files/test_dir/test2.cfg
@@ -0,0 +1,56 @@
+#version=DEVEL
+# System authorization information
+auth --enableshadow --passalgo=sha512
+# Use CDROM installation media
+cdrom
+# Use graphical install
+graphical
+# Run the Setup Agent on first boot
+firstboot --enable
+ignoredisk --only-use=sda
+# Keyboard layouts
+keyboard --vckeymap=us --xlayouts='us'
+# System language
+lang en_US.UTF-8
+# Network information
+network  --bootproto=dhcp --device=ens192 --ipv6=auto --no-activate
+network  --hostname=localhost.localdomain
+# System services
+services --enabled="chronyd"
+# System timezone
+timezone America/New_York --isUtc
+# X Window System configuration information
+xconfig  --startxonboot
+# System bootloader configuration
+bootloader --append=" crashkernel=auto" --location=mbr --boot-drive=sda
+autopart --type=lvm
+# Partition clearing information
+clearpart --none --initlabel
+#firewall --disable
+services --disabled=firewalld
+eula --agreed
+# Reboot when the install is finished.
+reboot
+
+%packages
+@^graphical-server-environment
+@base
+@core
+@desktop-debugging
+@dial-up
+@fonts
+@gnome-desktop
+@guest-agents
+@guest-desktop-agents
+@hardware-monitoring
+@input-methods
+@internet-browser
+@multimedia
+@print-client
+@x11
+chrony
+kexec-tools
+open-vm-tools-desktop
+%end
+%addon com_redhat_kdump --enable --reserve-mb='auto'
+%end
diff --git a/tests/integration/targets/iso_create/meta/main.yml b/tests/integration/targets/iso_create/meta/main.yml
new file mode 100644
index 0000000000..ca521ab1ef
--- /dev/null
+++ b/tests/integration/targets/iso_create/meta/main.yml
@@ -0,0 +1,3 @@
+dependencies:
+  - setup_pkg_mgr
+  - prepare_tests
diff --git a/tests/integration/targets/iso_create/tasks/main.yml b/tests/integration/targets/iso_create/tasks/main.yml
new file mode 100644
index 0000000000..20e42fb4d3
--- /dev/null
+++ b/tests/integration/targets/iso_create/tasks/main.yml
@@ -0,0 +1,149 @@
+# Test code for iso_create module
+# Copyright: (c) 2020, Diane Wang (Tomorrow9) <dianew@vmware.com>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+- name: install pycdlib
+  pip:
+    name: pycdlib
+    # state: latest
+  register: install_pycdlib
+- debug: var=install_pycdlib
+
+- set_fact:
+    output_dir_test: '{{ output_dir }}/test_iso_create'
+
+# - include_tasks: prepare_dest_dir.yml
+
+- name: Test check mode
+  iso_create:
+    src_files:
+      - "{{ role_path }}/files/test1.cfg"
+    dest_iso: "{{ output_dir_test }}/test.iso"
+    interchange_level: 3
+  register: iso_result
+  check_mode: yes
+- debug: var=iso_result
+
+- name: Check if iso file created
+  stat:
+    path: "{{ output_dir_test }}/test.iso"
+  register: iso_file
+- debug: var=iso_file
+- assert:
+    that:
+    - iso_result.changed == True
+    - iso_file.stat.exists == False
+
+- name: Create iso file with a specified file
+  iso_create:
+    src_files:
+      - "{{ role_path }}/files/test1.cfg"
+    dest_iso: "{{ output_dir_test }}/test.iso"
+    interchange_level: 3
+  register: iso_result
+- debug: var=iso_result
+
+- name: Check if iso file created
+  stat:
+    path: "{{ output_dir_test }}/test.iso"
+  register: iso_file
+
+- assert:
+    that:
+    - iso_result.changed == True
+    - iso_file.stat.exists == True
+
+- name: Create iso file with a specified file and folder
+  iso_create:
+    src_files:
+      - "{{ role_path }}/files/test1.cfg"
+      - "{{ role_path }}/files/test_dir"
+    dest_iso: "{{ output_dir_test }}/test1.iso"
+    interchange_level: 3
+  register: iso_result
+- debug: var=iso_result
+
+- name: Check if iso file created
+  stat:
+    path: "{{ output_dir_test }}/test1.iso"
+  register: iso_file
+
+- assert:
+    that:
+    - iso_result.changed == True
+    - iso_file.stat.exists == True
+
+- name: Create iso file with volume identification string 
+  iso_create:
+    src_files:
+      - "{{ role_path }}/files/test1.cfg"
+    dest_iso: "{{ output_dir_test }}/test2.iso"
+    vol_ident: "OEMDRV"
+  register: iso_result
+- debug: var=iso_result
+
+- name: Check if iso file created
+  stat:
+    path: "{{ output_dir_test }}/test2.iso"
+  register: iso_file
+
+- assert:
+    that:
+    - iso_result.changed == True
+    - iso_file.stat.exists == True
+
+- name: Create iso file with Rock Ridge extention
+  iso_create:
+    src_files:
+      - "{{ role_path }}/files/test1.cfg"
+    dest_iso: "{{ output_dir_test }}/test3.iso"
+    rock_ridge: "1.09"
+  register: iso_result
+- debug: var=iso_result
+
+- name: Check if iso file created
+  stat:
+    path: "{{ output_dir_test }}/test3.iso"
+  register: iso_file
+
+- assert:
+    that:
+    - iso_result.changed == True
+    - iso_file.stat.exists == True
+
+- name: Create iso file with Joliet extention
+  iso_create:
+    src_files:
+      - "{{ role_path }}/files/test1.cfg"
+    dest_iso: "{{ output_dir_test }}/test4.iso"
+    joliet: 3
+  register: iso_result
+- debug: var=iso_result
+
+- name: Check if iso file created
+  stat:
+    path: "{{ output_dir_test }}/test4.iso"
+  register: iso_file
+
+- assert:
+    that:
+    - iso_result.changed == True
+    - iso_file.stat.exists == True
+
+- name: Create iso file with UDF enabled
+  iso_create:
+    src_files:
+      - "{{ role_path }}/files/test1.cfg"
+    dest_iso: "{{ output_dir_test }}/test5.iso"
+    udf: True
+  register: iso_result
+- debug: var=iso_result
+
+- name: Check if iso file created
+  stat:
+    path: "{{ output_dir_test }}/test5.iso"
+  register: iso_file
+
+- assert:
+    that:
+    - iso_result.changed == True
+    - iso_file.stat.exists == True
diff --git a/tests/integration/targets/iso_create/tasks/prepare_dest_dir.yml b/tests/integration/targets/iso_create/tasks/prepare_dest_dir.yml
new file mode 100644
index 0000000000..94c529d52a
--- /dev/null
+++ b/tests/integration/targets/iso_create/tasks/prepare_dest_dir.yml
@@ -0,0 +1,12 @@
+# Test code for iso_create module
+# Copyright: (c) 2020, Diane Wang (Tomorrow9) <dianew@vmware.com>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+- name: Make sure our testing sub-directory does not exist
+  file:
+    path: '{{ output_dir_test }}'
+    state: absent
+
+- name: Create our testing sub-directory
+  file:
+    path: '{{ output_dir_test }}'
+    state: directory