Let's take an the cf-secrets.yml file from cf-boshworkspace, and see if we can convert it from a spiff based template to a spruce based template.

The original file is pretty big, so for simpliticy's sake, lets strip it down to just this:

meta:  
  admin_secret: (( merge || c1oudc0wc1oudc0w ))
  secret: (( merge || defaults.secret ))
  secrets:
    cc_bulk_api_password: (( merge || meta.secret ))
    cc_staging_upload_password: (( merge || meta.secret ))
    cc_db_encryption_key: (( merge || meta.secret ))

properties:  
  cc:
    staging_upload_user: staging
    bulk_api_password: (( meta.secrets.cc_bulk_api_password ))
    staging_upload_password: (( meta.secrets.cc_staging_upload_password ))
    db_encryption_key: (( meta.secrets.cc_db_encryption_key ))
  uaa:
    scim:
      users:
      - (( concat "admin|" meta.admin_secret "|scim.write,scim.read,openid,cloud_controller.admin,clients.read,clients.write,doppler.firehose" )) 
defaults:  
  secret: c1oudc0w

The gist of this template is defining a default shared secret and admin password for CloudFoundry, allowing you to override it in later templates.

Since spruce doesn't need any special syntax to allow for overrides, we can probably get away with the defaults and meta sections. However, it would be useful to have a single place to store the shared secret, for easier referencing. Let's have a start with this:

params:  
  admin_secret: c1oudc0wc1oudc0w
  default_secret: c1oudc0w

properties:  
  cc:
    staging_upload_user: staging
    bulk_api_password: (( grab params.default_secret ))
    staging_upload_password: (( grab params.default_secret ))
    db_encryption_key: (( grab params.default_secret ))
  uaa:
    scim:
      users:
      - (( concat "admin|" params.admin_secret "|scim.write,scim.read,openid,cloud_controller.admin,clients.read,clients.write,doppler.firehose" ))

This is functionally equivalent to what we started with, but what if we wanted to force users to provide secrets, so they don't go accidentally using a default password in an important deployment? Let's look at the param capability in spruce:

params:  
  admin_secret: (( param "You need to specify an admin password for CF" ))
  default_secret: (( param "You need to specify a shared-secret for this service, or provide params.default_secret" ))

properties:  
  cc:
    staging_upload_user: staging
    bulk_api_password: (( grab params.default_secret ))
    staging_upload_password: (( grab params.default_secret ))
    db_encryption_key: (( grab params.default_secret ))
  uaa:
    scim:
      users:
      - (( concat "admin|" params.admin_secret "|scim.write,scim.read,openid,cloud_controller.admin,clients.read,clients.write,doppler.firehose" ))

Now, users have a few options:

  • Override params.default_secret directly, and all the keys referencing it will have the same secret:
params:  
  admin_secret: mySecretPassword
  default_secret: superSecretSharedSecret
  • Override each key that would reference params.default_secret to get unique secrets for each service:
params:  
  admin_secret: mySecretPassword
properties:  
  cc:
    bulk_api_password: bulk_api_super_secret
    staging_upload_password: staging_upload_password_OooOOoo
    db_encryption_key: evenMoreSecretEncryptionKey
  • Give a default secret to most services, while providing a unique secret to a handful:
params:  
  admin_secret: mySecretPassword
  default_secret: supersecretsharedsecret
properties:  
  cc:
    db_encryption_key: evenMoreSecretEncryptionKey

Lets choose the last option, and merge them together:

spruce merge condensed-cf-secrets.yml overrides.yml  
params:  
  admin_secret: mySecretPassword
  default_secret: supersecretsharedsecret
properties:  
  cc:
    bulk_api_password: supersecretsharedsecret
    db_encryption_key: evenMoreSecretEncryptionKey
    staging_upload_password: supersecretsharedsecret
    staging_upload_user: staging
  uaa:
    scim:
      users:
      - admin|mySecretPassword|scim.write,scim.read,openid,cloud_controller.admin,clients.read,clients.write,doppler.firehose

This output still has the params key, which isn't used by anything other than the templates. It would be nice if we could get rid of that to clean things up.

spruce merge --prune params condensed-cf-secrets.yml overrides.yml  
properties:  
  cc:
    bulk_api_password: supersecretsharedsecret
    db_encryption_key: evenMoreSecretEncryptionKey
    staging_upload_password: supersecretsharedsecret
    staging_upload_user: staging
  uaa:
    scim:
      users:
      - admin|mySecretPassword|scim.write,scim.read,openid,cloud_controller.admin,clients.read,clients.write,doppler.firehose

Hmm. That did the trick, but can we be sure that the pruning didn't remove our param safeguards? Let's try again, but leave out the values of the params keys

spruce merge --prune params condensed-cf-secrets.yml overrides.yml  
2 error(s) detected:  
 - $.properties.cc.bulk_api_password: You need to specify a shared-secret for this service, or provide params.default_secret
 - $.properties.cc.staging_upload_password: You need to specify a shared-secret for this service, or provide params.default_secret

As hoped, the errors showed up. We now have a working spruce template!