Modify a YAML file

12 views
Skip to first unread message

Jerome Jean Verleyen

unread,
Aug 11, 2025, 5:26:59 PMAug 11
to help-cfengine
Dear all

I'm really a newbie in using cfengine. I'v do som policy to configure a server, and learning with errors and corrections. I managed to install some services, maybe not in a "elegant" way at this moment, but funcionally.

Right now, i'm facing the need to modify a YAML file from some value. For example, change the following line:
ipaddr: 10.0.0.1 
with
ipaddr: 172.16.1.1

I could get back in a policy test file the line of this ipaddr, with the readyaml funtion. But i could not foud how to write off this new values inside the original file.

I' could use a copy function to copy the correct file, and it's funcional. But requiee that i put this good file in a specific repository.
It's my next step of learning this great automation system that is Cfengine.

Regards!

Lars Erik Wik

unread,
Aug 12, 2025, 4:40:12 AMAug 12
to help-cfengine
Hi Jerome, you can maybe use the `replace_line_end` bundle from the standard library in masterfiles. The following policy illustrates how this works. I wrote it in the style of an acceptance test. However, the only bundle you would need is the `bundle agent test`.

```cf3
body common control
{
  # Run bundles in this order
  bundlesequence => { "init", "test", "check" };
  # Include files.cf from standard library
  inputs => { "$(sys.libdir)/files.cf" };
}

bundle agent init
{
  # Create a file with original content
  files:
    "/tmp/somefile.yaml"
      content => "ipaddr: 10.0.0.1";
}

bundle agent test
{
  # Replace IP address of file
  files:
    "/tmp/somefile.yaml"
      edit_line => replace_line_end("ipaddr:", "172.16.1.1");
}

bundle agent check
{
  # Check if file has correct content after edit
  classes:
    "ok"
      expression => strcmp(readfile("/tmp/somefile.yaml"), "ipaddr: 172.16.1.1"),
      if => fileexists("/tmp/somefile.yaml");

  # Print message based on outcome
  reports:
    ok::
      "IP addr was replaced";
    !ok::
      "IP addr was not replaced";
}
```

From the output below you can see that the file is created with the original content in `bundle agent init`. Then `bundle agent test` replaces the IP address. Finally `bundle agent init` reports a message based on the outcome.

```
$ sudo cf-agent -KIf ~/test.cf
    info: Created file '/tmp/somefile.yaml', mode 0600
    info: Updated file '/tmp/somefile.yaml' with content 'ipaddr: 10.0.0.1'
    info: Set field sub-value '172.16.1.1' in '/tmp/somefile.yaml'
    info: fields_edit promise '\s*ipaddr:\s.*' repaired
    info: Edited file '/tmp/somefile.yaml'
R: IP addr was replaced
```

Please let me know if something is unclear :)

Jerome Verleyen

unread,
Aug 12, 2025, 7:04:21 PMAug 12
to help-c...@googlegroups.com
Dear Lars

Your explanation and example policy is very celar. It's time for me to
understand that i know just 20% of all of the information abour libraries.

What i notice is that the replace_line_end is usable just once. In my
case, i need change various lines.
In your example. if i add a line in the somefile.yaml, like this:

bundle agent init
{
      # Create a file with original content
  files:
      "/tmp/somefile.yaml"
    content => "ipaddr: 10.0.0.1
network: 10.0.0.0";

}

And want to change natwork too, i used a "bad" rule like this:

  files:
      "/tmp/somefile.yaml"
        edit_line => replace_line_end("ipaddr:", "172.16.1.1");

     "/tmp/somefile.yaml"
        edit_line => replace_line_end("network:", "172.16.1.0");

As i can figure out how to include this 2 change in one file rule... I
need to investigate more abour all of this.

However, thank's a lot for yur anser. That help me a lot!

Regards
--
-- Jérôme
"C'est que, m'a expliqué papa, il y a des fois où ça lui fait de la peine
à ta maman, de te voir grandir.
D'ailleurs, tu comprendras plus tard qu'avec les femmes, il ne faut pas
chercher a comprendre.
(Histoires inédites du Petit Nicolas, Goscinny & Sempé)

craig.c...@northern.tech

unread,
Aug 12, 2025, 9:29:20 PMAug 12
to Jerome Verleyen, help-c...@googlegroups.com
On Tue, Aug 12, 2025 at 05:04:16PM -0600, Jerome Verleyen wrote:
> replace_line_end is usable just once. In

I think you should use a mustache template with a files promise.

Here is an example to get started. There is much more you can do depending on the data you provide and the nesting available.

https://docs.cfengine.com/docs/3.24/examples-tutorials-render-files-with-mustache-templates.html#top

-Craig
signature.asc

Lars Erik Wik

unread,
Aug 13, 2025, 3:23:18 AMAug 13
to help-cfengine
I believe replace_line_end should be usable more than once. Here is an example doing so:

```cf3
body common control
{
  # Run bundles in this order
  bundlesequence => { "init", "test", "check" };
  # Include files.cf from standard library
  inputs => { "$(sys.libdir)/files.cf" };
}

bundle agent init
{
  # Create a file with original content
  files:
    "/tmp/somefile.yaml"
      content => "ipaddr: 10.0.0.1
network: 10.0.0.0";

}

bundle agent test
{
  # Replace IP address of file
  files:
    "/tmp/somefile.yaml"
      edit_line => replace_line_end("ipaddr:", "172.16.1.1");
    "/tmp/somefile.yaml"
      edit_line => replace_line_end("network:", "172.16.1.0");
}

bundle agent check
{
  # Check if file has correct content after edit
  classes:
    "ok"
      expression => strcmp(readfile("/tmp/somefile.yaml"), "ipaddr: 172.16.1.1
network: 172.16.1.0

"),
      if => fileexists("/tmp/somefile.yaml");

  # Print message based on outcome
  reports:
    ok::
      "IP addr was replaced";
    !ok::
      "IP addr was not replaced";
}
```

And here is an example running that policy
```

$ sudo cf-agent -KIf ~/test.cf
    info: Updated file '/tmp/somefile.yaml' with content 'ipaddr: 10.0.0.1
network: 10.0.0.0'

    info: Set field sub-value '172.16.1.1' in '/tmp/somefile.yaml'
    info: fields_edit promise '\s*ipaddr:\s.*' repaired
    info: Edited file '/tmp/somefile.yaml'
    info: Set field sub-value '172.16.1.0' in '/tmp/somefile.yaml'
    info: fields_edit promise '\s*network:\s.*' repaired

    info: Edited file '/tmp/somefile.yaml'
R: IP addr was replaced
```

Hope this helps :)

Mustache templates that Craig mentioned is better if you want to control the entire contents of the file. However, if you only need to configure single lines in a file. E.g., make sure this line does (not) exist, or make sure this field is configured to this value. Then edit_line is probably the correct choice.

Nick Anderson

unread,
Aug 13, 2025, 10:42:48 AMAug 13
to CFEngine Help List, Jerome Verleyen, Lars Erik Wik

Here is another variant that uses a data structure (here it's a classic associative array, but it could be a data container (defined from JSON or YAML etc …)

bundle agent __main__
{
  methods:
      "init";
      "test";
      "check";
}

bundle agent init
{
  # Create a file with original content
  files:
    "/tmp/somefile.yaml"
      content => "ipaddr: 10.0.0.1
network: 10.0.0.0";
}

bundle agent test
{
  # Replace IP address of file
  vars:
      "start_ends[ipaddr]" string => "172.16.1.1";
      "start_ends[network]" string => "172.16.1.0";
      "l_start_ends" slist => getindices( start_ends );

  files:

      # As others say, full file management is ideal, if you can do that
      #
      # This shows one way to leverage variables to call this multiple times
      # BUT, this is still not ideal, because you are potentially causing an edit for each iteration.
      # It's better to do all the edits in one operation, in which case you would need to pass the full map to the edit_line bundle.
      # You may need to construct your own edit_line bundel to do exsactly what you need
      # Look at other edit_line bundles in the stdlib that contain replace_lines:

    "/tmp/somefile.yaml"
      edit_line => replace_line_end("$(l_start_ends):", "$(start_ends[$(l_start_ends)])" );
}

bundle agent check
{
  # Check if file has correct content after edit
  classes:
    "ok"
      expression => strcmp(readfile("/tmp/somefile.yaml"), "ipaddr: 172.16.1.1
network: 172.16.1.0
"),
      if => fileexists("/tmp/somefile.yaml");

  # Print message based on outcome
  reports:
    ok::
      "IP addr was replaced";
    !ok::
      "IP addr was not replaced";
}

    info: Created file '/tmp/somefile.yaml', mode 0600
    info: Updated file '/tmp/somefile.yaml' with content 'ipaddr: 10.0.0.1
network: 10.0.0.0'
    info: Set field sub-value '172.16.1.0' in '/tmp/somefile.yaml'
    info: fields_edit promise '\s*network:\s.*' repaired
    info: Edited file '/tmp/somefile.yaml'
    info: Set field sub-value '172.16.1.1' in '/tmp/somefile.yaml'
    info: fields_edit promise '\s*ipaddr:\s.*' repaired
    info: Edited file '/tmp/somefile.yaml'
R: IP addr was replaced

Jerome Verleyen

unread,
Aug 15, 2025, 7:02:12 PMAug 15
to Nick Anderson, CFEngine Help List, Lars Erik Wik
Dear Nick et all ...

Le 13/08/2025 à 08:42, Nick Anderson a écrit :

Here is another variant that uses a data structure (here it's a classic associative array, but it could be a data container (defined from JSON or YAML etc …)

bundle agent test
{
  # Replace IP address of file
  vars:
      "start_ends[ipaddr]" string => "172.16.1.1";
      "start_ends[network]" string => "172.16.1.0";
      "l_start_ends" slist => getindices( start_ends );

 

      

I forgot totaly the magic of the list in Cfengine, with automatic loops ! That's make all the stuff in my case. Thank's a lot.

Thank's too to Lars and Craig with their solutions. I will use in others stuff, although.

I've learn a lot with our solutions of all, and late to answer that i implement solutions in the moment!

Regards!

-- 
-- Jérôme 
J'aime le travail : il me fascine. 
Je peux rester des heures à le regarder
	(Jérôme K. Jérôme)

Nick Anderson

unread,
Aug 15, 2025, 8:01:24 PMAug 15
to jerome....@ibt.unam.mx, CFEngine Help List

I forgot totaly the magic of the list in Cfengine, with automatic loops ! That's make all the stuff in my case. Thank's a lot.

Thank's too to Lars and Craig with their solutions. I will use in others stuff, although.

I've learn a lot with our solutions of all, and late to answer that i implement solutions in the moment!

Regard!

Excellent!

Reply all
Reply to author
Forward
0 new messages