CFEngine is a scalable configuration management tool for your IT infrastructure. CFEngine allows you to use the same tool and policy language to manage everything in your infrastructure: Linux, Mac, Windows, BSD, Solaris, IBM AIX, HP-UX, and others. You install an agent (written in C) on each machine you want to manage. CFEngine supports many platforms and is useful for resource-constrained environments such as large infrastructures or small Internet of Things (IoT) devices.
In this tutorial, you will install CFEngine Community Edition 3.21 on Ubuntu 20.04, write sample policy files, and automate your policy runs.
To complete this tutorial, you will need:
sudo user. To set this up, follow our guide Initial Server Setup with Ubuntu 20.04.
sudo), such as
In this step, you will install CFEngine using
apt and the package repositories. The main advantage of using
apt for installation is that you can use
apt to update CFEngine later.
There are other ways to install CFEngine for different use cases:
cf-remote is a flexible choice that handles installing on multiple machines, different versions of CFEngine, and nightly builds.
quick-install shell script is a good choice for a single machine. You can install by copying a single command into the terminal.
To install CFEngine using
apt, you need
apt to be able to access the repository. The first step is to add the CFEngine public GPG key to the
apt key collection so that
apt can trust packages from the repository.
Download the key with this command:
- wget https://cfengine.com/pub/gpg.key -O ~/cfengine-gpg.key
apt-key to add it:
- sudo apt-key add ~/cfengine-gpg.key
Next, you need to add the CFEngine repository as a source to
apt with the following command:
- sudo sh -c "echo 'deb https://cfengine-package-repos.s3.amazonaws.com/pub/apt/packages stable main' > /etc/apt/sources.list.d/cfengine-community.list"
This command adds the CFEngine repository to a file in the
apt sources list directory.
sh argument and
-c option, this command would get a
Permission denied error because the redirection of the output is not performed by
sudo. To address this issue, you use
sh to run a shell with
sudo and give the command to it by using the
Now, run the
update command so that
apt can see the new packages from the CFEngine repository:
- sudo apt update
With the CFEngine package now registered with
apt, you can use
apt to install CFEngine:
- sudo apt install cfengine-community
If you receive a warning about the package being built on a different (older) operating system, you can ignore it. The package still works.
When the installation is complete, you will see the components that make up CFEngine in
/var/cfengine/bin. Out of all of these, the agent (
cf-agent) is the one you will interact with most of the time. This binary evaluates the policy you write, makes changes to the system, and records information about what it did. By default, the agent will wake up every five minutes, fetch new policy if necessary, and enforce it.
Use the following command to check that the agent was installed successfully:
- sudo cf-agent --version
The version number will print to the screen:
OutputCFEngine Core 3.21.0
Note: At the time of this writing, the current version is
3.21.0. Your version number may differ.
cf-agent command doesn’t work, check that
/var/cfengine/bin/ is in your
PATH variable and that
cf-agent exists in that directory.
You can also check the installation log file in
/var/log/CFEngine-Install.log for some hints about what went wrong.
To start CFEngine, you need to bootstrap the agent. Use the following command to do that:
- sudo cf-agent --bootstrap 127.0.0.1
This command tells the agent to start the CFEngine components and to fetch policy from this machine,
In the future, you will want multiple machines to fetch policy from the same server. In that case,
127.0.0.1 will be replaced by the IP address of that server. However, for getting started and learning about CFEngine, you’ll use one machine and
127.0.0.1 in this tutorial.
You now have CFEngine installed and running on your server. The next step is to start writing policy.
To automate a system administration task using CFEngine, you create a policy file, which is written in CFEngine’s own Domain Specific Language (DSL). To learn more about the policy language, such as what it can do and how it’s structured, see the CFEngine reference documentation.
Policy files differ from scripts in that they are declarative: you describe the desired state for the system, and CFEngine will check if that’s already the case and will make changes only if necessary. Some examples of things you might want to express in policy are:
curl package updated (and installed).
telnet is not installed.
httpd, if it’s running.
In all of these cases, you would write policy and put it in the correct place on your server (/var/cfengine/masterfiles). Then CFEngine will:
sammy, or changed the permissions on
/usr/bin, these changes would be restored automatically within five minutes.
In this step, you’ll create a “Hello World” policy to output the text “Hello!” to the terminal.
nano or your favorite text editor to create a new file,
- nano ~/hello_world.cf
Within the file, add the following:
bundle agent main
CFEngine policy is declarative and is not evaluated from top to bottom.
In CFEngine policy language, each statement you make about what you are managing is called a promise. Promises are organized into bundles. Bundles are useful because they allow you to choose which parts of the policy are run (and which parts aren’t) and what order they run in.
In this policy,
reports is a promise type, responsible for printing the message to the terminal. The
agent keyword, after
bundle, signifies that this bundle is for the
cf-agent component. The name of the bundle is
main. The bundle sequence specifies the bundles to evaluate. By default, it includes the
main bundle, so you don’t need to change it.
Save and close the file. Using
CTRL+X to exit,
Y to save, and
ENTER to confirm the filename and close the file.
Since CFEngine typically runs as the root user, it tries to prevent unauthorized changes to the system by requiring strict permissions on policy files. (Allowing other users to edit the policy CFEngine runs would allow them to make changes to the system as a privileged user). You can use
chmod to edit the file permissions and thus prevent other users from editing this file:
- chmod 600 hello_world.cf
To run the policy, use the following command:
- sudo cf-agent ~/hello_world.cf
The CFEngine agent finds your bundle (because of the
main name), looks at the promises inside of it, and evaluates them. When evaluating promises, the agent checks whether it needs to make changes to the system. In this case, there was just one promise, and the agent determined that it needs to print a message to the terminal to satisfy it.
You’ll receive the following output:
By default, CFEngine keeps track of what has been done and skips promises evaluated recently (within the last minute). If you try to rerun the policy, it won’t print anything. You can disable this locking with the
--no-lock command line option:
- sudo cf-agent --no-lock hello_world.cf
You’ve created and run your first policy. Although it is useful to be able to output to the console, this policy doesn’t actually change the system. You’ll do that in the next step.
In the previous step, you created a policy to print a message to the terminal. However, a more realistic use case is ensuring that a file with some specific content exists on all the hosts in your infrastructure. In this step, you will write a policy that creates a text file (
/tmp/my_file.txt) with the contents
Open a new policy file,
- nano ~/file_management.cf
Add the following content:
bundle agent manage_my_file
content => "Hello, CFEngine!$(const.n)";
The bundle’s name is
manage_my_file instead of
main. As you write more policy, you should give each bundle a unique and descriptive name. There can only be one
Because this policy manages files, the promise type is
content attribute is used for
files promises, stating what the content of that file should be. The last part of the string,
$(const.n), expands a special variable
const.n, which results in a new line at the end of the file.
Save and close the file.
As before, set the strict permissions on the policy file:
- chmod 600 file_management.cf
Now run the file with some additional command line options:
- sudo cf-agent --no-lock --info ~/file_management.cf --bundle manage_my_file
Specifying the bundle with
--bundle manage_my_file is necessary since there is no longer a
--info option makes CFEngine print informational messages about the changes it’s making to the system.
The output from this command will contain information about these changes:
Outputinfo: Using command line specified bundlesequence
info: Created file '/tmp/my_file.txt', mode 0600
info: Updated file '/tmp/my_file.txt' with content 'Hello, CFEngine!
This output indicates that CFEngine created a text file called
my_file.txt in the
/tmp directory with the contents
If you run the same command again, the messages about creating and updating the file will no longer display. CFEngine recognizes that the contents of the file are already correct, and it doesn’t make any changes.
--info are commonly used together when writing and testing policy files. To save some typing, there are shortcuts available;
-KI is equivalent to
Now that you have a working system policy, you may want to run it without your supervision. You’ll do that in the next step.
You probably don’t want to run policy manually from the command line all the time. CFEngine includes automation features to handle that.
In this step, you will automate policy runs by putting your policy files in the expected location and updating a JSON file. The JSON file periodically tells the CFEngine component what to do in the background without your having to run commands explicitly and manually from the command line.
Use the following command to copy the policy file you created in the previous step to the recommended location:
- sudo cp file_management.cf /var/cfengine/masterfiles/services/
All CFEngine policy is inside
/var/cfengine/masterfiles/. This includes policy you didn’t write that came with CFEngine. To keep your custom policy separate from the default policy, it is recommended to put your policy files inside the
When the CFEngine agents fetch new policy files, they copy them from this directory on the hub. Even if you are just using one machine, the agent still works in the same way: it will look for files in
/var/cfengine/masterfiles and copy them to
/var/cfengine/inputs. For new users, it’s best to use these paths. Customizing the paths or putting policy files in other locations requires more work since you’d have to ensure that permissions as well as copying and finding the files work correctly.
Next, create an augments JSON file to specify where the policy file is and what bundle should be run:
- sudo nano /var/cfengine/masterfiles/def.json
Add the following content to that file:
"inputs": [ "services/file_management.cf" ],
"control_common_bundlesequence_end": [ "manage_my_file" ]
There are two steps necessary to make your policy run regularly (every five minutes): make sure the agent finds and reads the file and ensure the agent runs your bundle.
inputs key in
def.json tells the agent what policy files to read. In this case, the agent will read the policy you created in the last step,
vars key can be used to define variables. The
control_common_bundlesequence_end variable is used in the default policy, so any bundle names you put there will be added to the end of the
bundlesequence and evaluated after all the default bundles. Together, these two pieces of information mean that
cf-agent knows what policy files to read and which bundles within them to evaluate without having to specify these things from the command line.
At this point, you are editing policy inside
/var/cfengine/masterfiles/ and automation takes care of the rest. More specifically,
cf-agent periodically wakes up and fetches the new policy files you’ve written. The agent will read and evaluate the policy, enforcing all the promises and making changes to the machines as necessary (such as editing files and creating users).
Based on the policy you’ve written in this tutorial, every time the agent runs, it will ensure
/tmp/my_file.txt exists with the content you specified in the policy file.
Save and close the file.
To confirm that the automation is working, delete the text file created when you first ran the file management policy:
- sudo rm /tmp/my_file.txt
After five minutes, you can confirm whether CFEngine re-created
my_file.txt in the background:
- cat /tmp/my_file.txt
Alternatively, you can also force the file creation to happen sooner:
- sudo rm /tmp/my_file.txt ; sudo cf-agent -Kf update.cf ; sudo cf-agent -KI
rm command deletes the file so CFEngine will see that changes are necessary.
cf-agent command updates the policy file, copying it from
cf-agent command enforces your policy, which makes it look for the
/tmp/my_file.txt file, and create and edit it if necessary.
In this case, you are running the agent immediately after deleting the file, so
cf-agent should print that it created the file. (The chances of an agent run happening in the background between these commands are slim to none.)
Note: The command
sudo cf-agent -Kf update.cf ; sudo cf-agent -KI is similar to the command CFEngine runs by default every five minutes. So, running this command should have the same result as waiting five minutes if CFEngine is working correctly. The first agent run updates the policy and the second one evaluates the policy and makes changes to the system.
In this step, you’ve automated your first system administration task with CFEngine. While this example focused on creating and editing a file, the process for another task would be identical: write the policy, put it in the correct directory, and update the
def.json file accordingly.
You’ve now installed and started CFEngine on a single server. You wrote your first policy and set it up to run automatically.
As a next step, check out the CFEngine official documentation, such as this tutorial on managing files: Create, Modify, and Delete files.
If you have any questions or need help, feel free to post in the Q&A section of CFEngine’s GitHub Discussions.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.