How to write idempotent script

Usually I do “that” on two times:
→ delete all inside internal flash or nand ←

  1. Paste on terminal one script to set BIOS (RouterBOOT) parameters, set internal sdd (if any), microsd (if any), create needed folders and files inside internal flash or nand memory.
    → reset without keep anything ←
    2a) After reboot, open new terminal, and after press “y”, paste another script for auto-configure the device for the supposed use
    OR
    2b) Prepare the export of previous device, removing all referencies to the MAC addresses (and remove also all auto-mac=no) when must be doed,
    after reboot, open new terminal, and after press “y”, paste the corrected exported configuration

Yup, the /system/reset-configuration requires a reboot. But you can add a "run-after-reboot=" to import a configuration.

The docs explain this, and the configuration system generally, pretty well: RouterOS - RouterOS - MikroTik Documentation

You can see how Mikrotik approaches an "import from clean" script by using:

/system default-configuration print

but the export does NOT generate this style but you'll note Mikrotik uses ":delay" and searches for comments. But gives you a feel for what a "import after a 'reset-configuration no-default=yes' " look like.

@rextended is correct, a "import-able" script sometimes take some trial-and-error to be safe to import.

But searching for comments (e.g. "defconf") using "find" is one way used by Mikrotik, is one way to construct your own idempotent script. But even Mikrotik default-configuration isn't idempotent. So ain't any built-in way to achieve idempotence using export/import – Mikrotik say use backup/restore for that on the same box.

Lately I have been using /export /import on my router during the v7 migration and I can assure you that a /export will not properly /import even on the same device, also not after inserting a :delay 30 at the top, due to various minor mistakes in the /export file.
These bugs are sometimes fixed (e.g. 7.2rc3 fixes one) but other problems just remain there for years. And I think the fact that it requires insertion of the :delay at the top is a bug as well, this was not required somewhere in the 6.32 ages but at some point suddenly it was needed.
IMHO the loading of the script specified in “run-after-reboot=” should only start AFTER the router has completed its interface initialisation.
It should be able to determine that using some deterministic method, instead of using a delay.

Totally agree. I too like an idempotent script. But as @pe1chl points out even export then import ONCE is fraught with problems. So long way from direct support for idempotence. But no shortage of workarounds/hacks.

Wrapping groups of configuration into a function is another approach. I’ve been trying this in V7 by having a script file with only functions, that I then just import. Since my first import is only functions, no actual configuration, it does nothing (other than expose a set of :global x do={}). Then I have another script that calls a subset of the functions to create/fixup a config – I write each function to be idempotent – instead of trying make a whole script file idempotent.

And with parameters on my functions can do stuff like have another script file that makes 10 functions calls, each with different “vlanid=”, that add 10 VLANs (with each function call adding [setting/removing] the associated DHCP server, IP address, FW rules, etc for the VLAN). In my case, I generally start with the assumption the default-configuration is applied (vs no configuration), and have then function to “fix it”, this avoid dropping connection when interfaces change and also then QuickSet generally isn’t as potential destructive if you leave the “defconf” comments alone (e.g. on the default LAN IP address etc).

Not ideal. But yeah I wanted to avoid all the oddities of import/export myself, since @pe1chl right it’s just not easy to even do export then import AND hasn’t gotten better with time either…