diff --git a/stack.sh b/stack.sh
index 0d9d836eca..26d9754f6f 100755
--- a/stack.sh
+++ b/stack.sh
@@ -1295,28 +1295,31 @@ fi
 # Save some values we generated for later use
 save_stackenv
 
-# Write out a clouds.yaml file
-# putting the location into a variable to allow for easier refactoring later
-# to make it overridable. There is current no usecase where doing so makes
-# sense, so I'm not actually doing it now.
+# Update/create user clouds.yaml file.
+# clouds.yaml will have a `devstack` entry for the `demo` user for the `demo`
+# project.
+
+# The location is a variable to allow for easier refactoring later to make it
+# overridable. There is currently no usecase where doing so makes sense, so
+# it's not currently configurable.
 CLOUDS_YAML=~/.config/openstack/clouds.yaml
-if [ ! -e $CLOUDS_YAML ]; then
-    mkdir -p $(dirname $CLOUDS_YAML)
-    cat >"$CLOUDS_YAML" <<EOF
-clouds:
-  devstack:
-    auth:
-      auth_url: $KEYSTONE_AUTH_URI/v$IDENTITY_API_VERSION
-      username: demo
-      project_name: demo
-      password: $ADMIN_PASSWORD
-    region_name: $REGION_NAME
-    identity_api_version: $IDENTITY_API_VERSION
-EOF
-    if [ -f "$SSL_BUNDLE_FILE" ]; then
-        echo "    cacert: $SSL_BUNDLE_FILE" >>"$CLOUDS_YAML"
-    fi
+
+mkdir -p $(dirname $CLOUDS_YAML)
+
+CA_CERT_ARG=''
+if [ -f "$SSL_BUNDLE_FILE" ]; then
+    CA_CERT_ARG="--os-cacert $SSL_BUNDLE_FILE"
 fi
+$TOP_DIR/tools/update_clouds_yaml.py \
+    --file $CLOUDS_YAML \
+    --os-cloud devstack \
+    --os-region-name $REGION_NAME \
+    --os-identity-api-version $IDENTITY_API_VERSION \
+    $CA_CERT_ARG \
+    --os-auth-url $KEYSTONE_AUTH_URI/v$IDENTITY_API_VERSION \
+    --os-username demo \
+    --os-password $ADMIN_PASSWORD \
+    --os-project-name demo
 
 
 # Wrapup configuration
diff --git a/tools/update_clouds_yaml.py b/tools/update_clouds_yaml.py
new file mode 100755
index 0000000000..08621352ad
--- /dev/null
+++ b/tools/update_clouds_yaml.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python
+
+# 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.
+
+# Update the clouds.yaml file.
+
+
+import argparse
+import os.path
+
+import yaml
+
+
+class UpdateCloudsYaml(object):
+    def __init__(self, args):
+        if args.file:
+            self._clouds_path = args.file
+            self._create_directory = False
+        else:
+            self._clouds_path = os.path.expanduser(
+                '~/.config/openstack/clouds.yaml')
+            self._create_directory = True
+        self._clouds = {}
+
+        self._cloud = args.os_cloud
+        self._cloud_data = {
+            'region_name': args.os_region_name,
+            'identity_api_version': args.os_identity_api_version,
+            'auth': {
+                'auth_url': args.os_auth_url,
+                'username': args.os_username,
+                'password': args.os_password,
+                'project_name': args.os_project_name,
+            },
+        }
+        if args.os_cacert:
+            self._cloud_data['cacert'] = args.os_cacert
+
+    def run(self):
+        self._read_clouds()
+        self._update_clouds()
+        self._write_clouds()
+
+    def _read_clouds(self):
+        try:
+            with open(self._clouds_path) as clouds_file:
+                self._clouds = yaml.load(clouds_file)
+        except IOError:
+            # The user doesn't have a clouds.yaml file.
+            print("The user clouds.yaml file didn't exist.")
+            self._clouds = {}
+
+    def _update_clouds(self):
+        self._clouds.setdefault('clouds', {})[self._cloud] = self._cloud_data
+
+    def _write_clouds(self):
+
+        if self._create_directory:
+            clouds_dir = os.path.dirname(self._clouds_path)
+            os.makedirs(clouds_dir)
+
+        with open(self._clouds_path, 'w') as clouds_file:
+            yaml.dump(self._clouds, clouds_file, default_flow_style=False)
+
+
+def main():
+    parser = argparse.ArgumentParser('Update clouds.yaml file.')
+    parser.add_argument('--file')
+    parser.add_argument('--os-cloud', required=True)
+    parser.add_argument('--os-region-name', default='RegionOne')
+    parser.add_argument('--os-identity-api-version', default='3')
+    parser.add_argument('--os-cacert')
+    parser.add_argument('--os-auth-url', required=True)
+    parser.add_argument('--os-username', required=True)
+    parser.add_argument('--os-password', required=True)
+    parser.add_argument('--os-project-name', required=True)
+
+    args = parser.parse_args()
+
+    update_clouds_yaml = UpdateCloudsYaml(args)
+    update_clouds_yaml.run()
+
+
+if __name__ == "__main__":
+    main()