diff --git a/oslo_concurrency/processutils.py b/oslo_concurrency/processutils.py
index 4b9ead4..d26856b 100644
--- a/oslo_concurrency/processutils.py
+++ b/oslo_concurrency/processutils.py
@@ -24,41 +24,18 @@ import os
 import random
 import shlex
 import signal
+import subprocess
 import sys
 import time
-import warnings
 
 import enum
 from oslo_utils import encodeutils
-from oslo_utils import importutils
 from oslo_utils import strutils
 from oslo_utils import timeutils
 
 from oslo_concurrency._i18n import _
 
 
-if os.name == 'nt':
-    warnings.warn('Support for Windows OS is deprecated.',
-                  category=DeprecationWarning)
-
-
-eventlet = importutils.try_import('eventlet')
-if eventlet:
-    from eventlet import tpool
-
-eventlet_patched = (eventlet and
-                    eventlet.patcher.is_monkey_patched('subprocess'))
-if eventlet_patched and os.name == 'nt':
-    # subprocess.Popen.communicate will spawn two threads consuming
-    # stdout/stderr when passing data through stdin. We need to make
-    # sure that *native* threads will be used as pipes are blocking
-    # on Windows.
-    subprocess = eventlet.patcher.original('subprocess')
-    subprocess.threading = eventlet.patcher.original('threading')
-else:
-    import subprocess
-
-
 LOG = logging.getLogger(__name__)
 
 
@@ -355,16 +332,11 @@ def execute(*cmd, **kwargs):
     cmd = [str(c) for c in cmd]
 
     if prlimit:
-        if os.name == 'nt':
-            LOG.log(loglevel,
-                    _('Process resource limits are ignored as '
-                      'this feature is not supported on Windows.'))
-        else:
-            args = [python_exec, '-m', 'oslo_concurrency.prlimit']
-            args.extend(prlimit.prlimit_args())
-            args.append('--')
-            args.extend(cmd)
-            cmd = args
+        args = [python_exec, '-m', 'oslo_concurrency.prlimit']
+        args.extend(prlimit.prlimit_args())
+        args.append('--')
+        args.extend(cmd)
+        cmd = args
 
     sanitized_cmd = strutils.mask_password(' '.join(cmd))
 
@@ -377,19 +349,13 @@ def execute(*cmd, **kwargs):
             LOG.log(loglevel, _('Running cmd (subprocess): %s'), sanitized_cmd)
             _PIPE = subprocess.PIPE  # pylint: disable=E1101
 
-            if os.name == 'nt':
-                on_preexec_fn = None
-                close_fds = False
-            else:
-                on_preexec_fn = functools.partial(_subprocess_setup,
-                                                  preexec_fn)
-                close_fds = True
+            on_preexec_fn = functools.partial(_subprocess_setup, preexec_fn)
 
             obj = subprocess.Popen(cmd,
                                    stdin=_PIPE,
                                    stdout=_PIPE,
                                    stderr=_PIPE,
-                                   close_fds=close_fds,
+                                   close_fds=True,
                                    preexec_fn=on_preexec_fn,
                                    shell=shell,
                                    cwd=cwd,
@@ -399,16 +365,7 @@ def execute(*cmd, **kwargs):
                 on_execute(obj)
 
             try:
-                # eventlet.green.subprocess is not really greenthread friendly
-                # on Windows. In order to avoid blocking other greenthreads,
-                # we have to wrap this call using tpool.
-                if eventlet_patched and os.name == 'nt':
-                    result = tpool.execute(obj.communicate,
-                                           process_input,
-                                           timeout=timeout)
-                else:
-                    result = obj.communicate(process_input,
-                                             timeout=timeout)
+                result = obj.communicate(process_input, timeout=timeout)
 
                 obj.stdin.close()  # pylint: disable=E1101
                 _returncode = obj.returncode  # pylint: disable=E1101
diff --git a/oslo_concurrency/tests/unit/test_processutils.py b/oslo_concurrency/tests/unit/test_processutils.py
index b4f1493..8ff1fd9 100644
--- a/oslo_concurrency/tests/unit/test_processutils.py
+++ b/oslo_concurrency/tests/unit/test_processutils.py
@@ -117,45 +117,6 @@ class UtilsTest(test_base.BaseTestCase):
             if type(e).__name__ != 'SubprocessError':
                 raise
 
-    @mock.patch.object(os, 'name', 'nt')
-    @mock.patch.object(processutils.subprocess, "Popen")
-    @mock.patch.object(processutils, 'tpool', create=True)
-    def _test_windows_execute(self, mock_tpool, mock_popen,
-                              use_eventlet=False):
-        # We want to ensure that if eventlet is used on Windows,
-        # 'communicate' calls are wrapped with eventlet.tpool.execute.
-        mock_comm = mock_popen.return_value.communicate
-        mock_comm.return_value = None
-        mock_tpool.execute.return_value = mock_comm.return_value
-
-        fake_pinput = b'fake pinput'
-
-        with mock.patch.object(processutils, 'eventlet_patched',
-                               use_eventlet):
-            processutils.execute(
-                TRUE_UTILITY,
-                process_input=fake_pinput,
-                check_exit_code=False)
-
-        mock_popen.assert_called_once_with(
-            [TRUE_UTILITY],
-            stdin=mock.ANY, stdout=mock.ANY,
-            stderr=mock.ANY, close_fds=mock.ANY,
-            preexec_fn=mock.ANY, shell=mock.ANY,
-            cwd=mock.ANY, env=mock.ANY)
-
-        if use_eventlet:
-            mock_tpool.execute.assert_called_once_with(
-                mock_comm, fake_pinput, timeout=None)
-        else:
-            mock_comm.assert_called_once_with(fake_pinput, timeout=None)
-
-    def test_windows_execute_without_eventlet(self):
-        self._test_windows_execute()
-
-    def test_windows_execute_using_eventlet(self):
-        self._test_windows_execute(use_eventlet=True)
-
 
 class ProcessExecutionErrorTest(test_base.BaseTestCase):
 
@@ -954,28 +915,6 @@ class PrlimitTestCase(test_base.BaseTestCase):
         else:
             self.fail("ProcessExecutionError not raised")
 
-    @mock.patch.object(os, 'name', 'nt')
-    @mock.patch.object(processutils.subprocess, "Popen")
-    def test_prlimit_windows(self, mock_popen):
-        # We want to ensure that process resource limits are
-        # ignored on Windows, in which case this feature is not
-        # supported. We'll just check the command passed to Popen,
-        # which is expected to be unaltered.
-        prlimit = self.limit_address_space()
-        mock_popen.return_value.communicate.return_value = None
-
-        processutils.execute(
-            *self.SIMPLE_PROGRAM,
-            prlimit=prlimit,
-            check_exit_code=False)
-
-        mock_popen.assert_called_once_with(
-            self.SIMPLE_PROGRAM,
-            stdin=mock.ANY, stdout=mock.ANY,
-            stderr=mock.ANY, close_fds=mock.ANY,
-            preexec_fn=mock.ANY, shell=mock.ANY,
-            cwd=mock.ANY, env=mock.ANY)
-
     @mock.patch.object(processutils.subprocess, 'Popen')
     def test_python_exec(self, sub_mock):
         mock_subprocess = mock.MagicMock()
diff --git a/releasenotes/notes/remove-windows-bad63cd41c15235d.yaml b/releasenotes/notes/remove-windows-bad63cd41c15235d.yaml
new file mode 100644
index 0000000..885de06
--- /dev/null
+++ b/releasenotes/notes/remove-windows-bad63cd41c15235d.yaml
@@ -0,0 +1,4 @@
+---
+upgrade:
+  - |
+    This library no longer supports Windows operating systems.