CORD-1151
Make cord_dir and cord_profile_dir local to config node
use head_* and config_* prefixes to avoid hardcoding paths
config-side ssh key generation+
fix frontend & mock builds
[build] group in inventory
fix inventory strangeness
raise privs when creating ssh_pki_dir
move admin-openrc.sh.j2 to cord-profile
add copy-cord-playbook.yml, clarify where it runs
fix paths for head_cord_profile_dir with mock/frontend
use /opt/cord_profile/admin-openrc.sh rather than ~/admin-openrc.sh
install pki
make comment in do-enlist-compute-node accurate, set correct interface
remove hardcoded credential path
logging and ssh key fixes

Change-Id: Ie7560c911dce1558e09806c9997884dfbd475e9c
diff --git a/README.md b/README.md
index 39b1c2b..9dd48e9 100644
--- a/README.md
+++ b/README.md
@@ -202,4 +202,52 @@
 xos-teardown; xos-launch; compute-node-refresh
 ```
 
+## Design Notes for Developers
+
+### Variables used in platform-install
+
+`cord_profile`: name of the profile_manifest to use.
+
+Paths on configuration node (where playbooks are run, may also be build node)
+
+ - `config_cord_dir` location on configuration node of cord dir
+ - `config_cord_profile_dir` location on configuration node of cord_profile dir
+ - `pki_dir`, `ssh_pki_dir`: where SSL and SSH certificates are created on
+   config node
+ - `credentials_dir` - location where autogenerated passwords file are created
+
+Paths on head node (target system operated on by playbooks)
+
+ - `head_cord_dir` - where the `cord` directory is copied to on the head node
+   (deprecated when we reach container-only deploys)
+ - `head_cord_profile_dir` - location of the `cord_profile` directory on the
+   head node
+ - `head_onos_cord_dir` - location of the `onos-cord` directory on the head
+   node
+ - `head_onos_fabric_dir` - location of the `onos-fabric` directory on the head
+   node
+
+### Style notes
+
+Prefix every role file with the yaml start block and comment with name of file
+relative to role base.
+
+```
+---
+# rolename/tasks/main.yml
+```
+
+When using templates, put the template filename and path within the role as a
+comment in the template so that it's easy to determine which template was used
+to create a file after it's been created.
+
+If you use a variable that isn't created by the ansible setup task, define it
+in the role defaults file. The default value for anay variable must be the same
+across all role defaults.
+
+Use the YAML style syntax for tasks, not the older `=` syntax, as the former is
+more likely to indent and syntax highlight properly in most editors.
+
+Roles should always have the same outcome, so avoid using conditionals or tags
+to change the behavior of a role. 
 
diff --git a/build-platform-install-playbook.yml b/build-platform-install-playbook.yml
index 8466693..358e300 100644
--- a/build-platform-install-playbook.yml
+++ b/build-platform-install-playbook.yml
@@ -2,7 +2,7 @@
 # build-platform-install-playbook.yml
 
 - name: Include vars
-  hosts: localhost
+  hosts: build
   tasks:
     - name: Include variables
       include_vars: "{{ item }}"
@@ -11,8 +11,7 @@
         - profile_manifests/local_vars.yml
 
 - name: Build the core image
-  hosts: localhost
+  hosts: build
   roles:
     - xos-core-build
 
-
diff --git a/copy-cord-playbook.yml b/copy-cord-playbook.yml
new file mode 100644
index 0000000..1c34ea9
--- /dev/null
+++ b/copy-cord-playbook.yml
@@ -0,0 +1,18 @@
+---
+# copy-cord-playbook.yml
+# Copy the cord directory from config node to head node
+
+- name: Include vars
+  hosts: config
+  tasks:
+    - name: Include variables
+      include_vars: "{{ item }}"
+      with_items:
+        - "profile_manifests/{{ cord_profile }}.yml"
+        - profile_manifests/local_vars.yml
+
+- name: Copy cord directory structure to head node
+  hosts: head
+  roles:
+    - copy-cord
+
diff --git a/copy-profile-playbook.yml b/copy-profile-playbook.yml
new file mode 100644
index 0000000..761ed0c
--- /dev/null
+++ b/copy-profile-playbook.yml
@@ -0,0 +1,26 @@
+---
+# copy-profile-book.yml
+# Copies the profile to the head node
+
+- name: Include vars
+  hosts: head, config
+  tasks:
+    - name: Include variables
+      include_vars: "{{ item }}"
+      with_items:
+        - "profile_manifests/{{ cord_profile }}.yml"
+        - profile_manifests/local_vars.yml
+
+- name: Copy cord_profile to head node from config node
+  hosts: head
+  roles:
+    - { role: copy-profile, become: yes }
+    - { role: ssh-install, become: yes }
+    - { role: glance-images, become: yes }
+    - { role: copy-credentials, become: yes, when: on_maas }
+
+- name: Install ssh keys when using MaaS
+  hosts: build
+  roles:
+    - { role: ssh-install-maas, become: yes, when: on_maas }
+
diff --git a/cord-config-playbook.yml b/cord-config-playbook.yml
new file mode 100644
index 0000000..8cab50c
--- /dev/null
+++ b/cord-config-playbook.yml
@@ -0,0 +1,26 @@
+---
+# cord-config-playbook.yml
+# Creates all configuration for a CORD pod
+
+- name: Include vars
+  hosts: all
+  tasks:
+    - name: Include variables
+      include_vars: "{{ item }}"
+      with_items:
+        - "profile_manifests/{{ cord_profile }}.yml"
+        - profile_manifests/local_vars.yml
+
+- name: Create SSL Root CA, Intermediate CA, Server certs, SSH CA and keypairs
+  hosts: config
+  roles:
+    - pki-root-ca
+    - pki-intermediate-ca
+    - pki-cert
+    - ssh-pki
+
+- name: Create CORD profile
+  hosts: config
+  roles:
+    - cord-profile
+
diff --git a/cord-profile-playbook.yml b/cord-profile-playbook.yml
deleted file mode 100644
index 7e7d9df..0000000
--- a/cord-profile-playbook.yml
+++ /dev/null
@@ -1,16 +0,0 @@
----
-# cord-profile-playbook.yml
-
-- name: Include vars
-  hosts: all
-  tasks:
-    - name: Include variables
-      include_vars: "{{ item }}"
-      with_items:
-        - "profile_manifests/{{ cord_profile }}.yml"
-        - profile_manifests/local_vars.yml
-
-- name: Create CORD profile and copy Glance images
-  hosts: head
-  roles:
-    - cord-profile
diff --git a/deploy-xos-playbook.yml b/deploy-xos-playbook.yml
index 1304c3e..24e29bc 100644
--- a/deploy-xos-playbook.yml
+++ b/deploy-xos-playbook.yml
@@ -23,9 +23,7 @@
 # build XOS core
 - include: build-platform-install-playbook.yml
 
-- include: pki-install-playbook.yml
-
-- include: cord-profile-playbook.yml
+- include: cord-config-playbook.yml
 
 - name: Pull other docker images if not present
   hosts: head
diff --git a/inventory/frontend b/inventory/frontend
index 0f6abe8..27f57c5 100644
--- a/inventory/frontend
+++ b/inventory/frontend
@@ -3,6 +3,14 @@
 [all:vars]
 cord_profile=frontend
 
+[config]
+localhost ansible_connection=local
+
 [head]
 localhost ansible_connection=local
 
+[build]
+localhost ansible_connection=local
+
+[compute]
+
diff --git a/inventory/mock-rcord b/inventory/mock-rcord
index 69eecee..5148c20 100644
--- a/inventory/mock-rcord
+++ b/inventory/mock-rcord
@@ -3,6 +3,14 @@
 [all:vars]
 cord_profile=mock-rcord
 
+[config]
+localhost ansible_connection=local
+
 [head]
 localhost ansible_connection=local
 
+[build]
+localhost ansible_connection=local
+
+[compute]
+
diff --git a/inventory/rcord b/inventory/rcord
index 92adce2..224584e 100644
--- a/inventory/rcord
+++ b/inventory/rcord
@@ -3,6 +3,14 @@
 [all:vars]
 cord_profile=rcord
 
+[config]
+localhost ansible_connection=local
+
 [head]
 localhost ansible_connection=local
 
+[build]
+localhost ansible_connection=local
+
+[compute]
+
diff --git a/pki-install-playbook.yml b/pki-install-playbook.yml
deleted file mode 100644
index 70183a5..0000000
--- a/pki-install-playbook.yml
+++ /dev/null
@@ -1,21 +0,0 @@
----
-# pki-install-playbook.yml
-
-- name: Include vars
-  hosts: all
-  tasks:
-    - name: Include variables
-      include_vars: "{{ item }}"
-      with_items:
-        - "profile_manifests/{{ cord_profile }}.yml"
-        - profile_manifests/local_vars.yml
-
-# Generate SSL certs
-- include: pki-setup-playbook.yml
-
-- name: Install CA certificates
-  become: yes
-  hosts: head
-  roles:
-    - pki-install
-
diff --git a/pki-setup-playbook.yml b/pki-setup-playbook.yml
deleted file mode 100644
index 1e4eb02..0000000
--- a/pki-setup-playbook.yml
+++ /dev/null
@@ -1,21 +0,0 @@
----
-# Create a SSL CA for CORD pod use
-
-- name: Include vars
-  hosts: localhost
-  connection: local
-  tasks:
-    - name: Include variables
-      include_vars: "{{ item }}"
-      with_items:
-        - "profile_manifests/{{ cord_profile }}.yml"
-        - profile_manifests/local_vars.yml
-
-- name: Create Root CA, Intermediate CA, Server certs
-  hosts: localhost
-  connection: local
-  roles:
-    - pki-root-ca
-    - pki-intermediate-ca
-    - pki-cert
-
diff --git a/prep-platform-playbook.yml b/prep-platform-playbook.yml
index 8661208..d2f9943 100644
--- a/prep-platform-playbook.yml
+++ b/prep-platform-playbook.yml
@@ -20,15 +20,16 @@
     - apt-cacher-ng
 
 - name: Configure all hosts to use DNS server
-  hosts: all
+  hosts: head, compute
   become: yes
   roles:
     - { role: dns-configure, when: not on_maas }
 
 - name: Prep systems
-  hosts: all
+  hosts: head, compute
   become: yes
   roles:
     - common-prep
+    - pki-install
     - { role: cloudlab-prep, when: on_cloudlab }
 
diff --git a/profile_manifests/api-test.yml b/profile_manifests/api-test.yml
index 2e19f43..6472c2b 100644
--- a/profile_manifests/api-test.yml
+++ b/profile_manifests/api-test.yml
@@ -17,7 +17,7 @@
 xos_admin_user: padmin@vicci.org
 xos_admin_pass: letmein
 xos_admin_first: XOS
-xos_admin_last: admin
+xos_admin_last: Admin
 
 xos_tosca_config_templates:
   - management-net.yaml
diff --git a/profile_manifests/ecord-global.yml b/profile_manifests/ecord-global.yml
index ff934ae..ebeb70e 100644
--- a/profile_manifests/ecord-global.yml
+++ b/profile_manifests/ecord-global.yml
@@ -7,8 +7,9 @@
 site_humanname: MySite
 deployment_type: MyDeployment
 
-xos_admin_user: xosadmin@opencord.org
-xos_admin_pass: "{{ lookup('password', 'credentials/xosadmin@opencord.org chars=ascii_letters,digits') }}"
+credentials_dir: "{{ playbook_dir }}/credentials"
+xos_admin_user: "xosadmin@opencord.org"
+xos_admin_pass: "{{ lookup('password', credentials_dir ~ '/xosadmin@opencord.org chars=ascii_letters,digits') }}"
 xos_admin_first: XOS
 xos_admin_last: Admin
 
diff --git a/profile_manifests/ecord.yml b/profile_manifests/ecord.yml
index a485c13..09d1750 100644
--- a/profile_manifests/ecord.yml
+++ b/profile_manifests/ecord.yml
@@ -7,8 +7,9 @@
 site_humanname: MySite
 deployment_type: MyDeployment
 
-xos_admin_user: xosadmin@opencord.org
-xos_admin_pass: "{{ lookup('password', 'credentials/xosadmin@opencord.org chars=ascii_letters,digits') }}"
+credentials_dir: "{{ playbook_dir }}/credentials"
+xos_admin_user: "xosadmin@opencord.org"
+xos_admin_pass: "{{ lookup('password', credentials_dir ~ '/xosadmin@opencord.org chars=ascii_letters,digits') }}"
 xos_admin_first: XOS
 xos_admin_last: Admin
 
diff --git a/profile_manifests/frontend.yml b/profile_manifests/frontend.yml
index f75ddbb..f6b3ff6 100644
--- a/profile_manifests/frontend.yml
+++ b/profile_manifests/frontend.yml
@@ -4,6 +4,10 @@
 site_name: frontend
 deployment_type: "Frontend Mock"
 
+# head == config for frontend mocks
+head_cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+head_cord_dir: "{{ ansible_user_dir + '/cord' }}"
+
 frontend_only: True
 use_redis: True
 use_openstack: False
@@ -12,10 +16,11 @@
 
 build_xos_base_image: True
 
-xos_admin_user: xosadmin@opencord.org
-xos_admin_pass: "{{ lookup('password', 'credentials/xosadmin@opencord.org chars=ascii_letters,digits') }}"
+credentials_dir: "{{ playbook_dir }}/credentials"
+xos_admin_user: "xosadmin@opencord.org"
+xos_admin_pass: "{{ lookup('password', credentials_dir ~ '/xosadmin@opencord.org chars=ascii_letters,digits') }}"
 xos_admin_first: XOS
-xos_admin_last: admin
+xos_admin_last: Admin
 
 xos_tosca_config_templates:
   - sample.yaml
diff --git a/profile_manifests/mock-ecord-global.yml b/profile_manifests/mock-ecord-global.yml
index 02c5abe..89e19f9 100644
--- a/profile_manifests/mock-ecord-global.yml
+++ b/profile_manifests/mock-ecord-global.yml
@@ -5,10 +5,11 @@
 site_name: mock-ecord
 deployment_type: "Mock E-CORD Global Pod"
 
-xos_admin_user: xosadmin@opencord.org
-xos_admin_pass: "{{ lookup('password', 'credentials/xosadmin@opencord.org chars=ascii_letters,digits') }}"
+credentials_dir: "{{ playbook_dir }}/credentials"
+xos_admin_user: "xosadmin@opencord.org"
+xos_admin_pass: "{{ lookup('password', credentials_dir ~ '/xosadmin@opencord.org chars=ascii_letters,digits') }}"
 xos_admin_first: XOS
-xos_admin_last: admin
+xos_admin_last: Admin
 
 frontend_only: False
 use_openstack: False
diff --git a/profile_manifests/mock-ecord.yml b/profile_manifests/mock-ecord.yml
index 1a969db..2a3fcc1 100644
--- a/profile_manifests/mock-ecord.yml
+++ b/profile_manifests/mock-ecord.yml
@@ -5,10 +5,11 @@
 site_name: mock-ecord
 deployment_type: "Mock E-CORD Pod"
 
-xos_admin_user: xosadmin@opencord.org
-xos_admin_pass: "{{ lookup('password', 'credentials/xosadmin@opencord.org chars=ascii_letters,digits') }}"
+credentials_dir: "{{ playbook_dir }}/credentials"
+xos_admin_user: "xosadmin@opencord.org"
+xos_admin_pass: "{{ lookup('password', credentials_dir ~ '/xosadmin@opencord.org chars=ascii_letters,digits') }}"
 xos_admin_first: XOS
-xos_admin_last: admin
+xos_admin_last: Admin
 
 frontend_only: False
 use_openstack: False
diff --git a/profile_manifests/mock-mcord.yml b/profile_manifests/mock-mcord.yml
index 5a9f876..ae69ab6 100644
--- a/profile_manifests/mock-mcord.yml
+++ b/profile_manifests/mock-mcord.yml
@@ -5,10 +5,11 @@
 site_name: mock-mcord
 deployment_type: "Mock M-CORD Pod"
 
-xos_admin_user: xosadmin@opencord.org
-xos_admin_pass: "{{ lookup('password', 'credentials/xosadmin@opencord.org chars=ascii_letters,digits') }}"
+credentials_dir: "{{ playbook_dir }}/credentials"
+xos_admin_user: "xosadmin@opencord.org"
+xos_admin_pass: "{{ lookup('password', credentials_dir ~ '/xosadmin@opencord.org chars=ascii_letters,digits') }}"
 xos_admin_first: XOS
-xos_admin_last: admin
+xos_admin_last: Admin
 
 frontend_only: True
 use_openstack: False
diff --git a/profile_manifests/mock-rcord.yml b/profile_manifests/mock-rcord.yml
index 85c3359..93ebad0 100644
--- a/profile_manifests/mock-rcord.yml
+++ b/profile_manifests/mock-rcord.yml
@@ -5,10 +5,15 @@
 site_name: mock-rcord
 deployment_type: "Mock R-CORD Pod"
 
-xos_admin_user: xosadmin@opencord.org
-xos_admin_pass: "{{ lookup('password', 'credentials/xosadmin@opencord.org chars=ascii_letters,digits') }}"
+credentials_dir: "{{ playbook_dir }}/credentials"
+xos_admin_user: "xosadmin@opencord.org"
+xos_admin_pass: "{{ lookup('password', credentials_dir ~ '/xosadmin@opencord.org chars=ascii_letters,digits') }}"
 xos_admin_first: XOS
-xos_admin_last: admin
+xos_admin_last: Admin
+
+# head == config for mocks
+head_cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+head_cord_dir: "{{ ansible_user_dir + '/cord' }}"
 
 frontend_only: True
 use_openstack: False
@@ -70,20 +75,6 @@
   - name: fabric
     path: orchestration/xos_services/fabric
 
-xos_service_sshkeys:
-  - name: onos_rsa
-    source_path: "/dev/null"
-  - name: onos_rsa.pub
-    source_path: "/dev/null"
-  - name: volt_rsa
-    source_path: "/dev/null"
-  - name: volt_rsa.pub
-    source_path: "/dev/null"
-  - name: vsg_rsa
-    source_path: "/dev/null"
-  - name: vsg_rsa.pub
-    source_path: "/dev/null"
-
 profile_library: "rcord"
 
 # site domain suffix
diff --git a/profile_manifests/opencloud.yml b/profile_manifests/opencloud.yml
index b8776ce..296d540 100644
--- a/profile_manifests/opencloud.yml
+++ b/profile_manifests/opencloud.yml
@@ -1,103 +1,198 @@
 ---
-# vars/opencloud.yaml
+# profile_manifests/opencloud.yml
 # Generic OpenCloud Site
 
+# redefined here for running XOS start/config on localhost
+cord_profile: opencloud
+
+# These are source paths, used only on the config host, and should be redefined
+# on a per-pod basis when installing multiple pods
+config_cord_dir: "{{ ansible_user_dir + '/cord' }}"
+config_cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+
+# Locations on head node (same on all pods)
+head_cord_dir: "/opt/cord"
+head_cord_profile_dir: "/opt/cord_profile"
+head_onos_cord_dir: "/opt/onos-cord"
+
+# Credentials and PKI
+credentials_dir: "{{ playbook_dir }}/credentials"
+pki_dir: "{{ playbook_dir }}/pki"
+ssh_pki_dir: "{{ playbook_dir }}/ssh_pki"
+
 # site configuration
 site_name: generic_opencloud
 site_humanname: "Generic OpenCloud"
 deployment_type: campus
 
-xos_admin_user: xosadmin@opencord.org
-xos_admin_pass: "{{ lookup('password', 'credentials/xosadmin@opencord.org chars=ascii_letters,digits') }}"
+xos_admin_user: "xosadmin@opencord.org"
+xos_admin_pass: "{{ lookup('password', credentials_dir ~ '/xosadmin@opencord.org chars=ascii_letters,digits') }}"
 xos_admin_first: XOS
 xos_admin_last: Admin
 
 xos_users: []
 
 use_vtn: True
+use_openstack: True
+use_fabric: False
+
+headnode_name: head1
 
 xos_tosca_config_templates:
-  - openstack.yaml
-  - nodes.yaml
   - vtn-service.yaml
   - management-net.yaml
 
-cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+build_xos_base_image: True
 
 xos_docker_volumes:
-  - host: "{{ cord_profile_dir }}/images"
+  - host: "/opt/cord_profile/images"
     container: /opt/xos/images
 
 # GUI Branding
 # Not neeeded, default is OpenCloud
 
+# GUI Config [new GUI], used in app.config.js.j2 and style.config.js.j2
+gui_project_name: "OpenCloud"
+gui_favicon: "opencloud-favicon.png"
+gui_background: "opencloud-bg.jpg"
+gui_payoff: "A Value-Added Cloud for the Internet2 Community"
+gui_logo: "opencloud-logo.png"
+
 # paths defined in manifest/default.xml
 xos_services:
   - name: vtn
     path: onos-apps/apps/vtn
+    keypair: vsg_rsa
+  - name: openstack
+    path: orchestration/xos_services/openstack
   - name: onos
     path: orchestration/xos_services/onos-service
+    keypair: onos_rsa
   - name: vrouter
     path: orchestration/xos_services/vrouter
 
-xos_service_sshkeys:
-  - name: onos_rsa
-    source_path: "~/.ssh/id_rsa"
-  - name: onos_rsa.pub
-    source_path: "~/.ssh/id_rsa.pub"
+profile_library: "rcord"
 
+# SSL certificate generation
+ssl_cert_subj_prefix: "/C=US/ST=California/L=Menlo Park/O=ON.Lab/OU={{ site_humanname }} Deployment"
 
-# IP prefix for VMs
-virt_nets:
-  - name: mgmtbr
-    ipv4_prefix: 192.168.250
-    head_vms: true
+server_certs:
+  - cn: "keystone.{{ site_suffix }}"
+    subj: "{{ ssl_cert_subj_prefix }}/CN=keystone.{{ site_suffix }}"
+    altnames:
+      - "DNS:keystone.{{ site_suffix }}"
+      - "DNS:keystone"
+  - cn: "xos-core.{{ site_suffix }}"
+    subj: "{{ ssl_cert_subj_prefix }}/CN=xos-core.{{ site_suffix }}"
+    altnames:
+      - "DNS:xos-core.{{ site_suffix }}"
+  - cn: "docker.{{ site_suffix }}"
+    subj: "{{ ssl_cert_subj_prefix }}/CN=docker.{{ site_suffix }}"
+    altnames:
+      - "DNS:docker.{{ site_suffix }}"
+      - "DNS:head.{{ site_suffix }}"
+      - "IP:127.0.0.1"
+      - "IP:{{ mgmt_ipv4_first_octets }}.1"
+      - "IP:{{ hostvars[headnode_name].ansible_default_ipv4.address }}"
+  - cn: "registry.{{ site_suffix }}"
+    subj: "{{ ssl_cert_subj_prefix }}/CN=registry.{{ site_suffix }}"
+    altnames:
+      - "DNS:registry.{{ site_suffix }}"
+      - "DNS:head.{{ site_suffix }}"
+      - "IP:127.0.0.1"
+      - "IP:{{ mgmt_ipv4_first_octets }}.1"
+      - "IP:{{ hostvars[headnode_name].ansible_default_ipv4.address }}"
 
-# DNS/domain settings
+client_certs:
+  - cn: "dockerclient"
+    subj: "{{ ssl_cert_subj_prefix }}/CN=dockerclient"
+    altnames:
+      - "email:dockerclient@{{ site_suffix }}"
+  - cn: "dockerbuildhost"
+    subj: "{{ ssl_cert_subj_prefix }}/CN=dockerbuildhost"
+    altnames:
+      - "email:dockerbuildhost@{{ site_suffix }}"
+
+# docker registry users
+docker_registry_users:
+  - name: "{{ xos_admin_user }}"
+    password: "{{ xos_admin_pass }}"
+
+# Network/DNS settings
 site_suffix: generic.infra.opencloud.us
 
 dns_search:
   - "{{ site_suffix }}"
 
-# SSL server certificate generation
-server_certs:
-  - cn: "keystone.{{ site_suffix }}"
-    subj: "/C=US/ST=California/L=Menlo Park/O=ON.Lab/OU=Test Deployment/CN=keystone.{{ site_suffix }}"
-    altnames:
-      - "DNS:keystone.{{ site_suffix }}"
-      - "DNS:keystone"
-  - cn: "xos-core.{{ site_suffix }}"
-    subj: "/C=US/ST=California/L=Menlo Park/O=ON.Lab/OU=Test Deployment/CN=xos-core.{{ site_suffix }}"
-    altnames:
-      - "DNS:xos-core.{{ site_suffix }}"
+mgmt_ipv4_first_octets: "192.168.200"
 
-# NSD/Unbound settings
+dns_servers:
+  - "{{ mgmt_ipv4_first_octets }}.1"
+
+headnode_user: vagrant
+
+# DNS settings for NSD/Unbound
 nsd_zones:
   - name: "{{ site_suffix }}"
-    ipv4_first_octets: 192.168.250
+    ipv4_first_octets: "{{ mgmt_ipv4_first_octets }}"
     name_reverse_unbound: "168.192.in-addr.arpa"
     soa: ns1
     ns:
       - { name: ns1 }
-    nodelist: head_vm_list
+    nodelists:
+      - head_lxd_list
+      - physical_node_list
     aliases:
-      - { name: "ns1" , dest: "head" }
-      - { name: "ns" , dest: "head" }
-      - { name: "apt-cache" , dest: "head" }
+      - { name: "apt-cache", dest: "head1" }
+      - { name: "cordloghost", dest: "head1" }
+      - { name: "docker", dest: "head1" }
+      - { name: "ns", dest: "head1" }
+      - { name: "ns1", dest: "head1" }
+      - { name: "onos-cord", dest: "head1" }
+      - { name: "xos", dest: "head1" }
+      - { name: "xos-chameleon", dest: "head1" }
+      - { name: "xos-rest-gw", dest: "head1" }
+      - { name: "xos-spa-gui", dest: "head1" }
 
-name_on_public_interface: head
+unbound_listen_all: True
 
-# If true, unbound listens on the head node's `ansible_default_ipv4` interface
-unbound_listen_on_default: True
+unbound_interfaces:
+  - "{{ mgmt_ipv4_first_octets }}.1/24"
+
+dhcpd_subnets:
+  - interface: mgmtbr
+    cidr: "{{ mgmt_ipv4_first_octets }}.1/24"
+    dhcp_first: 129
+    dhcp_last: 254
+    other_static:
+      - physical_node_list
+      - head_lxd_list
+
+# network interface setup
+mgmt_interface: eth1
+
+physical_node_list:
+  - name: head1
+    ipv4_last_octet: 1
+    aliases:
+      - head
+  - name: compute1
+    ipv4_last_octet: 20
+  - name: compute2
+    ipv4_last_octet: 21
 
 # VTN network configuration
 management_network_cidr: 172.27.0.0/24
 management_network_ip: 172.27.0.1/24
 data_plane_ip: 10.168.0.253/24
 
-on_maas: False
+# ONOS version
+onos_docker_image: "opencord/onos:1.8.2"
 
-run_dist_upgrade: True
+on_maas: False
+on_cloudlab: False
+
+run_dist_upgrade: False
 
 openstack_version: kilo
 
@@ -113,23 +208,30 @@
   - m1.xlarge
 
 charm_versions:
-  neutron-api: "cs:~cordteam/trusty/neutron-api-3"
+  ceilometer-agent: "cs:trusty/ceilometer-agent-13"
+  ceilometer: "cs:trusty/ceilometer-17"
+  glance: "cs:trusty/glance-28"
+  keystone: "cs:trusty/keystone-33"
+  mongodb: "cs:trusty/mongodb-33"
+  neutron-api: "cs:~cordteam/trusty/neutron-api-5"
   nova-compute: "cs:~cordteam/trusty/nova-compute-2"
-
-head_vm_list: []
+  ntp: "cs:trusty/ntp-14"
+  openstack-dashboard: "cs:trusty/openstack-dashboard-19"
+  percona-cluster: "cs:trusty/percona-cluster-31"
+  rabbitmq-server: "cs:trusty/rabbitmq-server-42"
 
 head_lxd_list:
   - name: "juju-1"
     service: "juju"
     aliases:
        - "juju"
-    ipv4_last_octet: 10
+    ipv4_last_octet: 50
 
   - name: "ceilometer-1"
     service: "ceilometer"
     aliases:
       - "ceilometer"
-    ipv4_last_octet: 20
+    ipv4_last_octet: 51
     forwarded_ports:
       - { ext: 8777, int: 8777 }
 
@@ -137,7 +239,7 @@
     service: "glance"
     aliases:
       - "glance"
-    ipv4_last_octet: 30
+    ipv4_last_octet: 52
     forwarded_ports:
       - { ext: 9292, int: 9292 }
 
@@ -145,7 +247,7 @@
     service: "keystone"
     aliases:
       - "keystone"
-    ipv4_last_octet: 40
+    ipv4_last_octet: 53
     forwarded_ports:
       - { ext: 35357, int: 35357 }
       - { ext: 4990, int: 4990 }
@@ -155,13 +257,13 @@
     service: "percona-cluster"
     aliases:
       - "percona-cluster"
-    ipv4_last_octet: 50
+    ipv4_last_octet: 54
 
   - name: "neutron-api-1"
     service: "neutron-api"
     aliases:
       - "neutron-api"
-    ipv4_last_octet: 70
+    ipv4_last_octet: 55
     forwarded_ports:
       - { ext: 9696, int: 9696 }
 
@@ -169,7 +271,7 @@
     service: "nova-cloud-controller"
     aliases:
       - "nova-cloud-controller"
-    ipv4_last_octet: 90
+    ipv4_last_octet: 56
     forwarded_ports:
       - { ext: 8774, int: 8774 }
 
@@ -177,7 +279,7 @@
     service: "openstack-dashboard"
     aliases:
       - "openstack-dashboard"
-    ipv4_last_octet: 100
+    ipv4_last_octet: 57
     forwarded_ports:
       - { ext: 8080, int: 80 }
 
@@ -185,24 +287,19 @@
     service: "rabbitmq-server"
     aliases:
       - "rabbitmq-server"
-    ipv4_last_octet: 110
+    ipv4_last_octet: 58
 
-  - name: "onos-cord-1"
+  - name: "mongodb-1"
+    service: "mongodb"
     aliases:
-      - "onos-cord"
-    ipv4_last_octet: 110
-    docker_path: "cord"
-
-  - name: "xos-1"
-    aliases:
-      - "xos"
-    ipv4_last_octet: 130
-    docker_path: 'service-profile/opencloud'
+      - "mongodb"
+    ipv4_last_octet: 59
 
 lxd_service_list:
   - ceilometer
   - glance
   - keystone
+  - mongodb
   - neutron-api
   - nova-cloud-controller
   - openstack-dashboard
diff --git a/profile_manifests/rcord.yml b/profile_manifests/rcord.yml
index 4e751f8..10e1057 100644
--- a/profile_manifests/rcord.yml
+++ b/profile_manifests/rcord.yml
@@ -7,8 +7,9 @@
 site_humanname: MySite
 deployment_type: MyDeployment
 
-xos_admin_user: xosadmin@opencord.org
-xos_admin_pass: "{{ lookup('password', 'credentials/xosadmin@opencord.org chars=ascii_letters,digits') }}"
+credentials_dir: "{{ playbook_dir }}/credentials"
+xos_admin_user: "xosadmin@opencord.org"
+xos_admin_pass: "{{ lookup('password', credentials_dir ~ '/xosadmin@opencord.org chars=ascii_letters,digits') }}"
 xos_admin_first: XOS
 xos_admin_last: Admin
 
@@ -56,60 +57,29 @@
   - name: vtn
     path: onos-apps/apps/vtn
     keypair: vsg_rsa
-    synchronizer: true
   - name: openstack
     path: orchestration/xos_services/openstack
-    synchronizer: true
   - name: onos
     path: orchestration/xos_services/onos-service
     keypair: onos_rsa
-    synchronizer: true
   - name: vrouter
     path: orchestration/xos_services/vrouter
-    synchronizer: true
   - name: vsg
     path: orchestration/xos_services/vsg
     keypair: vsg_rsa
-    synchronizer: true
   - name: vtr
     path: orchestration/xos_services/vtr
     keypair: vsg_rsa
-    synchronizer: true
   - name: fabric
     path: orchestration/xos_services/fabric
-    synchronizer: true
   - name: exampleservice
     path: orchestration/xos_services/exampleservice
     keypair: exampleservice_rsa
-    synchronizer: true
 #  - name: monitoring
 #    path: orchestration/xos_services/monitoring
 #    keypair: monitoringservice_rsa
 #    synchronizer: false
 
-xos_service_sshkeys:
-  - name: onos_rsa
-    source_path: "~/.ssh/id_rsa"
-  - name: onos_rsa.pub
-    source_path: "~/.ssh/id_rsa.pub"
-  - name: volt_rsa
-    source_path: "~/.ssh/id_rsa"
-  - name: volt_rsa.pub
-    source_path: "~/.ssh/id_rsa.pub"
-  - name: vsg_rsa
-    source_path: "~/.ssh/id_rsa"
-  - name: vsg_rsa.pub
-    source_path: "~/.ssh/id_rsa.pub"
-# needed onboarding synchronizer doesn't require service code to be present when started
-  - name: exampleservice_rsa
-    source_path: "~/.ssh/id_rsa"
-  - name: exampleservice_rsa.pub
-    source_path: "~/.ssh/id_rsa.pub"
-  - name: monitoringservice_rsa
-    source_path: "~/.ssh/id_rsa"
-  - name: monitoringservice_rsa.pub
-    source_path: "~/.ssh/id_rsa.pub"
-
 profile_library: "rcord"
 
 # VM networks/bridges on head
diff --git a/publish-platform-install-playbook.yml b/publish-platform-install-playbook.yml
index 576e21d..c7ce762 100644
--- a/publish-platform-install-playbook.yml
+++ b/publish-platform-install-playbook.yml
@@ -2,7 +2,7 @@
 # publish-platform-install-playbook.yml
 
 - name: Include vars
-  hosts: localhost
+  hosts: build
   tasks:
     - name: Include variables
       include_vars: "{{ item }}"
@@ -11,7 +11,7 @@
         - profile_manifests/local_vars.yml
 
 - name: Publish the core image
-  hosts: localhost
+  hosts: build
   roles:
     - xos-core-publish
 
diff --git a/roles/api-tests/defaults/main.yml b/roles/api-tests/defaults/main.yml
index f78a3b2..065a432 100644
--- a/roles/api-tests/defaults/main.yml
+++ b/roles/api-tests/defaults/main.yml
@@ -1,4 +1,5 @@
 ---
 # api-tests/defaults/main.yml
 
-cord_dir: "{{ hostvars['localhost']['ansible_user_dir'] + '/cord' }}"
+config_cord_dir: "{{ ansible_user_dir + '/cord' }}"
+
diff --git a/roles/api-tests/tasks/main.yml b/roles/api-tests/tasks/main.yml
index 843a384..8ea52e6 100644
--- a/roles/api-tests/tasks/main.yml
+++ b/roles/api-tests/tasks/main.yml
@@ -3,7 +3,7 @@
 
 - name: Copy apiary.apib to target location
   copy:
-    src: "{{ cord_dir }}/orchestration/xos/apiary.apib"
+    src: "{{ config_cord_dir }}/orchestration/xos/apiary.apib"
     dest: "/opt/xos/tests/api/apiary.apib"
 
 - name: Run API tests
diff --git a/roles/automation-integration/defaults/main.yml b/roles/automation-integration/defaults/main.yml
index c7af0e3..c41e867 100644
--- a/roles/automation-integration/defaults/main.yml
+++ b/roles/automation-integration/defaults/main.yml
@@ -1,4 +1,7 @@
 ---
 # automation-integration/defaults/main.yml
 
+# paths
+head_cord_dir: "/opt/cord"
+
 cord_in_a_box: False
diff --git a/roles/automation-integration/templates/do-enlist-compute-node.j2 b/roles/automation-integration/templates/do-enlist-compute-node.j2
index 3490c8c..155dcc2 100644
--- a/roles/automation-integration/templates/do-enlist-compute-node.j2
+++ b/roles/automation-integration/templates/do-enlist-compute-node.j2
@@ -18,10 +18,10 @@
 cat $INV >> $LOG
 echo "END INVENTORY_FILE" >> $LOG
 
-echo "cd /opt/cord/build/platform-install; ansible-playbook --private-key=/etc/maas/ansible/id_rsa -u $COMPUTE_USER --extra-vars '@{{ cord_dir }}/build/genconfig/config.yml' -i $INV cord-compute-playbook.yml" >> $LOG
+echo "cd /opt/cord/build/platform-install; ansible-playbook --private-key=/etc/maas/ansible/id_rsa -u $COMPUTE_USER --extra-vars '@{{ head_cord_dir }}/build/genconfig/config.yml' -i $INV cord-compute-maas-playbook.yml" >> $LOG
 
 cd /opt/cord/build/platform-install
-ansible-playbook --private-key=/etc/maas/ansible/id_rsa -u $COMPUTE_USER --extra-vars '@{{ cord_dir }}/build/genconfig/config.yml' -i $INV cord-compute-maas-playbook.yml >> $LOG
+ansible-playbook --private-key=/etc/maas/ansible/id_rsa -u $COMPUTE_USER --extra-vars '@{{ head_cord_dir }}/build/genconfig/config.yml' -i $INV cord-compute-maas-playbook.yml >> $LOG
 
 RESULT=$?
 rm $INV
diff --git a/roles/compute-node-config/defaults/main.yml b/roles/compute-node-config/defaults/main.yml
index 70507cc..dffca62 100644
--- a/roles/compute-node-config/defaults/main.yml
+++ b/roles/compute-node-config/defaults/main.yml
@@ -1,10 +1,14 @@
 ---
 # compute-node-config/defaults/main.yml
 
-cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+# location of cord_profile on head node
+head_cord_profile_dir: /opt/cord_profile
+
+# name of the external interface on compute nodes. Should have the ansible_ prefix.
+# `fabric` is default in R-CORD
+compute_external_interface: ansible_fabric
 
 # service configs referenced here are likely located in cord-profile/templates
-
 # used in openstack-compute-vtn.yaml.j2, referencing network in management-net.yaml.j2
 use_management_hosts: False
 vtn_management_host_net_interface: veth3
@@ -12,3 +16,4 @@
 # used in openstack-compute-vtn.yaml.j2, referencing service in fabric.yaml.j2
 use_fabric: False
 
+
diff --git a/roles/compute-node-config/tasks/main.yml b/roles/compute-node-config/tasks/main.yml
index 6500dbb..c592bf7 100644
--- a/roles/compute-node-config/tasks/main.yml
+++ b/roles/compute-node-config/tasks/main.yml
@@ -6,7 +6,7 @@
 - name: Create OpenStack compute node TOSCA
   template:
     src: "{{ item }}.j2"
-    dest: "{{ cord_profile_dir }}/{{ item }}"
+    dest: "{{ head_cord_profile_dir }}/{{ item }}"
     owner: "{{ ansible_user_id }}"
     mode: 0644
   with_items:
diff --git a/roles/compute-node-enable-maas/defaults/main.yml b/roles/compute-node-enable-maas/defaults/main.yml
index de6b9a3..f2b3a99 100644
--- a/roles/compute-node-enable-maas/defaults/main.yml
+++ b/roles/compute-node-enable-maas/defaults/main.yml
@@ -2,7 +2,7 @@
 # compute-node-enable-maas/defaults/main.yml
 
 credentials_dir: "{{ playbook_dir }}/credentials"
-cord_profile_dir: "{{ ansible_user_dir ~ '/cord_profile' }}"
+head_cord_profile_dir: "/opt/cord_profile"
 
 xos_admin_user: "xosadmin@opencord.org"
 xos_admin_pass: "{{ lookup('password', credentials_dir ~ '/xosadmin@opencord.org chars=ascii_letters,digits') }}"
diff --git a/roles/compute-node-enable-maas/tasks/main.yml b/roles/compute-node-enable-maas/tasks/main.yml
index 9892aa1..0c1fd39 100644
--- a/roles/compute-node-enable-maas/tasks/main.yml
+++ b/roles/compute-node-enable-maas/tasks/main.yml
@@ -1,9 +1,9 @@
 ---
 # compute-node-enable-maas/tasks/main.yml
 
-- name: Fetch generated nodes.yaml file
+- name: Fetch generated compute node onboarding TOSCA files
   fetch:
-    src: "{{ cord_profile_dir + '/' + item }}"
+    src: "{{ head_cord_profile_dir + '/' + item }}"
     dest: "/tmp/{{ item }}"
     flat: yes
     fail_on_missing: yes
diff --git a/roles/compute-node-enable/defaults/main.yml b/roles/compute-node-enable/defaults/main.yml
index a3a1e7c..6f52840 100644
--- a/roles/compute-node-enable/defaults/main.yml
+++ b/roles/compute-node-enable/defaults/main.yml
@@ -1,5 +1,5 @@
 ---
 # compute-node-enable/defaults/main.yml
 
-cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+head_cord_profile_dir: "/opt/cord_profile"
 
diff --git a/roles/compute-node-enable/tasks/main.yml b/roles/compute-node-enable/tasks/main.yml
index f746241..60c6651 100644
--- a/roles/compute-node-enable/tasks/main.yml
+++ b/roles/compute-node-enable/tasks/main.yml
@@ -2,7 +2,7 @@
 # compute-node-enable/tasks/main.yml
 
 - name: Load TOSCA to add OpenStack compute nodes
-  command: "python /opt/xos/tosca/run.py {{ xos_admin_user }} {{ cord_profile_dir }}/{{ item }}"
+  command: "python /opt/xos/tosca/run.py {{ xos_admin_user }} {{ head_cord_profile_dir }}/{{ item }}"
   with_items:
     - openstack.yaml
     - openstack-compute.yaml
@@ -14,7 +14,7 @@
     seconds: 20
 
 - name: Load TOSCA to enable VTN on OpenStack compute nodes
-  command: "python /opt/xos/tosca/run.py {{ xos_admin_user }} {{ cord_profile_dir }}/{{ item }}"
+  command: "python /opt/xos/tosca/run.py {{ xos_admin_user }} {{ head_cord_profile_dir }}/{{ item }}"
   with_items:
     - vtn-service.yaml
     - openstack-compute-vtn.yaml
diff --git a/roles/copy-cord/defaults/main.yml b/roles/copy-cord/defaults/main.yml
new file mode 100644
index 0000000..962c8db
--- /dev/null
+++ b/roles/copy-cord/defaults/main.yml
@@ -0,0 +1,5 @@
+---
+# copy-cord/defaults/main.yml
+
+config_cord_dir: "{{ ansible_user_dir + '/cord' }}"
+head_cord_dir: "/opt/cord"
diff --git a/roles/copy-cord/tasks/main.yml b/roles/copy-cord/tasks/main.yml
new file mode 100644
index 0000000..1cf241a
--- /dev/null
+++ b/roles/copy-cord/tasks/main.yml
@@ -0,0 +1,18 @@
+---
+# copy-cord/tasks/main.yml
+
+- name: Copy (sync) the cord directory to head node
+  become: yes
+  synchronize:
+    src: "{{ config_cord_dir }}/"
+    dest: "{{ head_cord_dir }}/"
+
+- name: Set ownership on CORD dir on head node
+  become: yes
+  file:
+    dest: "{{ head_cord_dir }}"
+    state: directory
+    recurse: yes
+    owner: "{{ ansible_user_id }}"
+    group: "{{ ansible_user_gid }}"
+
diff --git a/roles/copy-credentials/defaults/main.yml b/roles/copy-credentials/defaults/main.yml
new file mode 100644
index 0000000..cf19dfd
--- /dev/null
+++ b/roles/copy-credentials/defaults/main.yml
@@ -0,0 +1,4 @@
+---
+# copy-credentials/defaults/main.yml
+
+credentials_dir: "{{ playbook_dir }}/credentials"
diff --git a/roles/copy-credentials/tasks/main.yml b/roles/copy-credentials/tasks/main.yml
new file mode 100644
index 0000000..3d3af3e
--- /dev/null
+++ b/roles/copy-credentials/tasks/main.yml
@@ -0,0 +1,18 @@
+---
+# copy-credentials/tasks/main.yml
+
+- name: Copy (sync) the credentials directory to head node, for MaaS provisioner
+  become: yes
+  synchronize:
+    src: "{{ credentials_dir }}/"
+    dest: "/opt/credentials/"
+
+- name: Set ownership on credentials dir on head node, for MaaS provisioner
+  become: yes
+  file:
+    dest: "/opt/credentials"
+    state: directory
+    recurse: yes
+    owner: "{{ ansible_user_id }}"
+    group: "{{ ansible_user_gid }}"
+
diff --git a/roles/copy-profile/defaults/main.yml b/roles/copy-profile/defaults/main.yml
new file mode 100644
index 0000000..c4f7979
--- /dev/null
+++ b/roles/copy-profile/defaults/main.yml
@@ -0,0 +1,5 @@
+---
+# copy-profile/defaults/main.yml
+
+config_cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+head_cord_profile_dir: "/opt/cord_profile"
diff --git a/roles/copy-profile/tasks/main.yml b/roles/copy-profile/tasks/main.yml
new file mode 100644
index 0000000..c206a3f
--- /dev/null
+++ b/roles/copy-profile/tasks/main.yml
@@ -0,0 +1,17 @@
+---
+# copy-profile/tasks/main.yml
+
+- name: Copy (sync) the cord_profile directory structure to head node
+  synchronize:
+    src: "{{ config_cord_profile_dir }}/"
+    dest: "{{ head_cord_profile_dir }}/"
+    delete: yes
+
+- name: Set ownership on cord_profile dir on head node
+  file:
+    dest: "{{ head_cord_profile_dir }}"
+    state: directory
+    recurse: yes
+    owner: "{{ ansible_user_id }}"
+    group: "{{ ansible_user_gid }}"
+
diff --git a/roles/cord-profile/defaults/main.yml b/roles/cord-profile/defaults/main.yml
index 7c25a49..542ac64 100644
--- a/roles/cord-profile/defaults/main.yml
+++ b/roles/cord-profile/defaults/main.yml
@@ -1,16 +1,22 @@
 ---
 # cord-profile/defaults/main.yml
 
-cord_dir: "{{ ansible_user_dir + '/cord' }}"
-cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+# where the cord_profile directory is on the config node
+config_cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+
 pki_dir: "{{ playbook_dir }}/pki"
+ssh_pki_dir: "{{ playbook_dir }}/ssh_pki"
 credentials_dir: "{{ playbook_dir }}/credentials"
 
-deploy_docker_registry: ""
+# where cord files are copied to on head node
+head_cord_profile_dir: "/opt/cord_profile"
+head_cord_dir: "/opt/cord"
+
+deploy_docker_registry: "" # was: "localhost:5000/"
 deploy_docker_tag: "candidate"
 
 # name of docker image to use in onboarding synchronizer
-xos_docker_image: "xosproject/xos:candidate"
+xos_docker_image: "{{ deploy_docker_registry }}xosproject/xos{{ deploy_docker_tag }}"
 
 # For storing OpenStack images
 image_dir: /opt/images
@@ -42,10 +48,10 @@
 xos_libraries:
   - "ng-xos-lib"
 
-xos_services: []
-xos_service_sshkeys: []
+# name of master ssh key for this pod
+pod_sshkey_name: "headnode"
 
-xos_images: []
+xos_services: []
 
 xos_tosca_config_templates: []
 
@@ -62,6 +68,9 @@
 xos_dir: /opt/xos
 
 # GUI Config [new GUI], used in app.config.js.j2 and style.config.js.j2
+
+enabled_gui_extensions: []
+
 gw_port: 3000
 gui_api_endpoint: "/xosapi/v1"
 gui_websocket: "/"
@@ -94,6 +103,7 @@
 
 site_name: sitename
 site_humanname: "Site HumanName"
+site_suffix: sitename.test
 
 deployment_type: deploymenttype
 
diff --git a/roles/cord-profile/tasks/main.yml b/roles/cord-profile/tasks/main.yml
index 7a5fffb..2edba24 100644
--- a/roles/cord-profile/tasks/main.yml
+++ b/roles/cord-profile/tasks/main.yml
@@ -2,61 +2,57 @@
 # cord-profile/tasks/main.yml
 # Constructs a CORD service profile directory and configuration files
 
-- name: Create and copy XOS admin password
-  copy:
-    content: "{{ xos_admin_pass }}"
-    dest: "{{ cord_dir }}/build/platform-install/credentials/{{ xos_admin_user }}"
-
 - name: Create cord_profile directory
   become: yes
   file:
-    path: "{{ cord_profile_dir }}"
+    path: "{{ config_cord_profile_dir }}"
     state: directory
     mode: 0755
     owner: "{{ ansible_user_id }}"
     group: "{{ ansible_user_gid }}"
 
-- name: Create cord_profile/profile_name, containing profile name
+- name: Create cord_profile/profile_name file containing profile name
   copy:
-    dest: "{{ cord_profile_dir }}/profile_name"
+    dest: "{{ config_cord_profile_dir }}/profile_name"
     content: "{{ cord_profile }}"
     mode: 0644
 
 - name: Create subdirectories inside cord_profile directory
   file:
-    path: "{{ cord_profile_dir }}/{{ item }}"
+    path: "{{ config_cord_profile_dir }}/{{ item }}"
     state: directory
     mode: 0755
   with_items:
     - key_import
-    - onboarding-docker-compose
     - images
 
-# *** This should be revisited. ***
-# Currently the key pair is generated on the head node by the
-# "prep" role in the "maas" repo, invoked during the "deployBase" Gradle task.
-# The keys should probably be generated earlier, in the corddev VM, and copied over.
-# The /opt/credentials directory might be a good place to keep the generated keys.
-#
-# Ensure a keypair exists in case we're not running on MaaS.
-- name: Ensure keypair
-  user:
-    name: "{{ ansible_user_id }}"
-    generate_ssh_key: yes
-
-- name: Copy ssh keys to key_import directory
+- name: Copy ssh private key to node_key file
   copy:
-    # 'expanduser' won't work below, it expands on control machine
-    src: "{{ item.source_path | replace('~', ansible_user_dir, 1) }}"
-    dest: "{{ cord_profile_dir }}/key_import/{{ item.name }}"
+    src: "{{ ssh_pki_dir }}/client_certs/{{ pod_sshkey_name }}_sshkey"
+    dest: "{{ config_cord_profile_dir }}/node_key"
     mode: 0600
     remote_src: True
-  with_items: "{{ xos_service_sshkeys }}"
+
+- name: Copy ssh private key to key_import directory for services that require it
+  copy:
+    src: "{{ ssh_pki_dir }}/client_certs/{{ pod_sshkey_name }}_sshkey"
+    dest: "{{ config_cord_profile_dir }}/key_import/{{ item.keypair }}"
+    mode: 0600
+    remote_src: True
+  with_items: "{{ xos_services | selectattr('keypair', 'defined') | list }}"
+
+- name: Copy ssh public key to key_import directory for services that require it
+  copy:
+    src: "{{ ssh_pki_dir }}/client_certs/{{ pod_sshkey_name }}_sshkey.pub"
+    dest: "{{ config_cord_profile_dir }}/key_import/{{ item.keypair }}.pub"
+    mode: 0644
+    remote_src: True
+  with_items: "{{ xos_services | selectattr('keypair', 'defined') | list }}"
 
 - name: Copy cert chain and core api key and cert
   copy:
     src: "{{ pki_dir }}/{{ item.src }}"
-    dest: "{{ cord_profile_dir }}/{{ item.dest }}"
+    dest: "{{ config_cord_profile_dir }}/{{ item.dest }}"
     mode: 0600
   with_items:
     - src: "{{ site_name }}_im_ca/private/xos-core.{{ site_suffix }}_key.pem"
@@ -66,45 +62,10 @@
     - src: "{{ site_name }}_im_ca/certs/im_cert_chain.pem"
       dest: "im_cert_chain.pem"
 
-- name: Get localhost facts (to get local uid and gid)
-  setup:
-  delegate_to: localhost
-  delegate_facts: True
-
-- name: Make local images directory
-  delegate_to: localhost
-  become: yes
-  file:
-    path: "{{ image_dir }}"
-    state: directory
-    mode: 0755
-    owner: "{{ hostvars['localhost']['ansible_user_id'] }}"
-    group: "{{ hostvars['localhost']['ansible_user_gid'] }}"
-
-- name: Download Glance VM images
-  when: use_openstack
-  delegate_to: localhost
-  get_url:
-    url: "{{ item.url }}"
-    checksum: "{{ item.checksum }}"
-    dest: "{{ image_dir }}/{{ item.name }}.qcow2"
-  with_items: "{{ xos_images }}"
-  register: glance_vm_result
-  until: glance_vm_result|success
-  retries: 5
-  delay: 10
-
-- name: Copy Glance VM images to profile directory
-  when: use_openstack
-  copy:
-    src: "{{ image_dir }}/{{ item.name }}.qcow2"
-    dest: "{{ cord_profile_dir }}/images/{{ item.name }}.qcow2"
-  with_items: "{{ xos_images }}"
-
 - name: Copy over commonly used and utility TOSCA files
   copy:
     src: "{{ item }}"
-    dest: "{{ cord_profile_dir }}/{{ item }}"
+    dest: "{{ config_cord_profile_dir }}/{{ item }}"
   with_items:
     - fixtures.yaml
     - enable-onboarding.yaml
@@ -113,7 +74,7 @@
 - name: Create templated XOS configuration files
   template:
     src: "{{ item }}.j2"
-    dest: "{{ cord_profile_dir }}/{{ item }}"
+    dest: "{{ config_cord_profile_dir }}/{{ item }}"
     mode: 0644
   with_items:
     - xos_common_config
@@ -130,36 +91,21 @@
 - name: Create profile specific templated TOSCA config files
   template:
     src: "{{ item }}.j2"
-    dest: "{{ cord_profile_dir }}/{{ item }}"
+    dest: "{{ config_cord_profile_dir }}/{{ item }}"
   with_items: "{{ xos_tosca_config_templates }}"
 
 - name: Create profile specific templated non-TOSCA files
   template:
     src: "{{ item }}.j2"
-    dest: "{{ cord_profile_dir }}/{{ item }}"
+    dest: "{{ config_cord_profile_dir }}/{{ item }}"
   with_items: "{{ xos_other_templates }}"
 
-- name: Copy node key
-  when: not on_maas and use_openstack
-  copy:
-    src: "{{ ansible_user_dir }}/.ssh/id_rsa"
-    dest: "{{ item }}/node_key"
-    owner: "{{ ansible_user }}"
-    mode: 0600
-    remote_src: True
+- name: Create OpenStack config and TOSCA onboarding
+  when: use_openstack
+  template:
+    src: "{{ item }}.j2"
+    dest: "{{ config_cord_profile_dir }}/{{ item }}"
   with_items:
-    - "{{ ansible_user_dir }}"
-    - "{{ cord_profile_dir }}"
+    - openstack.yaml
+    - admin-openrc.sh
 
-- name: Copy node key (MaaS)
-  when: on_maas and use_openstack
-  become: yes
-  copy:
-    src: "{{ maas_node_key }}"
-    dest: "{{ item }}/node_key"
-    owner: "{{ ansible_user }}"
-    mode: 0600
-    remote_src: True
-  with_items:
-    - "{{ ansible_user_dir }}"
-    - "{{ cord_profile_dir }}"
diff --git a/roles/juju-setup/templates/admin-openrc.sh.j2 b/roles/cord-profile/templates/admin-openrc.sh.j2
similarity index 100%
rename from roles/juju-setup/templates/admin-openrc.sh.j2
rename to roles/cord-profile/templates/admin-openrc.sh.j2
diff --git a/roles/cord-profile/templates/docker-compose.yml.j2 b/roles/cord-profile/templates/docker-compose.yml.j2
index 29fa893..b64e837 100644
--- a/roles/cord-profile/templates/docker-compose.yml.j2
+++ b/roles/cord-profile/templates/docker-compose.yml.j2
@@ -1,7 +1,7 @@
 version: '2'
 
 # XOS docker compose
-# generated by platform-install/roles/cord-profile
+# generated by cord-profile/templates/docker-compose.yml.j2
 
 networks:
 {% for network in xos_docker_networks %}
@@ -94,8 +94,8 @@
       - xos_ws
       - xos_chameleon
     volumes:
-      - {{ cord_profile_dir }}/style.config.js:/var/www/dist/style.config.js
-      - {{ cord_profile_dir }}/app.config.js:/var/www/dist/app.config.js
+      - {{ head_cord_profile_dir }}/style.config.js:/var/www/dist/style.config.js
+      - {{ head_cord_profile_dir }}/app.config.js:/var/www/dist/app.config.js
     volumes_from:
       - gui_extensions_store
     logging:
@@ -171,7 +171,7 @@
       - xos_redis
 {% endif %}
     volumes:
-      - {{ cord_profile_dir }}/gateway-config.yml:/var/www/src/config/gateway-config.yml
+      - {{ head_cord_profile_dir }}/gateway-config.yml:/var/www/src/config/gateway-config.yml
     logging:
       driver: "json-file"
       options:
@@ -204,11 +204,11 @@
       - xos_redis
 {% endif %}
     volumes:
-      - {{ cord_profile_dir }}/xos_common_config:/opt/xos/xos_configuration/xos_common_config:ro
-      - {{ cord_profile_dir }}/xos_config.yaml:/opt/xos/xos_config.yaml:ro
-      - {{ cord_profile_dir }}:/opt/cord_profile:ro
-      - {{ cord_dir }}/orchestration/xos_libraries/ng-xos-lib:/opt/xos_libraries/ng-xos-lib:ro
-      - {{ cord_profile_dir }}/im_cert_chain.pem:/usr/local/share/ca-certificates/local_certs.crt:ro
+      - {{ head_cord_profile_dir }}/xos_common_config:/opt/xos/xos_configuration/xos_common_config:ro
+      - {{ head_cord_profile_dir }}/xos_config.yaml:/opt/xos/xos_config.yaml:ro
+      - {{ head_cord_profile_dir }}:/opt/cord_profile:ro
+      - {{ head_cord_dir }}/orchestration/xos_libraries/ng-xos-lib:/opt/xos_libraries/ng-xos-lib:ro
+      - {{ head_cord_profile_dir }}/im_cert_chain.pem:/usr/local/share/ca-certificates/local_certs.crt:ro
     logging:
       driver: "json-file"
       options:
@@ -243,11 +243,11 @@
       - xos_redis
 {% endif %}
     volumes:
-      - {{ cord_profile_dir }}/xos_common_config:/opt/xos/xos_configuration/xos_common_config:ro
-      - {{ cord_profile_dir }}/xos_config.yaml:/opt/xos/xos_config.yaml:ro
-      - {{ cord_profile_dir }}:/opt/cord_profile:ro
-      - {{ cord_dir }}/orchestration/xos_libraries/ng-xos-lib:/opt/xos_libraries/ng-xos-lib:ro
-      - {{ cord_profile_dir }}/im_cert_chain.pem:/usr/local/share/ca-certificates/local_certs.crt:ro
+      - {{ head_cord_profile_dir }}/xos_common_config:/opt/xos/xos_configuration/xos_common_config:ro
+      - {{ head_cord_profile_dir }}/xos_config.yaml:/opt/xos/xos_config.yaml:ro
+      - {{ head_cord_profile_dir }}:/opt/cord_profile:ro
+      - {{ head_cord_dir }}/orchestration/xos_libraries/ng-xos-lib:/opt/xos_libraries/ng-xos-lib:ro
+      - {{ head_cord_profile_dir }}/im_cert_chain.pem:/usr/local/share/ca-certificates/local_certs.crt:ro
       - /var/run/docker.sock:/var/run/docker.sock
     logging:
       driver: "json-file"
@@ -280,7 +280,7 @@
 
 {% if not frontend_only %}
 {% for svc in xos_services %}
-{% if svc.synchronizer is defined and svc.synchronizer %}
+{% if svc.synchronizer is not defined or svc.synchronizer %}
   {{ svc.name }}-synchronizer:
     image: {{ deploy_docker_registry }}xosproject/{{ svc.name }}-synchronizer:{{ deploy_docker_tag }}
     networks:
@@ -298,14 +298,14 @@
       - xos_redis:redis
 {% endif %}
     volumes:
-      - {{ cord_profile_dir }}/node_key:/opt/cord_profile/node_key:ro
-      - {{ cord_dir }}/build/platform-install/credentials/xosadmin@opencord.org:/opt/xos/services/{{ svc.name }}/credentials/xosadmin@opencord.org:ro
-      - {{ cord_profile_dir }}/im_cert_chain.pem:/usr/local/share/ca-certificates/local_certs.crt:ro
+      - {{ head_cord_profile_dir }}/node_key:/opt/cord_profile/node_key:ro
+      - /opt/credentials:/opt/xos/services/{{ svc.name }}/credentials:ro
+      - {{ head_cord_profile_dir }}/im_cert_chain.pem:/usr/local/share/ca-certificates/local_certs.crt:ro
 {% if svc.keypair is defined %}
-      - {{ cord_profile_dir }}/key_import/{{ svc.keypair }}:/opt/xos/services/{{ svc.name }}/keys/{{ svc.keypair }}:ro
+      - {{ head_cord_profile_dir }}/key_import/{{ svc.keypair }}:/opt/xos/services/{{ svc.name }}/keys/{{ svc.keypair }}:ro
 {% endif %}
 {% if svc.name == "openstack" %}
-      - {{ cord_profile_dir }}/images:/opt/xos/images:ro
+      - {{ head_cord_profile_dir }}/images:/opt/xos/images:ro
 {% endif %}
     logging:
       driver: "json-file"
diff --git a/roles/cord-profile/templates/xos-bootstrap-docker-compose.yaml.j2 b/roles/cord-profile/templates/xos-bootstrap-docker-compose.yaml.j2
deleted file mode 100644
index 06c20d2..0000000
--- a/roles/cord-profile/templates/xos-bootstrap-docker-compose.yaml.j2
+++ /dev/null
@@ -1,104 +0,0 @@
-version: '2'
-
-# XOS bootstrap docker compose
-# generated by platform-install/roles/cord-profile
-
-networks:
-{% for network in xos_docker_networks %}
-  {{ network }}:
-    external: true
-{% endfor %}
-
-services:
-  xos_db:
-    image: {{ deploy_docker_registry }}xosproject/xos-postgres:{{ deploy_docker_tag }}
-    networks:
-{% for network in xos_docker_networks %}
-      - {{ network }}
-{% endfor %}
-    expose:
-      - "5432"
-
-{% if use_redis %}
-  xos_redis:
-    image: {{ deploy_docker_registry }}redis:{{ deploy_docker_tag }}
-    networks:
-{% for network in xos_docker_networks %}
-     - {{ network }}
-{% endfor %}
-    logging:
-      driver: "json-file"
-      options:
-        max-size: "1000k"
-        max-file: "5"
-{% endif %}
-
-  xos_bootstrap_ui:
-    image: {{ deploy_docker_registry }}xosproject/xos:{{ deploy_docker_tag }}
-    command: python /opt/xos/manage.py runserver 0.0.0.0:{{ xos_bootstrap_ui_port }} --insecure --makemigrations
-    networks:
-{% for network in xos_docker_networks %}
-     - {{ network }}
-{% endfor %}
-    labels:
-      org.xosproject.kind: userinterface
-      org.xosproject.target: bootstrap
-    links:
-      - xos_db
-{% if use_redis %}
-      - xos_redis:redis
-{% endif %}
-    volumes:
-      - .:/opt/cord_profile:ro
-      - ./xos_common_config:/opt/xos/xos_configuration/xos_common_config:ro
-{% for service in xos_services %}
-      - {{ cord_dir }}/{{ service.path }}:/opt/xos_services/{{ service.path | basename }}:ro
-{% endfor %}
-{% for library in xos_libraries %}
-      - {{ cord_dir }}/orchestration/xos_libraries/{{ library }}:/opt/xos_libraries/{{ library }}:ro
-{% endfor %}
-{% for volume in xos_docker_volumes %}
-      - {{ volume.host }}:{{ volume.container }}{{ ":rw" if (volume.read_only is defined and not volume.read_only ) else ":ro" }}
-{% endfor %}
-    ports:
-      - "{{ xos_bootstrap_ui_port }}:{{ xos_bootstrap_ui_port }}"
-    logging:
-      driver: "json-file"
-      options:
-        max-size: "1000k"
-        max-file: "5"
-    depends_on:
-      - xos_db
-{% if use_redis %}
-      - xos_redis
-{% endif %}
-
-  xos_synchronizer_onboarding:
-    image: {{ deploy_docker_registry }}xosproject/xos:{{ deploy_docker_tag }}
-    command: bash -c "cd /opt/xos/synchronizers/onboarding; ./run.sh"
-    networks:
-{% for network in xos_docker_networks %}
-     - {{ network }}
-{% endfor %}
-    labels:
-      org.xosproject.kind: synchronizer
-      org.xosproject.target: onboarding
-    links:
-      - xos_db
-    volumes:
-      - /var/run/docker.sock:/var/run/docker.sock
-      - ./key_import:/opt/xos/key_import:ro
-      - ./onboarding-docker-compose:/opt/xos/synchronizers/onboarding/docker-compose
-{% for service in xos_services %}
-      - {{ cord_dir }}/{{ service.path }}:/opt/xos_services/{{ service.path | basename }}:ro
-{% endfor %}
-{% for library in xos_libraries %}
-      - {{ cord_dir }}/orchestration/xos_libraries/{{ library }}:/opt/xos_libraries/{{ library }}:ro
-{% endfor %}
-    logging:
-      driver: "json-file"
-      options:
-        max-size: "1000k"
-        max-file: "5"
-    depends_on:
-      - xos_db
diff --git a/roles/cord-profile/templates/xos.yaml.j2 b/roles/cord-profile/templates/xos.yaml.j2
index 4bd792f..553f9b1 100644
--- a/roles/cord-profile/templates/xos.yaml.j2
+++ b/roles/cord-profile/templates/xos.yaml.j2
@@ -11,4 +11,3 @@
     xos:
       type: tosca.nodes.XOS
 
-
diff --git a/roles/cord-profile/templates/xos_common_config.j2 b/roles/cord-profile/templates/xos_common_config.j2
index 175be92..ba67acd 100644
--- a/roles/cord-profile/templates/xos_common_config.j2
+++ b/roles/cord-profile/templates/xos_common_config.j2
@@ -41,7 +41,7 @@
 dependency_graph=/opt/xos/model-deps
 logfile=/var/log/xos_backend.log
 save_ansible_output=True
-node_key={{ cord_profile_dir }}/node_key
+node_key={{ head_cord_profile_dir }}/node_key
 
 [gui]
 disable_minidashboard={{ disable_minidashboard }}
diff --git a/roles/exampleservice-config/defaults/main.yml b/roles/exampleservice-config/defaults/main.yml
index 82098e0..0ce1f31 100644
--- a/roles/exampleservice-config/defaults/main.yml
+++ b/roles/exampleservice-config/defaults/main.yml
@@ -1,6 +1,6 @@
 ---
 # exampleservice-config/defaults/main.yml
 
-cord_dir: "{{ ansible_user_dir + '/cord' }}"
-cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
-
+# As a test, this is assumed to entirely run on the head node
+head_cord_dir: "/opt/cord"
+head_cord_profile_dir: "/opt/cord_profile"
diff --git a/roles/exampleservice-config/tasks/main.yml b/roles/exampleservice-config/tasks/main.yml
index dc21c17..c07838c 100644
--- a/roles/exampleservice-config/tasks/main.yml
+++ b/roles/exampleservice-config/tasks/main.yml
@@ -6,7 +6,7 @@
     remote_src: True # file is local to the remote machine
     force: False # only copy if destination file doesn't exist
     src: "/dev/null"
-    dest: "{{ cord_profile_dir }}/key_import/{{ item }}"
+    dest: "{{ head_cord_profile_dir }}/key_import/{{ item }}"
     mode: 0600
   with_items:
     - exampleservice_rsa
@@ -14,16 +14,16 @@
 
 - name: Copy exampleservice onboarding TOSCA files to cord_profile
   copy:
-    src: "{{ cord_dir }}/orchestration/xos_services/exampleservice/xos/exampleservice-onboard.yaml"
-    dest: "{{ cord_profile_dir }}/exampleservice-onboard.yaml"
+    src: "{{ head_cord_dir }}/orchestration/xos_services/exampleservice/xos/exampleservice-onboard.yaml"
+    dest: "{{ head_cord_profile_dir }}/exampleservice-onboard.yaml"
 
 - name: TOSCA to mount exampleservice volume in XOS container
   template:
     src: "xos-exampleservice.yaml.j2"
-    dest: "{{ cord_profile_dir }}/xos-exampleservice.yaml"
+    dest: "{{ head_cord_profile_dir }}/xos-exampleservice.yaml"
 
 - name: TOSCA to create exampleservice test config
   template:
     src: "test-exampleservice.yaml.j2"
-    dest: "{{ cord_profile_dir }}/test-exampleservice.yaml"
+    dest: "{{ head_cord_profile_dir }}/test-exampleservice.yaml"
 
diff --git a/roles/exampleservice-config/templates/xos-exampleservice.yaml.j2 b/roles/exampleservice-config/templates/xos-exampleservice.yaml.j2
index 441e075..1baba82 100644
--- a/roles/exampleservice-config/templates/xos-exampleservice.yaml.j2
+++ b/roles/exampleservice-config/templates/xos-exampleservice.yaml.j2
@@ -15,7 +15,7 @@
     /opt/xos_services/exampleservice:
       type: tosca.nodes.XOSVolume
       properties:
-          host_path: "{{ cord_dir }}/orchestration/xos_services/exampleservice"
+          host_path: "{{ head_cord_dir }}/orchestration/xos_services/exampleservice"
           read_only: True
       requirements:
           - xos:
diff --git a/roles/glance-images/defaults/main.yml b/roles/glance-images/defaults/main.yml
new file mode 100644
index 0000000..8b07f0b
--- /dev/null
+++ b/roles/glance-images/defaults/main.yml
@@ -0,0 +1,9 @@
+---
+# glance-images/defaults/main.yml
+
+use_openstack: True
+
+image_dir: /opt/images
+
+xos_images: []
+
diff --git a/roles/glance-images/tasks/main.yml b/roles/glance-images/tasks/main.yml
new file mode 100644
index 0000000..90cbaec
--- /dev/null
+++ b/roles/glance-images/tasks/main.yml
@@ -0,0 +1,33 @@
+---
+# glance-images/tasks/main.yml
+
+- name: Make images directory
+  when: use_openstack
+  become: yes
+  file:
+    path: "{{ image_dir }}"
+    state: directory
+    mode: 0755
+    owner: "{{ ansible_user_id }}"
+    group: "{{ ansible_user_gid }}"
+
+- name: Download Glance VM images
+  when: use_openstack
+  get_url:
+    url: "{{ item.url }}"
+    checksum: "{{ item.checksum }}"
+    dest: "{{ image_dir }}/{{ item.name }}.qcow2"
+  with_items: "{{ xos_images }}"
+  register: glance_vm_result
+  until: glance_vm_result|success
+  retries: 5
+  delay: 10
+
+- name: Copy Glance VM images to profile directory
+  when: use_openstack
+  copy:
+    remote_src: yes
+    src: "{{ image_dir }}/{{ item.name }}.qcow2"
+    dest: "/opt/cord_profile/images/{{ item.name }}.qcow2"
+  with_items: "{{ xos_images }}"
+
diff --git a/roles/head-diag/tasks/main.yml b/roles/head-diag/tasks/main.yml
index e73417b..d2e53fb 100644
--- a/roles/head-diag/tasks/main.yml
+++ b/roles/head-diag/tasks/main.yml
@@ -41,7 +41,7 @@
    - "juju status --format=json"
 
 - name: OpenStack diag collection
-  shell: "source ~/admin-openrc.sh && {{ item }} > ~/{{ diag_dir }}/openstack/{{ item | regex_replace('[^\\w-]', '_')}}"
+  shell: "source /opt/cord_profile/admin-openrc.sh && {{ item }} > ~/{{ diag_dir }}/openstack/{{ item | regex_replace('[^\\w-]', '_')}}"
   ignore_errors: yes
   args:
     executable: "/bin/bash"
diff --git a/roles/juju-compute-setup/tasks/main.yml b/roles/juju-compute-setup/tasks/main.yml
index f4e3918..d6a92c8 100644
--- a/roles/juju-compute-setup/tasks/main.yml
+++ b/roles/juju-compute-setup/tasks/main.yml
@@ -59,7 +59,7 @@
   with_items: "{{ groups['compute'] }}"
 
 - name: verify that the nodes appear in nova
-  action: shell bash -c "source ~/admin-openrc.sh; nova hypervisor-list | grep '{{ item }}'"
+  action: shell bash -c "source /opt/cord_profile/admin-openrc.sh; nova hypervisor-list | grep '{{ item }}'"
   register: result
   until: result | success
   retries: 20
diff --git a/roles/juju-setup/tasks/main.yml b/roles/juju-setup/tasks/main.yml
index d1607b8..115857d 100644
--- a/roles/juju-setup/tasks/main.yml
+++ b/roles/juju-setup/tasks/main.yml
@@ -86,11 +86,3 @@
   tags:
    - skip_ansible_lint # benign to do this more than once, hard to check for
 
-- name: Create admin-openrc.sh OpenStack credentials file
-  template:
-    src: admin-openrc.sh.j2
-    dest: "{{ item }}/admin-openrc.sh"
-  with_items:
-    - "{{ ansible_user_dir }}"
-    - "{{ cord_profile_dir }}"
-
diff --git a/roles/monitoringservice-config/defaults/main.yml b/roles/monitoringservice-config/defaults/main.yml
index 5016f47..2514331 100644
--- a/roles/monitoringservice-config/defaults/main.yml
+++ b/roles/monitoringservice-config/defaults/main.yml
@@ -1,6 +1,9 @@
 ---
 # monitoringservice-config/defaults/main.yml
 
-cord_dir: "{{ ansible_user_dir + '/cord' }}"
-cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+#paths
+config_cord_dir: "{{ ansible_user_dir + '/cord' }}"
+head_cord_dir: "/opt/cord"
+
+head_cord_profile_dir: "/opt/cord_profile"
 
diff --git a/roles/monitoringservice-config/tasks/main.yml b/roles/monitoringservice-config/tasks/main.yml
index c511f09..4b925a9 100644
--- a/roles/monitoringservice-config/tasks/main.yml
+++ b/roles/monitoringservice-config/tasks/main.yml
@@ -3,18 +3,18 @@
 
 - name: Copy monitoringservice onboarding TOSCA files to cord_profile
   copy:
-    src: "{{ cord_dir }}/orchestration/xos_services/monitoring/xos/monitoring-onboard.yaml"
-    dest: "{{ cord_profile_dir }}/monitoring-onboard.yaml"
+    src: "{{ config_cord_dir }}/orchestration/xos_services/monitoring/xos/monitoring-onboard.yaml"
+    dest: "{{ head_cord_profile_dir }}/monitoring-onboard.yaml"
 
 - name: TOSCA to mount monitoringservice volume in XOS container
   template:
     src: "xos-monitoringservice.yaml.j2"
-    dest: "{{ cord_profile_dir }}/xos-monitoringservice.yaml"
+    dest: "{{ head_cord_profile_dir }}/xos-monitoringservice.yaml"
 
 - name: TOSCA files to instantiate monitoringservice
   template:
     src: "{{ item }}.j2"
-    dest: "{{ cord_profile_dir }}/{{ item }}"
+    dest: "{{ head_cord_profile_dir }}/{{ item }}"
   with_items:
     - "monitoringservice.yaml"
     - "monitoringtenant.yaml"
diff --git a/roles/monitoringservice-config/templates/xos-monitoringservice.yaml.j2 b/roles/monitoringservice-config/templates/xos-monitoringservice.yaml.j2
index b97ffc7..f54874b 100644
--- a/roles/monitoringservice-config/templates/xos-monitoringservice.yaml.j2
+++ b/roles/monitoringservice-config/templates/xos-monitoringservice.yaml.j2
@@ -15,7 +15,7 @@
     /opt/xos_services/monitoring:
       type: tosca.nodes.XOSVolume
       properties:
-          host_path: "{{ cord_dir }}/orchestration/xos_services/monitoring"
+          host_path: "{{ head_cord_dir }}/orchestration/xos_services/monitoring"
           read_only: True
       requirements:
           - xos:
diff --git a/roles/monitoringservice-onboard/defaults/main.yml b/roles/monitoringservice-onboard/defaults/main.yml
index ff338b7..42c8d6e 100644
--- a/roles/monitoringservice-onboard/defaults/main.yml
+++ b/roles/monitoringservice-onboard/defaults/main.yml
@@ -1,8 +1,8 @@
 ---
 # monitoringservice-onboard/defaults/main.yml
 
-cord_dir: "{{ ansible_user_dir + '/cord' }}"
-cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+# paths
+head_cord_profile_dir: "/opt/cord_profile"
 
 xos_bootstrap_ui_port: 9001
 
diff --git a/roles/monitoringservice-onboard/tasks/main.yml b/roles/monitoringservice-onboard/tasks/main.yml
index 9538d7f..c1156f8 100644
--- a/roles/monitoringservice-onboard/tasks/main.yml
+++ b/roles/monitoringservice-onboard/tasks/main.yml
@@ -2,22 +2,22 @@
 # monitoringservice-onboard/tasks/main.yml
 
 - name: Disable onboarding
-  command: "python /opt/xos/tosca/run.py {{ xos_admin_user }} /opt/cord_profile/disable-onboarding.yaml"
+  command: "python /opt/xos/tosca/run.py {{ xos_admin_user }} {{ head_cord_profile_dir }}/disable-onboarding.yaml"
   tags:
     - skip_ansible_lint # TOSCA loading should be idempotent
 
 - name: Have XOS container mount monitoringservice volume
-  command: "python /opt/xos/tosca/run.py {{ xos_admin_user }} /opt/cord_profile/xos-monitoringservice.yaml"
+  command: "python /opt/xos/tosca/run.py {{ xos_admin_user }} {{ head_cord_profile_dir }}/xos-monitoringservice.yaml"
   tags:
     - skip_ansible_lint # TOSCA loading should be idempotent
 
 - name: Onboard monitoringservice
-  command: "python /opt/xos/tosca/run.py {{ xos_admin_user }} /opt/cord_profile/monitoring-onboard.yaml"
+  command: "python /opt/xos/tosca/run.py {{ xos_admin_user }} {{ head_cord_profile_dir }}/monitoring-onboard.yaml"
   tags:
     - skip_ansible_lint # TOSCA loading should be idempotent
 
 - name: Enable onboarding
-  command: "python /opt/xos/tosca/run.py {{ xos_admin_user }} /opt/cord_profile/enable-onboarding.yaml"
+  command: "python /opt/xos/tosca/run.py {{ xos_admin_user }} {{ head_cord_profile_dir }}/enable-onboarding.yaml"
   tags:
     - skip_ansible_lint # TOSCA loading should be idempotent
 
diff --git a/roles/onos-cord-install/defaults/main.yml b/roles/onos-cord-install/defaults/main.yml
index 9b43adc..bdf6d18 100644
--- a/roles/onos-cord-install/defaults/main.yml
+++ b/roles/onos-cord-install/defaults/main.yml
@@ -1,15 +1,23 @@
 ---
-# onos-vm-install/defaults/main.yml
+# onos-cord-install/defaults/main.yml
 
+# paths
+pki_dir: "{{ playbook_dir }}/pki"
+ssh_pki_dir: "{{ playbook_dir }}/ssh_pki"
+head_onos_cord_dir: "/opt/onos_cord"
+
+# Should probably be set to a specific version
+onos_docker_image: "opencord/onos:latest"
+
+# log level for ONOS
+onos_log_level: "INFO"
+
+# name of master ssh key for this pod
+pod_sshkey_name: "headnode"
+
+# used for Java KeyStore within ONOS image
 trust_store_pw: 222222
 
-# ONOS 1.7 not tagged yet, but latest is 1.7
-onos_docker_image: "onosproject/onos:latest"
-
-onos_cord_dest: "{{ ansible_user_dir }}/onos-cord/"
-
-node_private_key: "{{ ansible_user_dir }}/node_key"
-
 # logging_host should be set to DNS or IP addr of logstash host
 logging_host: "cordloghost"
 log4j_port: 4560
diff --git a/roles/onos-cord-install/tasks/main.yml b/roles/onos-cord-install/tasks/main.yml
index 441dc07..86ec128 100644
--- a/roles/onos-cord-install/tasks/main.yml
+++ b/roles/onos-cord-install/tasks/main.yml
@@ -1,29 +1,31 @@
 ---
-# Common ONOS setup
+# onos-cord-install/tasks/main.yml
 
-# onos_cord_dest: {{ ansible_user_dir }}/onos-cord/
-
-- name: Pull docker image for ONOS
-  become: yes
-  command: "docker pull {{ onos_docker_image }}"
-  tags:
-    - skip_ansible_lint # Should replace with http://docs.ansible.com/ansible/docker_module.html, when replacements are stable
+- name: Pull base docker image for ONOS
+  docker_image:
+    name: "{{ onos_docker_image }}"
 
 - name: Create dest directory
-  file: path="{{ onos_cord_dest }}" state=directory
+  become: yes
+  file:
+    path: "{{ head_onos_cord_dir }}"
+    state: directory
+    owner: "{{ ansible_user_id }}"
+    group: "{{ ansible_user_gid }}"
+    mode: 0755
 
 - name: Copy over SSH key
   copy:
-    remote_src: True
-    src: "{{ node_private_key }}"
-    dest: "{{ onos_cord_dest }}/node_key"
+    src: "{{ ssh_pki_dir }}/client_certs/{{ pod_sshkey_name }}_sshkey"
+    dest: "{{ head_onos_cord_dir }}/node_key"
     owner: "{{ ansible_user_id }}"
+    group: "{{ ansible_user_gid }}"
     mode: 0600
 
 - name: Create templated ONOS files
   template:
     src: "{{ item }}.j2"
-    dest: "{{ onos_cord_dest }}/{{ item }}"
+    dest: "{{ head_onos_cord_dir }}/{{ item }}"
   with_items:
     - Dockerfile
     - onos-service
@@ -32,27 +34,26 @@
 - name: Copy over ONOS playbook and other files
   copy:
     src: "onos-cord-docker-compose.yml"
-    dest: "{{ onos_cord_dest }}/docker-compose.yml"
+    dest: "{{ head_onos_cord_dir }}/docker-compose.yml"
 
-# TODO: Find the proper place for this on the dev machine rather than
-#       copying it within the head node machine.
-
-- name: Copy SSL Certs to ONOS so docker-compose can find it
+- name: Copy SSL Certs to ONOS so docker-compose can find them
   copy:
-    src: "/usr/local/share/ca-certificates/{{ item }}"
-    dest: "{{ onos_cord_dest }}/{{ item }}"
+    src: "{{ pki_dir }}/{{ item.src }}"
+    dest: "{{ head_onos_cord_dir }}/{{ item.dest }}"
     owner: "{{ ansible_user_id }}"
-    remote_src: True
   with_items:
-    - "cord_root_ca.crt"
-    - "cord_intermediate_ca.crt"
+    - src: "root_ca/certs/ca_cert.pem"
+      dest: "cord_root_ca.crt"
+    - src: "{{ site_name }}_im_ca/certs/im_cert.pem"
+      dest: "cord_{{ site_name }}_im_ca.crt"
 
-- name: Build onos image
-  command: docker-compose build chdir={{ onos_cord_dest }}
-  tags:
-    - skip_ansible_lint
+- name: Build xos/onos docker image
+  docker_image:
+    name: "xos/onos"
+    path: "{{ head_onos_cord_dir }}"
+    dockerfile: "Dockerfile"
 
-- name: Start ONOS
-  command: chdir="{{ onos_cord_dest }}" docker-compose up -d
-  tags:
-    - skip_ansible_lint
+- name: Start ONOS for CORD
+  docker_service:
+    project_src: "{{ head_onos_cord_dir }}"
+
diff --git a/roles/onos-cord-install/templates/Dockerfile.j2 b/roles/onos-cord-install/templates/Dockerfile.j2
index 263767f..3886943 100644
--- a/roles/onos-cord-install/templates/Dockerfile.j2
+++ b/roles/onos-cord-install/templates/Dockerfile.j2
@@ -5,19 +5,19 @@
 
 # Add SSL certs
 COPY cord_root_ca.crt /usr/local/share/ca-certificates/cord_root_ca.crt
-COPY cord_intermediate_ca.crt /usr/local/share/ca-certificates/cord_intermediate_ca.crt
+COPY cord_{{ site_name }}_im_ca.crt /usr/local/share/ca-certificates/cord_{{ site_name }}_im_ca.crt
 RUN update-ca-certificates
 
 # Create Java KeyStore from certs
 RUN openssl x509 -in /usr/local/share/ca-certificates/cord_root_ca.crt \
       -outform der -out /usr/local/share/ca-certificates/cord_root_ca.der && \
-    openssl x509 -in /usr/local/share/ca-certificates/cord_intermediate_ca.crt \
-      -outform der -out /usr/local/share/ca-certificates/cord_intermediate_ca.der && \
+    openssl x509 -in /usr/local/share/ca-certificates/cord_{{ site_name }}_im_ca.crt \
+      -outform der -out /usr/local/share/ca-certificates/cord_{{ site_name }}_im_ca.der && \
     keytool -import -noprompt -storepass {{ trust_store_pw }} -alias cord_root_ca \
       -file /usr/local/share/ca-certificates/cord_root_ca.der \
       -keystore /usr/local/share/ca-certificates/cord_ca_certs.jks && \
-    keytool -import -noprompt -storepass {{ trust_store_pw }} -alias cord_intermediate_ca \
-      -file /usr/local/share/ca-certificates/cord_intermediate_ca.der \
+    keytool -import -noprompt -storepass {{ trust_store_pw }} -alias cord_{{ site_name }}_im_ca \
+      -file /usr/local/share/ca-certificates/cord_{{ site_name }}_im_ca.der \
       -keystore /usr/local/share/ca-certificates/cord_ca_certs.jks
 
 # Updated onos-service to use the jks
diff --git a/roles/onos-cord-install/templates/org.ops4j.pax.logging.cfg.j2 b/roles/onos-cord-install/templates/org.ops4j.pax.logging.cfg.j2
index 2761c7e..d1c712a 100644
--- a/roles/onos-cord-install/templates/org.ops4j.pax.logging.cfg.j2
+++ b/roles/onos-cord-install/templates/org.ops4j.pax.logging.cfg.j2
@@ -18,7 +18,7 @@
 ################################################################################
 
 # Root logger
-log4j.rootLogger=INFO, out, logstash, osgi:*
+log4j.rootLogger={{ onos_log_level }}, out, logstash, osgi:*
 log4j.throwableRenderer=org.apache.log4j.OsgiThrowableRenderer
 
 # CONSOLE appender not used by default
@@ -28,7 +28,6 @@
 
 # logstash log4j appender
 log4j.appender.logstash=org.apache.log4j.net.SocketAppender
-log4j.appender.logstash.threshold=DEBUG
 log4j.appender.logstash.Port={{ log4j_port }}
 log4j.appender.logstash.RemoteHost={{ logging_host }}
 log4j.appender.logstash.ReconnectionDelay=5000
diff --git a/roles/onos-fabric-install/defaults/main.yml b/roles/onos-fabric-install/defaults/main.yml
index 8a1e199..2c70fc8 100644
--- a/roles/onos-fabric-install/defaults/main.yml
+++ b/roles/onos-fabric-install/defaults/main.yml
@@ -1,11 +1,10 @@
 ---
-# onos-vm-install/defaults/main.yml
+# onos-fabric-install/defaults/main.yml
 
-trust_store_pw: 222222
+# paths
+head_onos_fabric_dir: "/opt/onos_fabric"
 
-# ONOS 1.7 not tagged yet, but latest is 1.7
-onos_docker_image: "onosproject/onos:latest"
+# Should probably be set to a specific version
+onos_docker_image: "opencord/onos:latest"
 
-onos_fabric_dest: "{{ ansible_user_dir }}/onos-fabric/"
 
-node_private_key: "{{ ansible_user_dir }}/node_key"
diff --git a/roles/onos-fabric-install/tasks/main.yml b/roles/onos-fabric-install/tasks/main.yml
index 4f16e64..49c2538 100644
--- a/roles/onos-fabric-install/tasks/main.yml
+++ b/roles/onos-fabric-install/tasks/main.yml
@@ -1,28 +1,29 @@
 ---
-# Common ONOS setup
+# onos-fabric-install/tasks/main.yml
 
-- name: Pull docker image for ONOS
+- name: Pull base docker image for ONOS
+  docker_image:
+    name: "{{ onos_docker_image }}"
+
+- name: Create onos-fabric dest directory
   become: yes
-  command: "docker pull {{ onos_docker_image }}"
-  tags:
-    - skip_ansible_lint # Should replace with http://docs.ansible.com/ansible/docker_module.html, when replacements are stable
-
-- name: Create dest directory
   file:
-    path: "{{ onos_fabric_dest }}"
+    path: "{{ head_onos_fabric_dir }}"
     state: directory
+    owner: "{{ ansible_user_id }}"
+    group: "{{ ansible_user_gid }}"
+    mode: 0755
 
 - name: Create templated ONOS files
   template:
     src: "{{ item }}.j2"
-    dest: "{{ onos_fabric_dest }}/{{ item }}"
+    dest: "{{ head_onos_fabric_dir }}/{{ item }}"
   with_items:
     - docker-compose.yml
 
 # Note: we do not rebuild onos container for the fabric
 
-- name: Start ONOS
-  command: chdir="{{ onos_fabric_dest }}" docker-compose up -d
-  tags:
-    - skip_ansible_lint
+- name: Start ONOS for fabric
+  docker_service:
+    project_src: "{{ head_onos_fabric_dir }}"
 
diff --git a/roles/pki-root-ca/tasks/main.yml b/roles/pki-root-ca/tasks/main.yml
index 8c2f34d..fd526e4 100644
--- a/roles/pki-root-ca/tasks/main.yml
+++ b/roles/pki-root-ca/tasks/main.yml
@@ -1,21 +1,16 @@
 ---
 # pki-root-ca/tasks/main.yml
 
-- name: Create credentials directory
+- name: Create PKI and credentials directories
   become: yes
   file:
-    dest: "{{ credentials_dir }}"
+    dest: "{{ item }}"
     state: directory
     owner: "{{ ansible_user_id }}"
     mode: 0700
-
-- name: Create PKI directory
-  become: yes
-  file:
-    dest: "{{ pki_dir }}"
-    state: directory
-    owner: "{{ ansible_user_id }}"
-    mode: 0755
+  with_items:
+    - "{{ credentials_dir }}"
+    - "{{ pki_dir }}"
 
 - name: Create root CA directory
   become: yes
diff --git a/roles/platform-check/defaults/main.yml b/roles/platform-check/defaults/main.yml
index 16a8ef7..4e19128 100644
--- a/roles/platform-check/defaults/main.yml
+++ b/roles/platform-check/defaults/main.yml
@@ -1,8 +1,8 @@
 ---
 # platform-check/defaults/main.yml
 
-onos_cord_dest: "{{ ansible_user_dir }}/onos-cord/"
-cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+config_cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+head_onos_cord_dir: "/opt/onos-cord/"
 
 xos_ui_port: 9000
 
diff --git a/roles/platform-check/tasks/main.yml b/roles/platform-check/tasks/main.yml
index a67e837..bd9347e 100644
--- a/roles/platform-check/tasks/main.yml
+++ b/roles/platform-check/tasks/main.yml
@@ -16,7 +16,7 @@
   when: result | failed
   shell: docker-compose stop; docker-compose rm -f; docker-compose up -d
   args:
-    chdir: "{{ onos_cord_dest }}"
+    chdir: "{{ head_onos_cord_dir }}"
   tags:
     - skip_ansible_lint
 
@@ -26,7 +26,7 @@
     url: "http://xos.{{ site_suffix }}:{{ xos_ui_port }}/api/utility/tosca/run/"
     user: "{{ xos_admin_user }}"
     password:  "{{ xos_admin_pass }}"
-    recipe: "{{ lookup('file', cord_profile_dir + '/' + item ) }}"
+    recipe: "{{ lookup('file', head_cord_profile_dir + '/' + item ) }}"
   with_items:
     - openstack.yaml
     - openstack-compute.yaml
@@ -42,7 +42,7 @@
     url: "http://xos.{{ site_suffix }}:{{ xos_ui_port }}/api/utility/tosca/run/"
     user: "{{ xos_admin_user }}"
     password:  "{{ xos_admin_pass }}"
-    recipe: "{{ lookup('file', cord_profile_dir + '/' + item ) }}"
+    recipe: "{{ lookup('file', head_cord_profile_dir + '/' + item ) }}"
   with_items:
     - openstack-compute-vtn.yaml
 
diff --git a/roles/repo/defaults/main.yml b/roles/repo/defaults/main.yml
index b2af59f..6ef6c5a 100644
--- a/roles/repo/defaults/main.yml
+++ b/roles/repo/defaults/main.yml
@@ -1,7 +1,7 @@
 ---
 # repo/defaults/main.yml
 
-cord_dir: "{{ ansible_user_dir + '/cord' }}"
+config_cord_dir: "{{ ansible_user_dir + '/cord' }}"
 repo_dl_url: "https://storage.googleapis.com/git-repo-downloads/repo"
 
 # This is for repo v1.23, and will change, as repo_dl_url unfortunately lacks a version...
diff --git a/roles/repo/tasks/main.yml b/roles/repo/tasks/main.yml
index 779e406..58cafa3 100644
--- a/roles/repo/tasks/main.yml
+++ b/roles/repo/tasks/main.yml
@@ -13,25 +13,25 @@
 
 - name: Create CORD directory
   file:
-    dest: "{{ cord_dir }}"
+    dest: "{{ config_cord_dir }}"
     state: directory
 
 - name: Init CORD repos (master branch) using repo
   command: "/usr/local/bin/repo init -u {{ repo_manifest_url }} -b master -g build,onos,orchestration"
   args:
-    chdir: "{{ cord_dir }}"
-    creates: "{{ cord_dir }}/.repo"
+    chdir: "{{ config_cord_dir }}"
+    creates: "{{ config_cord_dir }}/.repo"
 
 - name: Synchronize CORD repos using repo
   command: "repo sync"
   args:
-    chdir: "{{ cord_dir }}"
-    creates: "{{ cord_dir }}/build"
+    chdir: "{{ config_cord_dir }}"
+    creates: "{{ config_cord_dir }}/build"
 
 - name: Download specific gerrit changesets using repo
   command: "/usr/local/bin/repo download {{ item.path }} {{ item.revision }}"
   args:
-    chdir: "{{ cord_dir }}"
+    chdir: "{{ config_cord_dir }}"
   with_items: "{{ gerrit_changesets }}"
   tags:
     - skip_ansible_lint # usually won't be run, except during dev
diff --git a/roles/ssh-install-maas/defaults/main.yml b/roles/ssh-install-maas/defaults/main.yml
new file mode 100644
index 0000000..15abf98
--- /dev/null
+++ b/roles/ssh-install-maas/defaults/main.yml
@@ -0,0 +1,11 @@
+---
+# ssh-install-maas/defaults/main.yml
+
+ssh_pki_dir: "{{ playbook_dir }}/ssh_pki"
+
+ssh_keytype: rsa
+
+# name of master ssh key for this pod
+pod_sshkey_name: "headnode"
+
+on_maas: False
diff --git a/roles/ssh-install-maas/tasks/main.yml b/roles/ssh-install-maas/tasks/main.yml
new file mode 100644
index 0000000..96d952d
--- /dev/null
+++ b/roles/ssh-install-maas/tasks/main.yml
@@ -0,0 +1,35 @@
+---
+# ssh-install-maas/tasks/main.yml
+
+# the following replicates the functionality of the maas repo `prep` role
+# users/perms may seem off, but is identical to that role
+
+- name: Create ssh key directory for MaaS
+  when: on_maas
+  file:
+    dest: "/etc/maas/.ssh/"
+    mode: 0755
+    owner: "root"
+    group: "root"
+    state: directory
+
+- name: Copy ssh private key for MaaS
+  when: on_maas
+  copy:
+    src: "{{ ssh_pki_dir }}/client_certs/{{ pod_sshkey_name }}_sshkey"
+    dest: "/etc/maas/.ssh/cord_rsa"
+    mode: 0644
+    owner: "root"
+    group: "root"
+    backup: true
+
+- name: Copy ssh public key for MaaS
+  when: on_maas
+  copy:
+    src: "{{ ssh_pki_dir }}/client_certs/{{ pod_sshkey_name }}_sshkey.pub"
+    dest: "/etc/maas/.ssh/cord_rsa.pub"
+    mode: 0644
+    owner: "root"
+    group: "root"
+    backup: true
+
diff --git a/roles/ssh-install/defaults/main.yml b/roles/ssh-install/defaults/main.yml
new file mode 100644
index 0000000..1fbddfa
--- /dev/null
+++ b/roles/ssh-install/defaults/main.yml
@@ -0,0 +1,11 @@
+---
+# ssh-install/defaults/main.yml
+
+ssh_pki_dir: "{{ playbook_dir }}/ssh_pki"
+
+ssh_keytype: rsa
+
+# name of master ssh key for this pod
+pod_sshkey_name: "headnode"
+
+on_maas: False
diff --git a/roles/ssh-install/tasks/main.yml b/roles/ssh-install/tasks/main.yml
new file mode 100644
index 0000000..d59db84
--- /dev/null
+++ b/roles/ssh-install/tasks/main.yml
@@ -0,0 +1,29 @@
+---
+# ssh-install/tasks/main.yml
+
+- name: Create ~/.ssh directory
+  file:
+    dest: "{{ ansible_user_dir }}/.ssh/"
+    mode: 0700
+    owner: "{{ ansible_user_id }}"
+    group: "{{ ansible_user_gid }}"
+    state: directory
+
+- name: Install ssh private key
+  copy:
+    src: "{{ ssh_pki_dir }}/client_certs/{{ pod_sshkey_name }}_sshkey"
+    dest: "{{ ansible_user_dir }}/.ssh/id_{{ ssh_keytype }}"
+    mode: 0600
+    owner: "{{ ansible_user_id }}"
+    group: "{{ ansible_user_gid }}"
+    backup: true
+
+- name: Install ssh public key
+  copy:
+    src: "{{ ssh_pki_dir }}/client_certs/{{ pod_sshkey_name }}_sshkey.pub"
+    dest: "{{ ansible_user_dir }}/.ssh/id_{{ ssh_keytype }}.pub"
+    mode: 0600
+    owner: "{{ ansible_user_id }}"
+    group: "{{ ansible_user_gid }}"
+    backup: true
+
diff --git a/roles/ssh-pki/defaults/main.yml b/roles/ssh-pki/defaults/main.yml
index c7e6125..0909635 100644
--- a/roles/ssh-pki/defaults/main.yml
+++ b/roles/ssh-pki/defaults/main.yml
@@ -11,9 +11,12 @@
 ssh_keytype: rsa
 ssh_keysize: 4096
 
+# name of master ssh key for this pod
+pod_sshkey_name: "headnode"
+
 # lists of keys to generate
 ssh_client_genkeys:
-  - name: headnode
+  - name: "{{ pod_sshkey_name }}"
 
 ssh_host_genkeys: []
 
diff --git a/roles/ssh-pki/tasks/main.yml b/roles/ssh-pki/tasks/main.yml
index 44dbe64..2cc7c64 100644
--- a/roles/ssh-pki/tasks/main.yml
+++ b/roles/ssh-pki/tasks/main.yml
@@ -2,6 +2,7 @@
 # ssh-pki/tasks/main.yml
 
 - name: Create SSH CA Directory
+  become: yes
   file:
     dest: "{{ item }}"
     state: directory
diff --git a/roles/teardown-profile/defaults/main.yml b/roles/teardown-profile/defaults/main.yml
index b050484..83c07e2 100644
--- a/roles/teardown-profile/defaults/main.yml
+++ b/roles/teardown-profile/defaults/main.yml
@@ -1,11 +1,9 @@
 ---
 # teardown-profile/defaults/main.yml
 
-cord_dir: "{{ ansible_user_dir + '/cord' }}"
-
-cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+head_cord_profile_dir: "/opt/cord_profile"
 
 xos_docker_networks:
   - "xos"
 
-delete_cord_profile_dir: False
\ No newline at end of file
+delete_cord_profile_dir: False
diff --git a/roles/teardown-profile/tasks/main.yml b/roles/teardown-profile/tasks/main.yml
index 4cfd552..365c087 100644
--- a/roles/teardown-profile/tasks/main.yml
+++ b/roles/teardown-profile/tasks/main.yml
@@ -6,7 +6,7 @@
 - name: Stop and remove XOS containers
   docker_service:
     project_name: "{{ cord_profile | regex_replace('\\W','') }}"
-    project_src: "{{ cord_profile_dir }}"
+    project_src: "{{ head_cord_profile_dir }}"
     state: absent
     remove_images: local
   ignore_errors: yes
@@ -22,7 +22,7 @@
 
 - name: Remove the cord_profile directory
   file:
-    path: "{{ cord_profile_dir }}"
+    path: "{{ head_cord_profile_dir }}"
     state: absent
   ignore_errors: yes
   when: delete_cord_profile_dir
diff --git a/roles/test-ecord-subscriber-config/defaults/main.yml b/roles/test-ecord-subscriber-config/defaults/main.yml
index 3fbf456..9d7abab 100644
--- a/roles/test-ecord-subscriber-config/defaults/main.yml
+++ b/roles/test-ecord-subscriber-config/defaults/main.yml
@@ -1,5 +1,4 @@
 ---
-# test-subscriber-config/defaults/main.yml
+# test-ecord-subscriber-config/defaults/main.yml
 
-cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
-
+head_cord_profile_dir: "/opt/cord_profile"
diff --git a/roles/test-ecord-subscriber-config/tasks/main.yml b/roles/test-ecord-subscriber-config/tasks/main.yml
index 16a343d..51fe639 100644
--- a/roles/test-ecord-subscriber-config/tasks/main.yml
+++ b/roles/test-ecord-subscriber-config/tasks/main.yml
@@ -1,10 +1,10 @@
 ---
-# test-subscriber/tasks/main.yml
+# test-ecord-subscriber-config/tasks/main.yml
 
 - name: Create test-ecord-subscriber.yaml TOSCA config
   template:
     src: test-ecord-subscriber.yaml.j2
-    dest: "{{ cord_profile_dir }}/test-ecord-subscriber.yaml"
+    dest: "{{ head_cord_profile_dir }}/test-ecord-subscriber.yaml"
     owner: "{{ ansible_user_id }}"
     mode: 0644
 
diff --git a/roles/test-exampleservice/defaults/main.yml b/roles/test-exampleservice/defaults/main.yml
index 04b252e..82900ab 100644
--- a/roles/test-exampleservice/defaults/main.yml
+++ b/roles/test-exampleservice/defaults/main.yml
@@ -1,6 +1,6 @@
 ---
 # test-exampleservice/defaults/main.yml
 
-cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+head_cord_profile_dir: "/opt/cord_profile"
 xos_ui_port: 9000
 
diff --git a/roles/test-exampleservice/tasks/main.yml b/roles/test-exampleservice/tasks/main.yml
index 08fad6b..fd69893 100644
--- a/roles/test-exampleservice/tasks/main.yml
+++ b/roles/test-exampleservice/tasks/main.yml
@@ -7,10 +7,10 @@
     url: "http://xos.{{ site_suffix }}:{{ xos_ui_port }}/api/utility/tosca/run/"
     user: "{{ xos_admin_user }}"
     password:  "{{ xos_admin_pass }}"
-    recipe: "{{ lookup('file', cord_profile_dir + '/test-exampleservice.yaml' ) }}"
+    recipe: "{{ lookup('file', head_cord_profile_dir + '/test-exampleservice.yaml' ) }}"
 
 - name: Wait for ExampleService VM to come up
-  shell: bash -c "source ~/admin-openrc.sh; nova list --all-tenants|grep 'exampleservice.*ACTIVE' > /dev/null"
+  shell: bash -c "source /opt/cord_profile/admin-openrc.sh; nova list --all-tenants|grep 'exampleservice.*ACTIVE' > /dev/null"
   register: result
   until: result | success
   retries: 10
@@ -19,25 +19,25 @@
     - skip_ansible_lint # running a sub job
 
 - name: Get ID of VM
-  shell: bash -c "source ~/admin-openrc.sh; nova list --all-tenants|grep mysite_exampleservice|cut -d '|' -f 2"
+  shell: bash -c "source /opt/cord_profile/admin-openrc.sh; nova list --all-tenants|grep mysite_exampleservice|cut -d '|' -f 2"
   register: nova_id
   tags:
     - skip_ansible_lint # running a sub job
 
 - name: Get mgmt IP of VM
-  shell: bash -c "source ~/admin-openrc.sh; nova interface-list {{ nova_id.stdout }}|grep -o -m 1 '172\.27\.[[:digit:]]*\.[[:digit:]]*'"
+  shell: bash -c "source /opt/cord_profile/admin-openrc.sh; nova interface-list {{ nova_id.stdout }}|grep -o -m 1 '172\.27\.[[:digit:]]*\.[[:digit:]]*'"
   register: mgmt_ip
   tags:
     - skip_ansible_lint # running a sub job
 
 - name: Get public IP of VM
-  shell: bash -c "source ~/admin-openrc.sh; nova interface-list {{ nova_id.stdout }}|grep -o -m 1 '10\.6\.[[:digit:]]*\.[[:digit:]]*'"
+  shell: bash -c "source /opt/cord_profile/admin-openrc.sh; nova interface-list {{ nova_id.stdout }}|grep -o -m 1 '10\.6\.[[:digit:]]*\.[[:digit:]]*'"
   register: public_ip
   tags:
     - skip_ansible_lint # running a sub job
 
 - name: Get name of compute node
-  shell: bash -c "source ~/admin-openrc.sh; nova service-list|grep nova-compute|cut -d '|' -f 3"
+  shell: bash -c "source /opt/cord_profile/admin-openrc.sh; nova service-list|grep nova-compute|cut -d '|' -f 3"
   register: node_name
   tags:
     - skip_ansible_lint # running a sub job
diff --git a/roles/test-subscriber-config/defaults/main.yml b/roles/test-subscriber-config/defaults/main.yml
index 3fbf456..9da9f64 100644
--- a/roles/test-subscriber-config/defaults/main.yml
+++ b/roles/test-subscriber-config/defaults/main.yml
@@ -1,5 +1,4 @@
 ---
 # test-subscriber-config/defaults/main.yml
 
-cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
-
+head_cord_profile_dir: "/opt/cord_profile"
diff --git a/roles/test-subscriber-config/tasks/main.yml b/roles/test-subscriber-config/tasks/main.yml
index 46c1642..fd8a7a1 100644
--- a/roles/test-subscriber-config/tasks/main.yml
+++ b/roles/test-subscriber-config/tasks/main.yml
@@ -1,10 +1,10 @@
 ---
-# test-subscriber/tasks/main.yml
+# test-subscriber-config/tasks/main.yml
 
 - name: Create test-subscriber.yaml TOSCA config
   template:
     src: test-subscriber.yaml.j2
-    dest: "{{ cord_profile_dir }}/test-subscriber.yaml"
+    dest: "{{ head_cord_profile_dir }}/test-subscriber.yaml"
     owner: "{{ ansible_user_id }}"
     mode: 0644
 
diff --git a/roles/test-vsg/tasks/main.yml b/roles/test-vsg/tasks/main.yml
index 6beb908..972dc7b 100644
--- a/roles/test-vsg/tasks/main.yml
+++ b/roles/test-vsg/tasks/main.yml
@@ -4,13 +4,13 @@
 # Run tests to check that the CORD-in-a-Box deployment has worked.
 
 - name: Get name of compute node
-  shell: bash -c "source ~/admin-openrc.sh; nova service-list|grep nova-compute|cut -d '|' -f 3"
+  shell: bash -c "source /opt/cord_profile/admin-openrc.sh; nova service-list|grep nova-compute|cut -d '|' -f 3"
   register: node_name
   tags:
     - skip_ansible_lint # running a sub job
 
 - name: Wait for vSG VM to come up
-  shell: bash -c "source ~/admin-openrc.sh; nova list --all-tenants|grep 'vsg.*ACTIVE' > /dev/null"
+  shell: bash -c "source /opt/cord_profile/admin-openrc.sh; nova list --all-tenants|grep 'vsg.*ACTIVE' > /dev/null"
   register: result
   until: result | success
   retries: 10
@@ -19,13 +19,13 @@
     - skip_ansible_lint # running a sub job
 
 - name: Get ID of VM
-  shell: bash -c "source ~/admin-openrc.sh; nova list --all-tenants|grep mysite_vsg|cut -d '|' -f 2"
+  shell: bash -c "source /opt/cord_profile/admin-openrc.sh; nova list --all-tenants|grep mysite_vsg|cut -d '|' -f 2"
   register: nova_id
   tags:
     - skip_ansible_lint # running a sub job
 
 - name: Get mgmt IP of VM
-  shell: bash -c "source ~/admin-openrc.sh; nova interface-list {{ nova_id.stdout }}|grep -o -m 1 '172\.27\.[[:digit:]]*\.[[:digit:]]*'"
+  shell: bash -c "source /opt/cord_profile/admin-openrc.sh; nova interface-list {{ nova_id.stdout }}|grep -o -m 1 '172\.27\.[[:digit:]]*\.[[:digit:]]*'"
   register: mgmt_ip
   tags:
     - skip_ansible_lint # running a sub job
diff --git a/roles/xos-bootstrap-hosts/defaults/main.yml b/roles/xos-bootstrap-hosts/defaults/main.yml
index 6b20046..86f373f 100644
--- a/roles/xos-bootstrap-hosts/defaults/main.yml
+++ b/roles/xos-bootstrap-hosts/defaults/main.yml
@@ -1,7 +1,5 @@
 ---
 # xos-bootstrap-hosts/defaults/main.yml
 
-cord_dir: "{{ ansible_user_dir + '/cord' }}"
-
-cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+head_cord_profile_dir: "/opt/cord_profile"
 
diff --git a/roles/xos-bootstrap-hosts/tasks/main.yml b/roles/xos-bootstrap-hosts/tasks/main.yml
index 32a589c..9437dde 100644
--- a/roles/xos-bootstrap-hosts/tasks/main.yml
+++ b/roles/xos-bootstrap-hosts/tasks/main.yml
@@ -4,7 +4,7 @@
 - name: Get the Docker container names for bootstrap containers
   docker_service:
     project_name: "{{ cord_profile | regex_replace('\\W','') }}bs"
-    project_src: "{{ cord_profile_dir }}"
+    project_src: "{{ head_cord_profile_dir }}"
     files: "xos-bootstrap-docker-compose.yaml"
     recreate: never
   register: xos_bootstrap_out
diff --git a/roles/xos-core-build/defaults/main.yml b/roles/xos-core-build/defaults/main.yml
index e6d6c26..2772a14 100644
--- a/roles/xos-core-build/defaults/main.yml
+++ b/roles/xos-core-build/defaults/main.yml
@@ -1,7 +1,8 @@
 ---
+# xos-core-build/defaults/main.yml
 
-cord_dir: "{{ playbook_dir + '/../..' }}"
-build_docker_tag: candidate
+build_cord_dir: "{{ playbook_dir ~ '/../..' }}"
+build_docker_tag: "candidate"
 
 deploy_docker_registry: "localhost:5000"
 deploy_docker_tag: "candidate"
@@ -12,3 +13,4 @@
 profile_library: ""
 
 xos_services: []
+
diff --git a/roles/xos-core-build/tasks/main.yml b/roles/xos-core-build/tasks/main.yml
index 88e6af6..06faff1 100644
--- a/roles/xos-core-build/tasks/main.yml
+++ b/roles/xos-core-build/tasks/main.yml
@@ -30,7 +30,7 @@
 
 - name: Create the BUILD directory
   file:
-    path: "{{ cord_dir | realpath }}/orchestration/xos/containers/xos/BUILD"
+    path: "{{ build_cord_dir | realpath }}/orchestration/xos/containers/xos/BUILD"
     state: directory
     mode: 0755
 
@@ -41,9 +41,9 @@
   shell: >
     docker run
     --rm
-    -v {{ cord_dir | realpath }}:/opt/cord
-    -v {{ cord_dir | realpath }}/orchestration/xos/containers/xos/BUILD:/opt/xos_corebuilder/BUILD
-    xosproject/xos-corebuilder:candidate
+    -v {{ build_cord_dir | realpath }}:/opt/cord
+    -v {{ build_cord_dir ~ "/orchestration/xos/containers/xos/BUILD" | realpath }}:/opt/xos_corebuilder/BUILD
+    xosproject/xos-corebuilder:{{ build_docker_tag }}
     {{ recipes_string }}
   tags:
    - skip_ansible_lint # running a build task
@@ -51,7 +51,7 @@
 - name: Build UI image
   docker_image:
     name: "xosproject/xos-ui"
-    path: "{{ cord_dir | realpath }}/orchestration/xos"
+    path: "{{ build_cord_dir | realpath }}/orchestration/xos"
     tag: "{{ build_docker_tag }}"
     dockerfile: "containers/xos/Dockerfile.UI"
     pull: False
diff --git a/roles/xos-gui-extension-build/defaults/main.yml b/roles/xos-gui-extension-build/defaults/main.yml
index 260f667..0dd8561 100644
--- a/roles/xos-gui-extension-build/defaults/main.yml
+++ b/roles/xos-gui-extension-build/defaults/main.yml
@@ -1,7 +1,7 @@
 ---
 # xos-gui-extension-build/defaults/main.yml
 
-cord_dir: "{{ ansible_user_dir + '/cord' }}"
-cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+head_cord_dir: "/opt/cord"
+head_cord_profile_dir: "/opt/cord_profile"
 
-enabled_gui_extensions: []
\ No newline at end of file
+enabled_gui_extensions: []
diff --git a/roles/xos-gui-extension-build/tasks/main.yml b/roles/xos-gui-extension-build/tasks/main.yml
index 51be926..b337d4f 100644
--- a/roles/xos-gui-extension-build/tasks/main.yml
+++ b/roles/xos-gui-extension-build/tasks/main.yml
@@ -5,7 +5,7 @@
 - name: Build xos-gui-extension-builder image
   docker_image:
     name: "xosproject/xos-gui-extension-builder"
-    path: "{{ cord_dir }}/orchestration/xos-gui/"
+    path: "{{ head_cord_dir }}/orchestration/xos-gui/"
     dockerfile: "Dockerfile.xos-gui-extension-builder"
   register: "xos-gui-extension-builder"
 
@@ -13,24 +13,17 @@
 - name: Build xos-gui-extensions docker images
   docker_image:
     name: "xosproject/gui-extension-{{ item.name }}"
-    path: "{{ cord_dir }}/{{ item.path }}"
+    path: "{{ head_cord_dir }}/{{ item.path }}"
     pull: false
   with_items: "{{ enabled_gui_extensions }}"
 
-# Compile the TOSCA to onboard extensions
+# Compile the TOSCA to onboard and persist extensions
 - name: Create templated TOSCA to onboard extensions
   template:
     src: "{{ item }}.j2"
-    dest: "{{ cord_profile_dir }}/{{ item }}"
+    dest: "{{ head_cord_profile_dir }}/{{ item }}"
     mode: 0644
   with_items:
     - xos-gui-extensions-onboard.yml
-
-# Compile the TOSCA to persist extensions
-- name: Create templated TOSCA to persist extensions
-  template:
-    src: "{{ item }}.j2"
-    dest: "{{ cord_profile_dir }}/{{ item }}"
-    mode: 0644
-  with_items:
     - xos-gui-extensions.yml
+
diff --git a/roles/xos-onboard-hosts/defaults/main.yml b/roles/xos-onboard-hosts/defaults/main.yml
index ee4dbb9..089a98a 100644
--- a/roles/xos-onboard-hosts/defaults/main.yml
+++ b/roles/xos-onboard-hosts/defaults/main.yml
@@ -1,7 +1,5 @@
 ---
 # xos-onboard-hosts/defaults/main.yml
 
-cord_dir: "{{ ansible_user_dir + '/cord' }}"
-
-cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+head_cord_profile_dir: /opt/cord_profile
 
diff --git a/roles/xos-onboard-hosts/tasks/main.yml b/roles/xos-onboard-hosts/tasks/main.yml
index 2b60f5a..bc378cd 100644
--- a/roles/xos-onboard-hosts/tasks/main.yml
+++ b/roles/xos-onboard-hosts/tasks/main.yml
@@ -4,7 +4,7 @@
 - name: Get the Docker container names for onboarded containers
   docker_service:
     project_name: "{{ cord_profile | regex_replace('\\W','') }}"
-    project_src: "{{ cord_profile_dir }}"
+    project_src: "{{ head_cord_profile_dir }}"
     recreate: never
   register: xos_onboard_out
 
diff --git a/roles/xos-onboard-test-hosts/defaults/main.yml b/roles/xos-onboard-test-hosts/defaults/main.yml
index 659d9c4..8fe0c9d 100644
--- a/roles/xos-onboard-test-hosts/defaults/main.yml
+++ b/roles/xos-onboard-test-hosts/defaults/main.yml
@@ -1,5 +1,4 @@
 ---
+# xos-onboard-hosts/defaults/main.yml
 
-cord_dir: "{{ ansible_user_dir + '/cord' }}"
-
-cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+head_cord_profile_dir: "/opt/cord_profile"
diff --git a/roles/xos-onboard-test-hosts/tasks/main.yml b/roles/xos-onboard-test-hosts/tasks/main.yml
index 0496464..53c5b42 100644
--- a/roles/xos-onboard-test-hosts/tasks/main.yml
+++ b/roles/xos-onboard-test-hosts/tasks/main.yml
@@ -4,7 +4,7 @@
 - name: Get the Docker container names for onboarded containers
   docker_service:
     project_name: "{{ cord_profile | regex_replace('\\W','') }}"
-    project_src: "{{ cord_profile_dir }}/onboarding-docker-compose/"
+    project_src: "{{ head_cord_profile_dir }}/onboarding-docker-compose/"
     recreate: never
   register: xos_onboard_out
 
diff --git a/roles/xos-up/defaults/main.yml b/roles/xos-up/defaults/main.yml
index 7804fa3..189b3a8 100644
--- a/roles/xos-up/defaults/main.yml
+++ b/roles/xos-up/defaults/main.yml
@@ -1,8 +1,7 @@
 ---
-# xos-service-onboard/defaults/main.yml
+# xos-up/defaults/main.yml
 
-cord_dir: "{{ ansible_user_dir + '/cord' }}"
-cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+head_cord_profile_dir: "/opt/cord_profile"
 
 xos_docker_networks:
   - "xos"
diff --git a/roles/xos-up/tasks/main.yml b/roles/xos-up/tasks/main.yml
index 8e442b9..e97c9e5 100644
--- a/roles/xos-up/tasks/main.yml
+++ b/roles/xos-up/tasks/main.yml
@@ -9,4 +9,4 @@
 - name: Bring up XOS services
   docker_service:
     project_name: "{{ cord_profile | regex_replace('\\W','') }}"
-    project_src: "{{ cord_profile_dir }}"
+    project_src: "{{ head_cord_profile_dir }}"