I promised more Build modules in my previous monthly Monday module blog post: package-method-winget.
And here they are: windows-capability and windows-optional-feature.
Inventory Both of these modules use similar usage details to control whether to inventory and to promise the state for specific capabilities and optional features.
By default inventory is taken in the form of a classic array which ends up in the Mission Portal as a comma separated list:
OpenSSH.Server~~~~0.0.1.0:Installed, OpenSSH.Client~~~~0.0.1.0:Installed, etc. To disable this inventory, define the class disable_windows_capability_inventory or disable_windows_optional_feature_inventory in the data namespace. The data namespace is the default if you use Host specific data or Group data. If you want to set these in augments you will need to specify the namespace explicitly like this:
Ever want a custom CFEngine Agent logo? Check out agentsvg.
You can find agentsvg in core/contrib/ it’s a python script that can generate CFEngine agent logos.
python3 ./agentsvg.py > agent.svg python3 ./agentsvg.py --body="#f5821f" --head="#052569" > agent-body-head-colors.svg In addition to customizing the head and body colors you can customize the arm positions to be up, down, out or angled and the legs can be straight or out
python3 ./agentsvg.py --arms angled --legs out > agent-arms-legs.svg Happy Friday! 🎉
Did you know you can use variables in class expressions?
If you are reading this, you probably are already familiar with the ability to use class expressions to restrict the context of multiple promises. For example, here we have three reports type promises, all guarded by the class expression linux::.
bundle agent __main__ { reports: linux:: "Only hosts with the linux class"; "Will have these promises"; "In context"; } And, if you are tracking this series, you know that you can restrict the context of a single promise using if or unless.1 However, you can also use variables in class expressions. Let’s take a look.
Get CFEngine support in Zed, “The editor for what’s next”.
“Zed is a next-generation code editor designed for high-performance collaboration with humans and AI.” Cody and Nick are joined by Herman for an introduction to tree-sitter grammars, language servers and a quick demo of progress being made to add CFEngine support to the Zed editor. After reviewing the progress made and future work Nick shows some features of Emacs (Spacemacs) and Org-mode with CFEngine.
Class expressions are powerful. They let you restrict the context for multiple promises in a single statement. What if you want to further control the context of a specific promise?
Let’s take a look at a contrived example:
/tmp/feature-friday-28-0.cf bundle agent __main__ { reports: "I am running $(sys.os_release[PRETTY_NAME])"; linux:: "I love Linux!"; linux.ubuntu:: "Especially Ubuntu."; linux.redhat:: "Especially RedHat."; linux.!(ubuntu|redhat):: "But not RedHat or Ubuntu."; } command cf-agent -Kf /tmp/feature-friday-28-0.cf output R: I am running Ubuntu 22.04.4 LTS R: I love Linux! R: Especially Ubuntu. Here, we have a report showing the distribution we’re running through class expressions protecting the individual promises. We would see I love Linux! on Linux hosts. Depending on the specific distribution running we would see Especially Ubuntu. or Especially RedHat.. If we are running something other than those two distributions But not RedHat or Ubuntu. would be reported.
When promises are actuated, a class can be defined based on its result. For example, if a promise modifies a file’s content, you could define a class that indicates it has been repaired. However, did you know that promises can have multiple outcomes concurrently?
That’s right! Native promises (but not custom promises) can have multiple outcomes. For example, a promise can be both kept and repaired at the same time. Let’s take a look.
Looking for a place to chat about CFEngine?
Historically CFEngineers could be found in #cfengine on irc.freenode.net but we moved to #cfengine on irc.libera.chat in 2021. At that time we introduced some chat bridges linking various channels together. The bridge between Matrix and Libera was temporarily shut down last August but that temporary measure eventually became a permanent situation leaving our channels split.
Since then we have decided to remain on Matrix and have established a new channel CFEngine:matrix.org, we hope to see you there!
There’s a users promise type for managing local users. However, did you know there is also a custom one for managing local groups?
You might have seen it mentioned in the CFEngine Build announcement, the blog post on Managing local groups, or in the announcement supporting custom bodies post. But let’s take another look. The easiest way to integrate the groups custom promise type is by using cfbs, simply cfbs add promise-type-groups in your project. Next, we need some policy that leverages the groups promise type. Let’s create groups.cf in the projects root directory and add it to the project with cfbs add ./groups.cf, selecting the option to add the groups bundle to the bundlesequence.
As a developer and user of CFEngine I want to use policy to manage the software on my systems so that I can switch operating systems, distributions, computers and have all my normal tools available wherever I go.
Towards this end I searched for a Windows package manager and found one in winget. I showed a prototype in Agent Is In - Episode 37 - Windows package management as well as refined the whole process in Agent Is In - Episode 40 - Windows module workshop.
Generally, cf-agent runs as a privileged user. But did you know that you can also run as an unprivileged user?
A major benefit of running cf-agent unprivileged is the ability to prototype policies during development. However, attempting to execute cf-agent as an unprivileged user without proper configuration will result in errors. Let’s create /tmp/feature-friday-25.cf with the following content:
/tmp/feature-friday-25.cf bundle agent main { reports: "Happy Friday!"; } Now, let’s try running that policy with cf-agent as an unprivileged user: