def merge_files(service, skip_on_user_group_error=False):
  """
  Given a prefix, find all templates below; merge with parameters; write to "dest"
  Args:
    service: "<service>", "all", or "ssh"
    skip_on_user_group_error: True or False

  For S3, full path becomes:
    s3://ellation-cx-global-configs/<service>/templates/<filename>
    s3://ellation-cx-global-configs/<service>/parameters/<filename>.parameters.<yaml|yml|json>
  For filesystem, full path becomes:
    /vagrant/configs/<service>/templates/<filename>
    /vagrant/configs/<service>/parameters/<filename>.parameters.<yaml|yml|json>
  """
  if WHERE == "ec2":
    config_reader = EFInstanceinitConfigReader("s3", service, log_info, RESOURCES["s3"])
    resolver = EFTemplateResolver()
  elif WHERE == "virtualbox-kvm":
    config_path = "{}/{}".format(VIRTUALBOX_CONFIG_ROOT, service)
    config_reader = EFInstanceinitConfigReader("file", config_path, log_info)
    environment = EFConfig.VAGRANT_ENV
    resolver = EFTemplateResolver(env=environment, profile=get_account_alias(environment),
                                  region=EFConfig.DEFAULT_REGION, service=service)

  while config_reader.next():
    log_info("checking: {}".format(config_reader.current_key))

    # if 'dest' for the current object contains an 'environments' list, check it
    dest = config_reader.dest
    if "environments" in dest:
      if not resolver.resolved["ENV_SHORT"] in dest["environments"]:
        log_info("Environment: {} not enabled for {}".format(
          resolver.resolved["ENV_SHORT"], config_reader.current_key)
        )
        continue

    # If 'dest' for the current object contains a user_group that hasn't been created in the environment yet and the
    # flag is set to True to skip, log the error and move onto the next config file without blowing up.
    if skip_on_user_group_error:
      user, group = get_user_group(dest)
      try:
        getpwnam(user).pw_uid
      except KeyError:
        log_info("File specifies user {} that doesn't exist in environment. Skipping config file.".format(user))
        continue
      try:
        getgrnam(group).gr_gid
      except KeyError:
        log_info("File specifies group {} that doesn't exist in environment. Skipping config file.".format(group))
        continue

    # Process the template_body - apply context + parameters
    log_info("Resolving template")
    resolver.load(config_reader.template, config_reader.parameters)
    rendered_body = resolver.render()
    if not resolver.resolved_ok():
      critical("Couldn't resolve all symbols; template has leftover {{ or }}: {}".format(resolver.unresolved_symbols()))

    # Write the rendered file
    dir_path = normpath(dirname(dest["path"]))
    # Resolved OK. try to write the template
    log_info("make directories: {} {}".format(dir_path, dest["dir_perm"]))
    try:
      makedirs(dir_path, int(dest["dir_perm"], 8))
    except OSError as error:
      if error.errno != 17:
        critical("Error making directories {}".format(repr(error)))
    log_info("open: " + dest["path"] + ",w+")
    try:
      outfile = open(dest["path"], 'w+')
      log_info("write")
      outfile.write(rendered_body)
      log_info("close")
      outfile.close()
      log_info("chmod file to: " + dest["file_perm"])
      chmod(dest["path"], int(dest["file_perm"], 8))
      user, group = get_user_group(dest)
      uid = getpwnam(user).pw_uid
      gid = getgrnam(group).gr_gid
      log_info("chown last directory in path to: " + dest["user_group"])
      chown(dir_path, uid, gid)
      log_info("chown file to: " + dest["user_group"])
      chown(dest["path"], uid, gid)
    except Exception as error:
      critical("Error writing file: " + dest["path"] + ": " + repr(error))