Introduction
Salt is a configuration management and distributed remote execution system. It was created with the goal to improve, simplify, and further customize the best remote execution solutions. It can maintain the remote nodes in defined states and query and execute commands on various nodes.

In this blog, we will discuss grains in the salt system.
Grains
Grain is a salt interface that derives information about the underlying system. Grains are collected for the IP address, domain name, OS, kernel, OS type, and various other system properties.
The salt modules have access to the grains interface so that the correct salt-minion commands are automatically available on the correct systems.
Let’s discuss how to list grains.
Listing Grains
The available grains can be listed using the 'grains.ls' module:
salt '*' grains.ls
The grains data can be listed using the ​​ 'grains.items' module:
salt '*' grains.items
Assigning Grains to Minions
The multiple ways to assign grains to minions are:-
GRAINS IN THE MINION CONFIG
Grains can be assigned statically inside the minion configuration file. You just need to add the option grains and pass the options.
grains:
roles:
- webserver
- memcache
deployment: datacenter4
cabinet: 13
cab_u: 14-15
GRAINS IN /ETC/SALT/GRAINS
Previously we have assigned the grains to the minion configuration file, but if you don't want to place your custom grains inside the minion configuration file. You can put them in /etc/salt/grains on the minion. Let’s see the below example:-
roles:
- webserver
- memcache
deployment: datacenter4
cabinet: 13
cab_u: 14-15
Matching Grains In Top File
If the grains are correctly configured on minions, the top file used in Pillar can be made very efficient. Let’s see the following configuration:
'roles: webserver':
- match: grain
- state0
'roles:memcache':
- match: grain
- state1
- state2
Writing Custom Grains
The rules to write your own custom grains are:-
-
Grains are derived by executing all the public functions. Public functions are those function which does not begin with an underscore.
-
The functions in a grains module must return a Python dictionary, where the keys of the dictionary represent the name of the grains, and the values represent the value for the grain corresponding to that key.
-
The custom grains modules must be put in the _grains subfolder under the file_roots given in the master config file.
-
The custom grains modules are given to the minions when the state.highstate is executed, or by executing the saltutil.sync_grains or saltutil.sync_all functions.
Let’s see an example to understand how to write custom grains:
def codingninjas():
# initialize a grains dictionary
grains = {}
# Some code for logic that sets grains like
grains["codingninjas"] = True
grains["anothergrain"] = "somevalue"
return grains
When to Use Custom Grains
Grains are used for the static data and the data that is helpful for targeting minions in the top file or the Salt CLI. If the data is likely to change, use other modules instead of Grains.
Loading Custom Grains
If there are multiple functions specifying grains and are called from the main function, make sure to use an underscore as a prefix with the name of grains. Let’s consider the custom grain file:-
#!/usr/bin/env python
def _ninjas_custom_grain():
ninjas_grains = {"hi": "hey", "hello": "ninjas"}
return ninjas_grains
def main():
# initialize a grains dictionary
grains = {}
grains["ninjas_grains"] = _ninjas_custom_grain()
return grains
The output of the above example will look like this:-
# salt-call --local grains.items
local:
----------
<Snipped for brevity>
ninjas_grains:
----------
hi:
hey
hello:
ninjas
Let’s take a case where you have not prepended the underscore before the grain function name. In this case, the function will be rendered twice by Salt. The output for the above-taken example, in this case, will be:-
# salt-call --local grains.items
local:
----------
<Snipped for brevity>
hi:
hey
<Snipped for brevity>
hello:
ninjas
<Snipped for brevity>
ninjas_grains:
----------
hi:
hey
hello:
ninjas