diff --git a/lib/ansible/modules/windows/win_format.ps1 b/lib/ansible/modules/windows/win_format.ps1
new file mode 100644
index 00000000000..6302ad8754c
--- /dev/null
+++ b/lib/ansible/modules/windows/win_format.ps1
@@ -0,0 +1,184 @@
+#!powershell
+
+# Copyright: (c) 2019, Varun Chopra (@chopraaa) <v@chopraaa.com>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+#AnsibleRequires -CSharpUtil Ansible.Basic
+#AnsibleRequires -OSVersion 6.2
+
+Set-StrictMode -Version 2
+
+$ErrorActionPreference = "Stop"
+
+$spec = @{
+    options = @{
+        drive_letter = @{ type = "str" }
+        path = @{ type = "str" }
+        label = @{ type = "str" }
+        new_label = @{ type = "str" }
+        file_system = @{ type = "str"; choices = "ntfs", "refs", "exfat", "fat32", "fat" }
+        allocation_unit_size = @{ type = "int" }
+        large_frs = @{ type = "bool" }
+        full = @{ type = "bool"; default = $false }
+        compress = @{ type = "bool" }
+        integrity_streams = @{ type = "bool" }
+        force = @{ type = "bool"; default = $false }
+    }
+    mutually_exclusive = @(
+        ,@('drive_letter', 'path', 'label')
+    )
+    required_one_of = @(
+        ,@('drive_letter', 'path', 'label')
+    )
+    supports_check_mode = $true
+}
+
+$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
+
+$drive_letter = $module.Params.drive_letter
+$path = $module.Params.path
+$label = $module.Params.label
+$new_label = $module.Params.new_label
+$file_system = $module.Params.file_system
+$allocation_unit_size = $module.Params.allocation_unit_size
+$large_frs = $module.Params.large_frs
+$full_format = $module.Params.full
+$compress_volume = $module.Params.compress
+$integrity_streams = $module.Params.integrity_streams
+$force_format = $module.Params.force
+
+# Some pre-checks
+if ($null -ne $drive_letter -and $drive_letter -notmatch "^[a-zA-Z]$") {
+    $module.FailJson("The parameter drive_letter should be a single character A-Z")
+}
+if ($integrity_streams -eq $true -and $file_system -ne "refs") {
+    $module.FailJson("Integrity streams can be enabled only on ReFS volumes. You specified: $($file_system)")
+}
+if ($compress_volume -eq $true) {
+    if ($file_system -eq "ntfs") {
+        if ($null -ne $allocation_unit_size -and $allocation_unit_size -gt 4096) {
+            $module.FailJson("NTFS compression is not supported for allocation unit sizes above 4096")
+        }
+    }
+    else {
+        $module.FailJson("Compression can be enabled only on NTFS volumes. You specified: $($file_system)")
+    }
+}
+
+function Get-AnsibleVolume {
+    param(
+        $DriveLetter,
+        $Path,
+        $Label
+    )
+
+    if ($null -ne $DriveLetter) {
+        try {
+            $volume = Get-Volume -DriveLetter $DriveLetter
+        } catch {
+            $module.FailJson("There was an error retrieving the volume using drive_letter $($DriveLetter): $($_.Exception.Message)", $_)
+        }
+    }
+    elseif ($null -ne $Path) {
+        try {
+            $volume = Get-Volume -Path $Path
+        } catch {
+            $module.FailJson("There was an error retrieving the volume using path $($Path): $($_.Exception.Message)", $_)
+        }
+    }
+    elseif ($null -ne $Label) {
+        try {
+            $volume = Get-Volume -FileSystemLabel $Label
+        } catch {
+            $module.FailJson("There was an error retrieving the volume using label $($Label): $($_.Exception.Message)", $_)
+        }
+    }
+    else {
+        $module.FailJson("Unable to locate volume: drive_letter, path and label were not specified")
+    }
+
+    return $volume
+}
+
+function Format-AnsibleVolume {
+    param(
+        $Path,
+        $Label,
+        $FileSystem,
+        $Full,
+        $UseLargeFRS,
+        $Compress,
+        $SetIntegrityStreams
+    )
+    $parameters = @{
+        Path = $Path
+        Full = $Full
+    }
+    if ($null -ne $UseLargeFRS) {
+        $parameters.Add("UseLargeFRS", $UseLargeFRS)
+    }
+    if ($null -ne $SetIntegrityStreams) {
+        $parameters.Add("SetIntegrityStreams", $SetIntegrityStreams)
+    }
+    if ($null -ne $Compress){
+        $parameters.Add("Compress", $Compress)
+    }
+    if ($null -ne $Label) {
+        $parameters.Add("NewFileSystemLabel", $Label)
+    }
+    if ($null -ne $FileSystem) {
+        $parameters.Add("FileSystem", $FileSystem)
+    }
+
+    Format-Volume @parameters -Confirm:$false | Out-Null
+
+}
+
+$ansible_volume = Get-AnsibleVolume -DriveLetter $drive_letter -Path $path -Label $label
+$ansible_file_system = $ansible_volume.FileSystem
+$ansible_volume_size = $ansible_volume.Size
+
+$ansible_partition = Get-Partition -Volume $ansible_volume
+
+foreach ($access_path in $ansible_partition.AccessPaths) {
+    if ($access_path -ne $Path) {
+        $files_in_volume = (Get-ChildItem -LiteralPath $access_path -ErrorAction SilentlyContinue | Measure-Object).Count
+
+        if (-not $force_format -and $files_in_volume -gt 0) {
+            $module.FailJson("Force format must be specified to format non-pristine volumes")
+        } else {
+            if (-not $force_format -and
+                -not $null -eq $file_system -and
+                -not [string]::IsNullOrEmpty($ansible_file_system) -and
+                $file_system -ne $ansible_file_system) {
+                $module.FailJson("Force format must be specified since target file system: $($file_system) is different from the current file system of the volume: $($ansible_file_system.ToLower())")
+            } else {
+                $pristine = $true
+            }
+        }
+    }
+}
+
+if ($force_format) {
+    if (-not $module.CheckMode) {
+        Format-AnsibleVolume -Path $ansible_volume.Path -Full $full_format -Label $new_label -FileSystem $file_system -SetIntegrityStreams $integrity_streams -UseLargeFRS $large_frs -Compress $compress_volume
+    }
+    $module.Result.changed = $true
+}
+else {
+    if ($pristine) {
+        if ($null -eq $new_label) {
+            $new_label = $ansible_volume.FileSystemLabel
+        }
+        # Conditions for formatting
+        if ($ansible_volume_size -eq 0 -or
+            $ansible_volume.FileSystemLabel -ne $new_label) {
+            if (-not $module.CheckMode) {
+                Format-AnsibleVolume -Path $ansible_volume.Path -Full $full_format -Label $new_label -FileSystem $file_system -SetIntegrityStreams $integrity_streams -UseLargeFRS $large_frs -Compress $compress_volume
+            }
+            $module.Result.changed = $true
+        }
+    }
+}
+
+$module.ExitJson()
diff --git a/lib/ansible/modules/windows/win_format.py b/lib/ansible/modules/windows/win_format.py
new file mode 100644
index 00000000000..8b85cce8131
--- /dev/null
+++ b/lib/ansible/modules/windows/win_format.py
@@ -0,0 +1,100 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2019, Varun Chopra (@chopraaa) <v@chopraaa.com>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+ANSIBLE_METADATA = {
+    'metadata_version': '1.1',
+    'status': ['preview'],
+    'supported_by': 'community'
+}
+
+DOCUMENTATION = r'''
+module: win_format
+version_added: '2.8'
+short_description: Formats an existing volume or a new volume on an existing partition on Windows
+description:
+  - The M(win_format) module formats an existing volume or a new volume on an existing partition on Windows
+options:
+  drive_letter:
+    description:
+      - Used to specify the drive letter of the volume to be formatted.
+    type: str
+  path:
+    description:
+      - Used to specify the path to the volume to be formatted.
+    type: str
+  label:
+    description:
+      - Used to specify the label of the volume to be formatted.
+    type: str
+  new_label:
+    description:
+      - Used to specify the new file system label of the formatted volume.
+    type: str
+  file_system:
+    description:
+      - Used to specify the file system to be used when formatting the target volume.
+    type: str
+    choices: [ ntfs, refs, exfat, fat32, fat ]
+  allocation_unit_size:
+    description:
+      - Specifies the cluster size to use when formatting the volume.
+      - If no cluster size is specified when you format a partition, defaults are selected based on
+        the size of the partition.
+    type: int
+  large_frs:
+    description:
+      - Specifies that large File Record System (FRS) should be used.
+    type: bool
+  compress:
+    description:
+      - Enable compression on the resulting NTFS volume.
+      - NTFS compression is not supported where I(allocation_unit_size) is more than 4096.
+    type: bool
+  integrity_streams:
+    description:
+      - Enable integrity streams on the resulting ReFS volume.
+    type: bool
+  full:
+    description:
+      - A full format writes to every sector of the disk, takes much longer to perform than the
+        default (quick) format, and is not recommended on storage that is thinly provisioned.
+      - Specify C(true) for full format.
+    type: bool
+  force:
+    description:
+      - Specify if formatting should be forced for volumes that are not created from new partitions
+        or if the source and target file system are different.
+    type: bool
+notes:
+  - One of three parameters (I(drive_letter), I(path) and I(label)) are mandatory to identify the target
+    volume but more than one cannot be specified at the same time.
+  - This module is idempotent if I(force) is not specified and file system labels remain preserved.
+  - For more information, see U(https://docs.microsoft.com/en-us/previous-versions/windows/desktop/stormgmt/format-msft-volume)
+seealso:
+  - module: win_disk_facts
+  - module: win_partition
+author:
+  - Varun Chopra (@chopraaa) <v@chopraaa.com>
+'''
+
+EXAMPLES = r'''
+- name: Create a partition with drive letter D and size 5 GiB
+  win_partition:
+    drive_letter: D
+    partition_size: 5 GiB
+    disk_number: 1
+
+- name: Full format the newly created partition as NTFS and label it
+  win_format:
+    drive_letter: D
+    file_system: NTFS
+    new_label: Formatted
+    full: True
+'''
+
+RETURN = r'''
+#
+'''
diff --git a/test/integration/targets/win_format/aliases b/test/integration/targets/win_format/aliases
new file mode 100644
index 00000000000..3aa71f86abd
--- /dev/null
+++ b/test/integration/targets/win_format/aliases
@@ -0,0 +1,3 @@
+shippable/windows/group4
+skip/windows/2008
+skip/windows/2008-R2
diff --git a/test/integration/targets/win_format/meta/main.yml b/test/integration/targets/win_format/meta/main.yml
new file mode 100644
index 00000000000..9f37e96cd90
--- /dev/null
+++ b/test/integration/targets/win_format/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+- setup_remote_tmp_dir
diff --git a/test/integration/targets/win_format/tasks/main.yml b/test/integration/targets/win_format/tasks/main.yml
new file mode 100644
index 00000000000..de773469df2
--- /dev/null
+++ b/test/integration/targets/win_format/tasks/main.yml
@@ -0,0 +1,7 @@
+---
+- name: Check if Format-Volume is supported
+  win_shell: if (Get-Command -Name Format-Volume -ErrorAction SilentlyContinue) { $true } else { $false }
+  register: module_present
+
+- include: pre_test.yml
+  when: module_present.stdout | trim | bool
diff --git a/test/integration/targets/win_format/tasks/pre_test.yml b/test/integration/targets/win_format/tasks/pre_test.yml
new file mode 100644
index 00000000000..edc59ae52c5
--- /dev/null
+++ b/test/integration/targets/win_format/tasks/pre_test.yml
@@ -0,0 +1,21 @@
+---
+- set_fact:
+    AnsibleVhdx: '{{ remote_tmp_dir }}\AnsiblePart.vhdx'
+
+- name: Copy VHDX scripts
+  win_template:
+    src: "{{ item.src }}"
+    dest: '{{ remote_tmp_dir }}\{{ item.dest }}'
+  loop:
+    - { src: partition_creation_script.j2, dest: partition_creation_script.txt }
+    - { src: partition_deletion_script.j2, dest: partition_deletion_script.txt }
+
+- name: Create partition
+  win_command: diskpart.exe /s {{ remote_tmp_dir }}\partition_creation_script.txt
+
+- name: Run tests
+  block:
+    - include: tests.yml
+  always:
+    - name: Detach disk
+      win_command: diskpart.exe /s {{ remote_tmp_dir }}\partition_deletion_script.txt
diff --git a/test/integration/targets/win_format/tasks/tests.yml b/test/integration/targets/win_format/tasks/tests.yml
new file mode 100644
index 00000000000..1db4576cfef
--- /dev/null
+++ b/test/integration/targets/win_format/tasks/tests.yml
@@ -0,0 +1,138 @@
+---
+- win_shell: $AnsiPart = Get-Partition -DriveLetter T; $AnsiVol = Get-Volume -DriveLetter T; "$($AnsiPart.Size),$($AnsiVol.Size)"
+  register: shell_result
+
+- name: Assert volume size is 0 for pristine volume
+  assert:
+    that:
+      - shell_result.stdout | trim == "2096037888,0"
+
+- name: Get partition access path
+  win_shell: (Get-Partition -DriveLetter T).AccessPaths[1]
+  register: shell_partition_result
+
+- name: Try to format using mutually exclusive parameters
+  win_format:
+    drive_letter: T
+    path: "{{ shell_partition_result.stdout | trim }}"
+  register: format_mutex_result
+  ignore_errors: True
+
+- assert:
+    that:
+      - format_mutex_result is failed
+      - 'format_mutex_result.msg == "parameters are mutually exclusive: drive_letter, path, label"'
+
+- name: Fully format volume and assign label (check)
+  win_format:
+    drive_letter: T
+    new_label: Formatted
+    full: True
+  register: format_result_check
+  check_mode: True
+
+- win_shell: $AnsiPart = Get-Partition -DriveLetter T; $AnsiVol = Get-Volume -DriveLetter T; "$($AnsiPart.Size),$($AnsiVol.Size),$($AnsiVol.FileSystemLabel)"
+  register: formatted_value_result_check
+
+- name: Fully format volume and assign label
+  win_format:
+    drive_letter: T
+    new_label: Formatted
+    full: True
+  register: format_result
+
+- win_shell: $AnsiPart = Get-Partition -DriveLetter T; $AnsiVol = Get-Volume -DriveLetter T; "$($AnsiPart.Size),$($AnsiVol.Size),$($AnsiVol.FileSystemLabel)"
+  register: formatted_value_result
+
+- assert:
+    that:
+      - format_result_check is changed
+      - format_result is changed
+      - formatted_value_result_check.stdout | trim == "2096037888,0,"
+      - formatted_value_result.stdout | trim == "2096037888,2096033792,Formatted"
+
+- name: Format NTFS volume with integrity streams enabled
+  win_format:
+    path: "{{ shell_partition_result.stdout | trim }}"
+    file_system: ntfs
+    integrity_streams: True
+  ignore_errors: True
+  register: ntfs_integrity_streams
+
+- assert:
+    that:
+      - ntfs_integrity_streams is failed
+      - 'ntfs_integrity_streams.msg == "Integrity streams can be enabled only on ReFS volumes. You specified: ntfs"'
+
+- name: Format volume (require force_format for specifying different file system)
+  win_format:
+    path: "{{ shell_partition_result.stdout | trim }}"
+    file_system: fat32
+  ignore_errors: True
+  register: require_force_format
+
+- assert:
+    that:
+      - require_force_format is failed
+      - 'require_force_format.msg == "Force format must be specified since target file system: fat32 is different from the current file system of the volume: ntfs"'
+
+- name: Format volume (forced) (check)
+  win_format:
+    path: "{{ shell_partition_result.stdout | trim }}"
+    file_system: refs
+    force: True
+  check_mode: True
+  ignore_errors: True
+  register: not_pristine_forced_check
+
+- name: Format volume (forced)
+  win_format:
+    path: "{{ shell_partition_result.stdout | trim }}"
+    file_system: refs
+    force: True
+  register: not_pristine_forced
+
+- name: Format volume (forced) (idempotence will not work)
+  win_format:
+    path: "{{ shell_partition_result.stdout | trim }}"
+    file_system: refs
+    force: True
+  register: not_pristine_forced_idem_fails
+
+- name: Format volume (idempotence)
+  win_format:
+    path: "{{ shell_partition_result.stdout | trim }}"
+    file_system: refs
+  register: not_pristine_forced_idem
+
+- assert:
+    that:
+      - not_pristine_forced_check is changed
+      - not_pristine_forced is changed
+      - not_pristine_forced_idem_fails is changed
+      - not_pristine_forced_idem is not changed
+
+- name: Add a file
+  win_file:
+    path: T:\path\to\directory
+    state: directory
+  register: add_file_to_volume
+
+- name: Format volume with file inside without force
+  win_format:
+    path: "{{ shell_partition_result.stdout | trim }}"
+  register: format_volume_without_force
+  ignore_errors: True
+
+- name: Format volume with file inside with force
+  win_format:
+    path: "{{ shell_partition_result.stdout | trim }}"
+    force: True
+  register: format_volume_with_force
+
+- assert:
+    that:
+      - add_file_to_volume is changed
+      - format_volume_without_force is failed
+      - 'format_volume_without_force.msg == "Force format must be specified to format non-pristine volumes"'
+      - format_volume_with_force is changed
diff --git a/test/integration/targets/win_format/templates/partition_creation_script.j2 b/test/integration/targets/win_format/templates/partition_creation_script.j2
new file mode 100644
index 00000000000..8e47fda95ba
--- /dev/null
+++ b/test/integration/targets/win_format/templates/partition_creation_script.j2
@@ -0,0 +1,11 @@
+create vdisk file="{{ AnsibleVhdx }}" maximum=2000 type=fixed
+
+select vdisk file="{{ AnsibleVhdx }}"
+
+attach vdisk
+
+convert mbr
+
+create partition primary
+
+assign letter="T"
diff --git a/test/integration/targets/win_format/templates/partition_deletion_script.j2 b/test/integration/targets/win_format/templates/partition_deletion_script.j2
new file mode 100644
index 00000000000..c2be9cd1446
--- /dev/null
+++ b/test/integration/targets/win_format/templates/partition_deletion_script.j2
@@ -0,0 +1,3 @@
+select vdisk file="{{ AnsibleVhdx }}"
+
+detach vdisk