How to Ensure Directory Existence Before Creating Template Files in Chef


2 views

When working with Chef templates, a common pitfall occurs when the target directory structure doesn't exist. The template resource will fail if parent directories aren't present, unlike some other configuration management tools that automatically create required directories.

template "/var/www/myapp/shared/config/database.yml" do
  source 'database.yml.erb'
  # ... other attributes ...
end

The proper Chef way to handle this is by explicitly declaring directory resources for each required parent directory:

directory "#{node[:app][:deploy_to]}/#{node[:app][:name]}/shared/config" do
  owner node[:user][:username]
  group node[:user][:username]
  mode '0755'
  recursive true
  action :create
end

When implementing directory creation in your recipes:

  • Set recursive true to create all parent directories
  • Ensure proper permissions (owner/group/mode) match your security requirements
  • Consider using not_if or only_if guards for conditional creation

Here's a full implementation pattern:

# Create parent directories first
directory "#{node[:app][:deploy_to]}" do
  owner node[:user][:username]
  group node[:user][:username]
  mode '0755'
  recursive true
end

directory "#{node[:app][:deploy_to]}/#{node[:app][:name]}" do
  owner node[:user][:username]
  group node[:user][:username]
  mode '0755'
end

directory "#{node[:app][:deploy_to]}/#{node[:app][:name]}/shared" do
  owner node[:user][:username]
  group node[:user][:username]
  mode '0755'
end

directory "#{node[:app][:deploy_to]}/#{node[:app][:name]}/shared/config" do
  owner node[:user][:username]
  group node[:user][:username]
  mode '0755'
end

# Then create the template
template "#{node[:app][:deploy_to]}/#{node[:app][:name]}/shared/config/database.yml" do
  source 'database.yml.erb'
  owner node[:user][:username]
  group node[:user][:username]
  mode '0644'
  variables({
    # ... template variables ...
  })
end

For frequent use cases, consider creating a custom resource:

# libraries/ensure_directory.rb
module MyCookbook
  module Helpers
    def ensure_directory(path, owner, group, mode)
      directory path do
        owner owner
        group group
        mode mode
        recursive true
        action :create
      end
    end
  end
end

When working with Chef templates, a common issue arises when the target directory structure doesn't exist yet. The template resource will fail if parent directories are missing, which is exactly what happens in this database.yml example:

template "#{node[:app][:deploy_to]}/#{node[:app][:name]}/shared/config/database.yml" do
  source 'database.yml.erb'
  owner node[:user][:username]
  group node[:user][:username]
  mode 0644
  variables({
    :environment => node[:app][:environment],
    :adapter => node[:database][:adapter],
    :database => node[:database][:name],
    :username => node[:database][:username],
    :password => node[:database][:password],
    :host => node[:database][:host]
  })
end

Chef provides a simple way to handle this using the directory resource. You should create the directory structure before attempting to place files in it:

directory "#{node[:app][:deploy_to]}/#{node[:app][:name]}/shared/config" do
  owner node[:user][:username]
  group node[:user][:username]
  mode 0755
  recursive true
  action :create
end

Here's how to properly combine both resources in your recipe:

# First ensure directory exists
config_dir = "#{node[:app][:deploy_to]}/#{node[:app][:name]}/shared/config"

directory config_dir do
  owner node[:user][:username]
  group node[:user][:username]
  mode 0755
  recursive true
end

# Then create the template file
template "#{config_dir}/database.yml" do
  source 'database.yml.erb'
  owner node[:user][:username]
  group node[:user][:username]
  mode 0644
  variables({
    :environment => node[:app][:environment],
    :adapter => node[:database][:adapter],
    :database => node[:database][:name],
    :username => node[:database][:username],
    :password => node[:database][:password],
    :host => node[:database][:host]
  })
end
  • Always use recursive true when creating nested directory structures
  • Set appropriate permissions (mode) for directories (usually 755) vs files (644)
  • Consider extracting directory paths into variables for maintainability
  • The directory resource is idempotent - it won't fail if the directory exists

For more complex scenarios, you can set up notifications between resources:

directory config_dir do
  # ... same attributes as before ...
  notifies :create, "template[#{config_dir}/database.yml]", :immediately
end

template "#{config_dir}/database.yml" do
  # ... same attributes as before ...
  action :nothing
end