Configuration management in Python. No YAML. No DSL. Just code.
pip install fscmAnsible is a mass of YAML and Jinja2 that pretends to be declarative but isn't. fscm is Python. You get loops, functions, conditionals, debugging, type hints, and everything else you already know.
Dependencies are kept minimal because this stuff is security critical.
import fscm
from fscm import s, file, run, mkdir, template
# Install packages (auto-detects apt/pacman/brew)
s.pkgs_install("nginx", "certbot")
# Write a config file
file("/etc/nginx/sites-available/myapp", template("nginx.conf.j2", domain="example.com"))
# Run commands
run("ln -sf /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/")
run("systemctl reload nginx", sudo=True)
# See what changed
for c in fscm.CHANGELIST:
print(c)from fscm import lineinfile
# Ensure sshd config is secure
lineinfile("/etc/ssh/sshd_config", line="PermitRootLogin no", regexp=r"^#?PermitRootLogin")
lineinfile("/etc/ssh/sshd_config", line="PasswordAuthentication no", regexp=r"^#?PasswordAuthentication")
# Add a host entry
lineinfile("/etc/hosts", line="10.0.0.5 db.internal", regexp=r"db\.internal$")from fscm.modules import systemd
systemd.simple_service(
name="myapp",
exec_start="/opt/myapp/venv/bin/gunicorn -w 4 -b unix:/run/myapp.sock app:app",
user="www-data",
working_dir="/opt/myapp",
env_file="/etc/myapp/env"
)from fscm.remote import Host, SSH, Sudo, executor
host = Host(
name="web1",
connection=SSH(hostname="10.0.0.5", username="deploy"),
become=Sudo()
)
with executor(host) as exe:
exe.fscm.s.pkgs_install("nginx")
exe.fscm.file("/var/www/index.html", "<h1>deployed</h1>")
exe.fscm.run("systemctl restart nginx", sudo=True)from fscm.modules import docker
from fscm import run
if not docker.is_container_up("redis"):
run("docker run -d --name redis -p 6379:6379 redis:alpine")
# Or use docker-compose with systemd
from fscm.modules import systemd
systemd.docker_compose_service("mystack", "/opt/mystack/docker-compose.yml")from fscm.modules.wireguard import Server, Peer, server
srv = Server(address="10.100.0.1/24", listen_port=51820, private_key=srv_key, public_key=srv_pub)
peers = [
Peer(name="laptop", address="10.100.0.2/32", private_key=p1_key, public_key=p1_pub),
Peer(name="phone", address="10.100.0.3/32", private_key=p2_key, public_key=p2_pub),
]
server(srv, peers, endpoint="vpn.example.com:51820")from fscm.modules import pki
ca_key, ca_cert = pki.create_ca("Internal CA", "My Org")
svc_key, svc_cert = pki.create_cert_for_service(ca_key, ca_cert, ["api.internal", "api"], "My Org")import fscm
fscm.settings.dry_run = True # nothing actually happens
fscm.s.pkgs_install("nginx")
fscm.file("/etc/nginx/nginx.conf", "...")
print(fscm.CHANGELIST) # see what would have changed#!/usr/bin/env python3
"""Deploy a Flask app with nginx and gunicorn."""
import fscm
from fscm import s, file, run, mkdir, template
from fscm.modules import systemd
from fscm.thirdparty.clii import App
cli = App(description="Deploy Flask app")
APP = "myapp"
APP_DIR = f"/opt/{APP}"
@cli.main
def main(dry_run: bool = False):
"""dry_run: Preview changes without applying them"""
fscm.settings.dry_run = dry_run
# Packages
s.pkgs_install("nginx", "python3", "python3-venv")
# App directory
mkdir(APP_DIR)
run(f"python3 -m venv {APP_DIR}/venv")
run(f"{APP_DIR}/venv/bin/pip install flask gunicorn")
# Gunicorn service
systemd.simple_service(
name=APP,
exec_start=f"{APP_DIR}/venv/bin/gunicorn -w 4 -b unix:/run/{APP}.sock app:app",
user="www-data",
working_dir=APP_DIR
)
# Nginx
file(f"/etc/nginx/sites-available/{APP}", template("nginx.conf.j2", app=APP))
run(f"ln -sf /etc/nginx/sites-available/{APP} /etc/nginx/sites-enabled/")
run("nginx -t && systemctl reload nginx", sudo=True)
print(f"\n{len(fscm.CHANGELIST)} changes made")
if __name__ == "__main__":
cli.run()- Python-native configuration management
- Change tracking and dry-run by default
- Remote execution via SSH (powered by mitogen)
- Mainstream Unix only: Debian, Arch, macOS
- Windows
- Exotic distros
- Being a provisioning tool
- Being an orchestrator
pip install fscm # core
pip install "fscm[remote]" # + SSH execution
pip install "fscm[pki]" # + certificate generation
pip install "fscm[remote,pki]" # everythingMIT