diff --git a/openstackclient/common/envvars.py b/openstackclient/common/envvars.py
new file mode 100644
index 0000000000..5f702ff3d4
--- /dev/null
+++ b/openstackclient/common/envvars.py
@@ -0,0 +1,57 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import os
+
+from openstackclient.i18n import _
+
+
+def bool_from_str(value, strict=False):
+    true_strings = ('1', 't', 'true', 'on', 'y', 'yes')
+    false_strings = ('0', 'f', 'false', 'off', 'n', 'no')
+
+    if isinstance(value, bool):
+        return value
+
+    lowered = value.strip().lower()
+    if lowered in true_strings:
+        return True
+    elif lowered in false_strings or not strict:
+        return False
+
+    msg = _(
+        "Unrecognized value '%(value)s'; acceptable values are: %(valid)s"
+    ) % {
+        'value': value,
+        'valid': ', '.join(
+            f"'{s}'" for s in sorted(true_strings + false_strings)
+        ),
+    }
+    raise ValueError(msg)
+
+
+def boolenv(*vars, default=False):
+    """Search for the first defined of possibly many bool-like env vars.
+
+    Returns the first environment variable defined in vars, or returns the
+    default.
+
+    :param vars: Arbitrary strings to search for. Case sensitive.
+    :param default: The default to return if no value found.
+    :returns: A boolean corresponding to the value found, else the default if
+        no value found.
+    """
+    for v in vars:
+        value = os.environ.get(v, None)
+        if value:
+            return bool_from_str(value)
+    return default
diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py
index 8e88d8e90c..d415a84538 100644
--- a/openstackclient/compute/v2/server.py
+++ b/openstackclient/compute/v2/server.py
@@ -34,6 +34,7 @@ from osc_lib import exceptions
 from osc_lib import utils
 
 from openstackclient.api import compute_v2
+from openstackclient.common import envvars
 from openstackclient.common import pagination
 from openstackclient.i18n import _
 from openstackclient.identity import common as identity_common
@@ -324,48 +325,6 @@ def _prep_server_detail(compute_client, image_client, server, *, refresh=True):
     return info
 
 
-def bool_from_str(value, strict=False):
-    true_strings = ('1', 't', 'true', 'on', 'y', 'yes')
-    false_strings = ('0', 'f', 'false', 'off', 'n', 'no')
-
-    if isinstance(value, bool):
-        return value
-
-    lowered = value.strip().lower()
-    if lowered in true_strings:
-        return True
-    elif lowered in false_strings or not strict:
-        return False
-
-    msg = _(
-        "Unrecognized value '%(value)s'; acceptable values are: %(valid)s"
-    ) % {
-        'value': value,
-        'valid': ', '.join(
-            f"'{s}'" for s in sorted(true_strings + false_strings)
-        ),
-    }
-    raise ValueError(msg)
-
-
-def boolenv(*vars, default=False):
-    """Search for the first defined of possibly many bool-like env vars.
-
-    Returns the first environment variable defined in vars, or returns the
-    default.
-
-    :param vars: Arbitrary strings to search for. Case sensitive.
-    :param default: The default to return if no value found.
-    :returns: A boolean corresponding to the value found, else the default if
-        no value found.
-    """
-    for v in vars:
-        value = os.environ.get(v, None)
-        if value:
-            return bool_from_str(value)
-    return default
-
-
 class AddFixedIP(command.ShowOne):
     _description = _("Add fixed IP address to server")
 
@@ -1876,7 +1835,7 @@ class CreateServer(command.ShowOne):
 
             if 'delete_on_termination' in mapping:
                 try:
-                    value = bool_from_str(
+                    value = envvars.bool_from_str(
                         mapping['delete_on_termination'],
                         strict=True,
                     )
@@ -2223,7 +2182,7 @@ class DeleteServer(command.Command):
         parser.add_argument(
             '--all-projects',
             action='store_true',
-            default=boolenv('ALL_PROJECTS'),
+            default=envvars.boolenv('ALL_PROJECTS'),
             help=_(
                 'Delete server(s) in another project by name (admin only)'
                 '(can be specified using the ALL_PROJECTS envvar)'
@@ -2389,7 +2348,7 @@ class ListServer(command.Lister):
         parser.add_argument(
             '--all-projects',
             action='store_true',
-            default=boolenv('ALL_PROJECTS'),
+            default=envvars.boolenv('ALL_PROJECTS'),
             help=_(
                 'Include all projects (admin only) '
                 '(can be specified using the ALL_PROJECTS envvar)'
@@ -4967,7 +4926,7 @@ class StartServer(command.Command):
         parser.add_argument(
             '--all-projects',
             action='store_true',
-            default=boolenv('ALL_PROJECTS'),
+            default=envvars.boolenv('ALL_PROJECTS'),
             help=_(
                 'Start server(s) in another project by name (admin only) '
                 '(can be specified using the ALL_PROJECTS envvar)'
@@ -5002,7 +4961,7 @@ class StopServer(command.Command):
         parser.add_argument(
             '--all-projects',
             action='store_true',
-            default=boolenv('ALL_PROJECTS'),
+            default=envvars.boolenv('ALL_PROJECTS'),
             help=_(
                 'Stop server(s) in another project by name (admin only) '
                 '(can be specified using the ALL_PROJECTS envvar)'
diff --git a/openstackclient/tests/unit/test_shell.py b/openstackclient/tests/unit/test_shell.py
index fd95fce615..628e362bba 100644
--- a/openstackclient/tests/unit/test_shell.py
+++ b/openstackclient/tests/unit/test_shell.py
@@ -183,14 +183,14 @@ class TestShell(osc_lib_test_utils.TestShell):
             osc_lib_test_utils.fake_execute(_shell, _cmd)
 
             self.app.assert_called_with(["list", "role"])
-            self.assertEqual(
-                default_args.get("token", ''), _shell.options.token, "token"
-            )
-            self.assertEqual(
-                default_args.get("auth_url", ''),
-                _shell.options.auth_url,
-                "auth_url",
-            )
+
+            if default_args.get('token'):
+                self.assertEqual(default_args['token'], _shell.options.token)
+
+            if default_args.get('auth_url'):
+                self.assertEqual(
+                    default_args['auth_url'], _shell.options.auth_url
+                )
 
     def _assert_cli(self, cmd_options, default_args):
         with mock.patch(
@@ -204,25 +204,28 @@ class TestShell(osc_lib_test_utils.TestShell):
             osc_lib_test_utils.fake_execute(_shell, _cmd)
 
             self.app.assert_called_with(["list", "server"])
+
+            # TODO(stephenfin): Remove "or ''" when we bump osc-lib minimum to
+            # a version that includes I1d26133c9d9ed299d1035f207059aa8fe463a001
             self.assertEqual(
                 default_args["compute_api_version"],
-                _shell.options.os_compute_api_version,
+                _shell.options.os_compute_api_version or '',
             )
             self.assertEqual(
                 default_args["identity_api_version"],
-                _shell.options.os_identity_api_version,
+                _shell.options.os_identity_api_version or '',
             )
             self.assertEqual(
                 default_args["image_api_version"],
-                _shell.options.os_image_api_version,
+                _shell.options.os_image_api_version or '',
             )
             self.assertEqual(
                 default_args["volume_api_version"],
-                _shell.options.os_volume_api_version,
+                _shell.options.os_volume_api_version or '',
             )
             self.assertEqual(
                 default_args["network_api_version"],
-                _shell.options.os_network_api_version,
+                _shell.options.os_network_api_version or '',
             )
 
 
diff --git a/openstackclient/volume/v3/volume_attachment.py b/openstackclient/volume/v3/volume_attachment.py
index fe4765c7b4..b24c96b83a 100644
--- a/openstackclient/volume/v3/volume_attachment.py
+++ b/openstackclient/volume/v3/volume_attachment.py
@@ -18,6 +18,7 @@ from osc_lib.command import command
 from osc_lib import exceptions
 from osc_lib import utils
 
+from openstackclient.common import envvars
 from openstackclient.common import pagination
 from openstackclient.i18n import _
 from openstackclient.identity import common as identity_common
@@ -399,7 +400,7 @@ class ListVolumeAttachment(command.Lister):
             '--all-projects',
             dest='all_projects',
             action='store_true',
-            default=utils.env('ALL_PROJECTS', default=False),
+            default=envvars.boolenv('ALL_PROJECTS'),
             help=_('Shows details for all projects (admin only).'),
         )
         parser.add_argument(
diff --git a/openstackclient/volume/v3/volume_group.py b/openstackclient/volume/v3/volume_group.py
index 201e7a6d7e..f943b80302 100644
--- a/openstackclient/volume/v3/volume_group.py
+++ b/openstackclient/volume/v3/volume_group.py
@@ -17,6 +17,7 @@ from osc_lib.command import command
 from osc_lib import exceptions
 from osc_lib import utils
 
+from openstackclient.common import envvars
 from openstackclient.i18n import _
 
 
@@ -410,7 +411,7 @@ class ListVolumeGroup(command.Lister):
             '--all-projects',
             dest='all_projects',
             action='store_true',
-            default=utils.env('ALL_PROJECTS', default=False),
+            default=envvars.boolenv('ALL_PROJECTS'),
             help=_('Shows details for all projects (admin only).'),
         )
         # TODO(stephenfin): Add once we have an equivalent command for
diff --git a/openstackclient/volume/v3/volume_group_snapshot.py b/openstackclient/volume/v3/volume_group_snapshot.py
index eabf700288..5a760ac9bf 100644
--- a/openstackclient/volume/v3/volume_group_snapshot.py
+++ b/openstackclient/volume/v3/volume_group_snapshot.py
@@ -17,6 +17,7 @@ from osc_lib.command import command
 from osc_lib import exceptions
 from osc_lib import utils
 
+from openstackclient.common import envvars
 from openstackclient.i18n import _
 
 LOG = logging.getLogger(__name__)
@@ -145,7 +146,7 @@ class ListVolumeGroupSnapshot(command.Lister):
             '--all-projects',
             dest='all_projects',
             action='store_true',
-            default=utils.env('ALL_PROJECTS', default=False),
+            default=envvars.boolenv('ALL_PROJECTS'),
             help=_('Shows details for all projects (admin only).'),
         )
         # TODO(stephenfin): Add once we have an equivalent command for