An easy solution for system/dotfile configuration
Loosely based off of Ansible for the task and file organization
pip3 install instater
Using yay
:
yay -Sy instater
Using makepkg:
git clone https://aur.archlinux.org/instater.git
cd instater
makepkg -sirc
See the File Structure Example below to set up variables, files, and tasks.
Once a setup.yml
file is created, it can be run using
instater
# or:
instater --setup-file setup.yml
To see what changed will be made, but not actually make then, use --dry-run
:
instater --dry-run
For a complete example, see dotfiles
First, create a setup.yml
file:
# Lots of ways to prompt for data at the beginning of execution
vars_prompt:
- name: my_var
- name: custom_prompt
prompt: Enter something here
- name: private_var
private: true
- name: private_confirm_var
private: true
confirm: true
- name: allow_empty_var
allow_empty: true
# variables that can be used within tasks/files can be populated
# from a static file, in this case vars/common.yml
vars_files:
- vars/common.yml
# variables can be used within the file names
- "vars/{{ vars_file }}.yml"
# All of the tasks to perform are enumerated
tasks:
- name: Copy file
# {{ username }} is replaced with the variable `username`
copy:
content: "The contents of a new file in here"
dest: "/home/{{ username }}/Downloads/file1"
mode: "600"
# if desired, the output of this task can be registered to use as
# a condition for subsequent tasks
register: file1_copy
- name: Run a command if file1 written
command: "touch /home/{{ username }}/testfile"
when: file1_copy.changed
Then, create a vars/
directory and common.yml
within:
my_test: 1
some_var: "{{ my_test + 2 }}"
vars_file: "second"
username: something
And vars/second.yml
(since common.yml
set vars_file
to second
):
from_second_yml: data in here
Now in all of the tasks, my_test
, username
, from_second_yml
, etc will be
present and accessible.
All tasks support the following arguments:
name
(string, optional): The name of the task, included in logswhen
(string, optional): A Jinja2 statement to determine whether the task should be skipped. If the statement evaluates toTrue
, the task will be executed. Example:my_variable == 'foo'
register
(string, optional): A variable to store task results under. Can be used in conjunction with a subsequentwhen
clause, for exampleregister: my_task
can be used in another task aswhen: my_task.changed
with_fileglob
(string, optional): If provided, find all files in the instater root that match the glob, and create a task with all other
Example of register
and when
:
- name: Set locale to en_US
copy:
content: "en_US.UTF-8 UTF-8\n"
dest: /etc/locale.gen
register: locale_gen
- name: Generate locale files
command: locale-gen
when: locale_gen.changed
Example of with_fileglob
:
- include: "{{ task }}"
with_fileglob: "applications/*"
Install packages from a Arch User Repository
packages
(string, [string]): The packages to install, can be a single package or a list of packagesbecome
(string, optional): A user to become while installing packages
Examples:
- name: Install python package
aur:
packages: python
become: makepkg
- name: Install python libraries
aur:
packages:
- python-setuptools
- python-wheel
Run arbitrary shell commands (can be risky)
command
(string, [string]): The command or commands to executecondition
(string, optional): A command to run prior to thecommand
, as a condition for whether or not it should actually be executedcondition_code
(int, optional): The return code from thecondition
command to match against. If thecondition
returns this code, thecommand
will be executed. Defaults to 0become
(string, optional): A user to become while running the commands (including the condition command)directory
(string, optional): The working directory to use while running the commands
Note that the command and conditions may make use of pipes, for example
curl -s https://get.sdkman.io | bash
Examples:
- name: Make a curl
command:
command: curl https://google.com
- name: Create a file if it doesn't exist
command:
command: touch to/file
condition: ls to/file
condition_code: 2
directory: path
- name: Run several commands
command:
command:
- echo "This does nothing"
- echo "More commands"
Copy a file, directory, url, or direct string content to a destination file or directory
dest
(string): The destination file or directory to write tosrc
(string, optional): The source file or directory to copycontent
(string, optional): The exact content that should be copied to the desturl
(string, optional): A url to GET and use as the contentowner
(string, optional): The owner to set on the file. Note that if a parent directory must be created, it may not be given this owner and should be created separatelygroup
(string, optional): The group to set on the file. Note that if a parent directory must be created, it may not be given this group and should be created separatelymode
(string, integer, optional): The file permissions to set on the destination. Note that if a YAML integer is provided, it must start with a0
to be parsed as octalis_template
(bool, optional): If set to true, the content will be rendered using Jinja2 and all available variables before comparing or writing todest
validate
(string, optional): A command to run to validate the source file or content prior to writing it to the destination. Should contain a%s
which will be replaced by a filename to validate. When applied to a directory, each file is separately validated
Exactly one of src
, content
, or url
must be provided
Examples:
- name: Copy a file
copy:
src: files/my_file.txt
dest: /path/to/destination
- name: Copy a directory
copy:
src: files/my_directory
dest: /path/to/dest
- name: Copy and set owner/group/mode
copy:
src: files/executable.sh
dest: /usr/local/bin/executable.sh
owner: some_user
group: some_group
mode: 0755
- name: Download a url
copy:
url: https://raw.githubusercontent.com/nayaverdier/instater/main/README.md
dest: /path/to/instater/README.md
owner: my_user
group: my_user
- name: Copy content directly
copy:
content: "{{ hostname }}"
dest: /etc/hostname
- name: Copy and validate sudoers file
copy:
src: files/sudoers
dest: /etc/sudoers
mode: 0440
validate: /usr/sbin/visudo -csf %s
- name: Render jinja template and copy
copy:
src: files/my_template
dest: /path/to/file
is_template: true
Log a debug message
debug
(str): The message to log
- name: Log execution information
debug: "Instater root directory: {{ instater_dir }}"
Create a directory. Same as passing directory: true
to the file
task.
(see file
)
- name: Create example directory
directory:
path: "/path/to/directory"
- name: Create a user directory
directory:
path: "/home/exampleuser/private_directory"
owner: exampleuser
group: exampleuser
mode: 0700
Create an empty file, directory, symlink, or hard link on the file system.
path
(string): The path of the file or directory to managetarget
(string, optional): When managing asymlink
orhard_link
, the target file or directory to point toowner
(string, optional): The owner to set on the file. Note that if a parent directory must be created, it may not be given this owner and should be created separatelygroup
(string, optional): The group to set on the file. Note that if a parent directory must be created, it may not be given this group and should be created separatelymode
(string, integer, optional): The file permissions to set on the destination. Note that if a YAML integer is provided, it must start with a0
to be parsed as octaldirectory
(boolean, optional): If set totrue
, create a directorysymlink
(boolean, optional): If set totrue
, create a symlinkhard_link
(boolean, optional): If set totrue
, create a hard link
At most one of directory
, symlink
, and hard_link
may be provided
- name: Create an empty file
file:
path: /path/to/file
- name: Create an empty file with owner/group/mode
file:
path: /home/myuser/myfile
owner: myuser
group: myuser
mode: 0600
- name: Create a symlink
file:
path: /etc/localtime
target: /usr/share/zoneinfo/America/New_York
symlink: true
- name: Create a hard link
file:
path: /path/to/new/file
target: /path/to/existing/file
hard_link: true
- name: Create a directory
file:
path: /path/to/my/dir/
directory: true
- name: Create a directory with owner/group/mode
file:
path: /home/myuser/dir/
owner: myuser
group: myuser
mode: 0700
directory: true
Clone or update a git repository
repo
(string): The git repo uri to clonedest
(string): The destination path to clone intodepth
(integer, optional): Creates a shallow clone with truncated historyfetch_tags
(boolean, optional): Whether or not to fetch git tags (defaults to true)become
(string, optional): The UNIX user that should be used to run git commands
- name: Clone instater
git:
repo: https://github.com/nayaverdier/instater
dest: /home/myuser/Documents/instater
become: myuser
- name: Clone with truncated history
git:
repo: https://github.com/nayaverdier/instater
dest: /home/myuser/Documents/instater
depth: 1
become: myuser
Create a UNIX group
group
(string): The name of the UNIX group to create
- name: Create a group
group: mygroup
Create a hard link to a file
(see file
)
- name: Create a hard link
hard_link:
path: /path/to/new/linked/file
target: /path/to/existing/file
Include another YAML file containing tasks, to allow for better organization of tasks
include
(string): The path of the YAML file to include (relative to the setup.yml)
- include: tasks/something.yml
- include: "{{ item }}"
with_fileglob: "tasks/applications/*"
Install Arch Linux packages using the pacman
, yay
, or makepkg
commands
packages
(string, [string]): The packages to install, can be a single package or a list of packagesaur
(boolean, optional): If set to true, the packages will be installed from the Arch User Repository usingyay
(ormakepkg
as a fallback)become
(string, optional): Whenaur
is true, install using a specific user
Examples:
- name: Install python package
pacman:
packages: python
- name: Install python libraries
pacman:
packages:
- python-setuptools
- python-wheel
- name: Install instater
pacman:
packages:
- instater
aur: true
become: makepkg
Start, enable, or disable a systemctl service
service
(string): The name of the service to managestarted
(boolean, optional): If set totrue
, start the serviceenabled
(boolean, optional): Whether or not the service should be enabled
- name: Enable postgres service
service:
service: postgresql
started: true
enabled: true
- name: Start redis service
service:
service: redis
started: true
Create a symlink to a file or directory
(see file
)
- name: Create a symlink
symlink:
path: /path/to/new/linked/file
target: /path/to/existing/file
Copy data, transforming the content as a Jinja2 template prior to writing.
Same as passing is_template: true
to copy
(see copy
)
- name: Copy configuration template
template:
src: files/some_config
dest: "/home/{{ username }}/.config/some_config"
owner: "{{ username }}"
group: "{{ username }}"
mode: 0644
Create a UNIX user
user
(string): The name of the user to createsystem
(boolean, optional): Whether or not this user is a system user (only used when the user needs to be created)create_home
(boolean, optional): Whether or not a home directory for this user should be createdpassword
(string, optional): A hashed password to set as the user's password (only used when the user needs to be created )shell
(string, optional): The login shell to use for the usergroups
(string, [string]): The group or groups to add the user to
- name: Create primary user
user:
user: my_username
shell: /usr/bin/zsh
groups: sudo
password: "{{ login_password | password_hash('sha512') }}"
when: login_password is defined
- name: Create makepkg user
user:
user: makepkg
groups: makepkg
system: true