diff --git a/README.md b/README.md
index c0316332c..bae4459f4 100644
--- a/README.md
+++ b/README.md
@@ -27,6 +27,10 @@
* [zabbix-sender](#usage-zabbix-sender)
* [zabbix-userparameters](#usage-zabbix-userparameters)
* [zabbix-template](#usage-zabbix-template)
+ * [zabbix-authcfg](#usage-zabbix-authcfg)
+ * [zabbix-user](#usage-zabbix-user)
+ * [zabbix-usergroup](#usage-zabbix-usergroup)
+ * [zabbix-role](#usage-zabbix-role)
6. [Reference - An under-the-hood peek at what the module is doing and how](#reference)
7. [Limitations - OS compatibility, etc.](#limitations)
8. [Development - Contributors](#contributors)
@@ -355,6 +359,235 @@ zabbix::template { 'Template App MySQL':
}
```
+### Usage zabbix-authcfg
+
+With the `zabbix_authcfg` resource you can configure authentication via the API.
+
+Please be aware of the following limitations:
+- You can only make use of this feature when you have configured the module to make use of exported resources.
+- Only '1' is supported as the namevar, this is a Zabbix limitation.
+- Only tested on Zabbix 6.0.
+- Only LDAP and internal authentication are implemented.
+
+You can configure zabbix to use LDAP with the following example:
+``` ruby
+zabbix_authcfg { '1':
+ ensure => present,
+ authentication_type => 'LDAP',
+ ldap_host => 'ldaps://ldap.example.com'
+ ldap_port => 636,
+ ldap_base_dn => 'dc=example,dc=com',
+ ldap_bind_dn => 'CN=Manager',
+ ldap_bind_password => Sensitive('my-bind-password'),
+ ldap_search_attribute => 'sAMAccountName',
+ ldap_case_sensitive => true,
+}
+```
+
+### Usage zabbix-user
+
+With the `zabbix_user` resource you can manage users via the API.
+
+Please be aware of the following limitations:
+- You can only make use of this feature when you have configured the module to make use of exported resources.
+- Only tested on Zabbix 6.0.
+- Usergroups (if defined) must exist (you can use `zabbix_usergroup`)
+
+Example:
+
+``` ruby
+# Update admin password
+zabbix_user { 'NewUser':
+ ensure => present,
+ firstname => 'New,
+ surname => 'User',
+ role => 'Admin role',
+ usrgrps => ['Zabbix administrators'],
+ passwd => Sensitive('a_password'),
+}
+```
+
+Other supported params:
+- `autologin` (boolean)
+
+When you want to use this resource to change the default admin password you can use the helper fact `zbx_admin_passwd_default`:
+
+``` ruby
+# Use default password unless the password was changed already
+$_server_api_pass = $facts['zbx_admin_passwd_default'] ? {
+ true => Sensitive('zabbix'),
+ false => Sensitive('mynewpassword'),
+ default => Sensitive('zabbix'),
+}
+
+class { 'zabbix':
+ ...
+ zabbix_api_pass => $_server_api_pass,
+ ...
+}
+
+# Update admin password
+zabbix_user { 'Admin':
+ ensure => present,
+ firstname => 'Zabbix',
+ role => 'Super admin role',
+ surname => 'Administrator',
+ usrgrps => ['Zabbix administrators'],
+ passwd => Sensitive('mynewpassword'),
+}
+
+unless $facts['zbx_admin_passwd_default'] {
+ # Do other stuff with the API
+}
+
+```
+
+### Usage zabbix-usergroup
+
+With the `zabbix_usergroup` resource you can manage usergroups via the API.
+
+Please be aware of the following limitations:
+- You can only make use of this feature when you have configured the module to make use of exported resources.
+- Only tested on Zabbix 6.0.
+
+Example:
+
+``` ruby
+# Make sure 'Zabbix administrators' uses internal authentication and add an LDAP administrators group
+zabbix_usergroup {
+ default:
+ ensure => present,
+ ;
+ 'Zabbix administrators':
+ gui_access => 'internal',
+ ;
+ 'LDAP administrators':
+ gui_access => 'LDAP,
+ ;
+}
+
+zabbix_user { 'LDAPAdmin':
+ ensure => present,
+ firstname => 'LDAP,
+ role => 'Super admin role',
+ surname => 'Administrator',
+ usrgrps => ['LDAP administrators'],
+ passwd => Sensitive('mynewpassword'),
+ require => Zabbix_usergroup['LDAP administrators']
+}
+```
+
+`gui_access` can be one of:
+- default (use the default)
+- internal
+- LDAP
+
+Other supported parameters:
+- debug_mode (boolean - default false)
+- users_status (boolean - default true)
+
+### Usage zabbix-role
+
+With the `zabbix_role` resource you can manage Zabbix roles and role rules.
+
+Please be aware of the following limitations:
+- You can only make use of this feature when you have configured the module to make use of exported resources.
+- Only tested on Zabbix 6.0.
+- To avoid having to define enormous hashes when just overriding one default rule the provider will ignore any default rules when comparing rules. This means that if you want a role with just one rule enabled you will have to define a hash that overrides all defaults.
+
+For the role rules syntax (and information on defaults) please refer to the official zabbix documentation: https://www.zabbix.com/documentation/current/en/manual/api/reference/role/object
+
+Example:
+``` ruby
+# Create custom production role (and its rules)
+$_production_role_rules = {
+ 'ui' => [
+ {
+ 'name' => 'configuration.actions',
+ 'status' => '0'
+ },
+ {
+ 'name' => 'configuration.discovery',
+ 'status' => '0'
+ },
+ {
+ 'name' => 'configuration.host_groups',
+ 'status' => '0'
+ },
+ {
+ 'name' => 'configuration.hosts',
+ 'status' => '0'
+ },
+ {
+ 'name' => 'configuration.templates',
+ 'status' => '0'
+ },
+ ],
+ 'ui.default_access' => '1',
+ 'services.read.mode' => '1',
+ 'services.write.mode' => '0',
+ 'modules.default_access' => '0',
+ 'api.access' => '0',
+ 'actions' => [
+ {
+ 'name' => 'edit_dashboards',
+ 'status' => '1',
+ },
+ {
+ 'name' => 'edit_maps',
+ 'status' => '1',
+ },
+ {
+ 'name' => 'acknowledge_problems',
+ 'status' => '1',
+ },
+ {
+ 'name' => 'close_problems',
+ 'status' => '1',
+ },
+ {
+ 'name' => 'change_severity',
+ 'status' => '1',
+ },
+ {
+ 'name' => 'add_problem_comments',
+ 'status' => '1',
+ },
+ {
+ 'name' => 'execute_scripts',
+ 'status' => '0',
+ },
+ {
+ 'name' => 'edit_maintenance',
+ 'status' => '1',
+ },
+ {
+ 'name' => 'manage_scheduled_reports',
+ 'status' => '1',
+ },
+ {
+ 'name' => 'manage_sla',
+ 'status' => '1',
+ },
+ ],
+ 'actions.default_access' => '1',
+}
+
+zabbix_role { 'Production role':
+ ensure => present,
+ type => 'Admin',
+ rules => $_production_role_rules,
+}
+
+Type can be one of:
+- User
+- Admin
+- Super admin
+
+Other supported params:
+- readonly (boolean - default false)
+```
+
## Zabbix Upgrades
It is possible to do upgrades via this module. An example for the zabbix agent:
diff --git a/REFERENCE.md b/REFERENCE.md
index 22a1ed425..0cc0cc45f 100644
--- a/REFERENCE.md
+++ b/REFERENCE.md
@@ -42,11 +42,15 @@
### Resource types
* [`zabbix_application`](#zabbix_application): Manage zabbix applications Example: zabbix_application{"app1": ensure => present, template => 'template1', } It Raise exceptio
+* [`zabbix_authcfg`](#zabbix_authcfg): %q("Manage zabbix authentication configuration zabbix_authcfg { '1': ensure => present, ldap_configured => true,
* [`zabbix_host`](#zabbix_host): Manage zabbix hosts
* [`zabbix_hostgroup`](#zabbix_hostgroup): Manage zabbix hostgroups
* [`zabbix_proxy`](#zabbix_proxy): Manage zabbix proxies
+* [`zabbix_role`](#zabbix_role): %q("Manage zabbix role objects zabbix_role { 'My custom role': ensure => present, type => 'Admin', readonly => false, rules => {
* [`zabbix_template`](#zabbix_template): Manage zabbix templates
* [`zabbix_template_host`](#zabbix_template_host): Link or Unlink template to host. Only for Zabbix < 6.0! Example: zabbix_template_host{ 'mysql_template@db1': ensure => present, } Na
+* [`zabbix_user`](#zabbix_user): %q("Manage zabbix users zabbix_user{ 'username': ensure => present, firstname => 'firstname', surname => 'surname', passwd
+* [`zabbix_usergroup`](#zabbix_usergroup): %q("Manage zabbix usergroups zabbix_usergroup{ 'group1': ensure => present, gui_access => 2, debug_mode => true, users_sta
* [`zabbix_userparameters`](#zabbix_userparameters): Manage zabbix user templates
### Data types
@@ -6016,6 +6020,102 @@ usually discover the appropriate provider for your platform.
template to which the application is linked
+### `zabbix_authcfg`
+
+%q("Manage zabbix authentication configuration
+
+zabbix_authcfg { '1':
+ ensure => present,
+ ldap_configured => true,
+ ldap_host => 'ldap://host.name',
+ ldap_port => '389',
+ ldap_base_dn => 'dc=example,dc=com',
+ ldap_search_attribute => 'uid',
+ ldap_bind_dn => 'cn=Manager',
+ ldap_bind_password => Sensitive('password'),
+ ldap_case_sensitive => true,
+}")
+
+#### Properties
+
+The following properties are available in the `zabbix_authcfg` type.
+
+##### `authentication_type`
+
+Valid values: `internal`, `LDAP`
+
+authentication type
+
+Default value: `internal`
+
+##### `ensure`
+
+Valid values: `present`
+
+The basic property that the resource should be in.
+
+Default value: `present`
+
+##### `ldap_base_dn`
+
+LDAP base DN
+
+##### `ldap_bind_dn`
+
+LDAP bind DN
+
+##### `ldap_bind_password`
+
+LDAP bind password
+
+##### `ldap_case_sensitive`
+
+Valid values: `true`, `false`
+
+LDAP case sensitive login
+
+Default value: `true`
+
+##### `ldap_configured`
+
+Valid values: `true`, `false`
+
+Enable LDAP authentication
+
+Default value: `true`
+
+##### `ldap_host`
+
+LDAP host
+
+##### `ldap_port`
+
+LDAP port
+
+##### `ldap_search_attribute`
+
+Valid values: `uid`, `sAMAccountName`
+
+LDAP search attribute
+
+#### Parameters
+
+The following parameters are available in the `zabbix_authcfg` type.
+
+* [`id`](#-zabbix_authcfg--id)
+* [`provider`](#-zabbix_authcfg--provider)
+
+##### `id`
+
+Valid values: `1`
+
+authentication settings id
+
+##### `provider`
+
+The specific backend to use for this `zabbix_authcfg` resource. You will seldom need to specify this --- Puppet will
+usually discover the appropriate provider for your platform.
+
### `zabbix_host`
Manage zabbix hosts
@@ -6209,6 +6309,83 @@ FQDN of the proxy.
The specific backend to use for this `zabbix_proxy` resource. You will seldom need to specify this --- Puppet will
usually discover the appropriate provider for your platform.
+### `zabbix_role`
+
+%q("Manage zabbix role objects
+
+zabbix_role { 'My custom role':
+ ensure => present,
+ type => 'Admin',
+ readonly => false,
+ rules => {
+ 'ui'=> [
+ {
+ 'name' => 'configuration.actions',
+ 'status' => '1'
+ },
+ {
+ 'name' => 'configuration.discovery',
+ 'status'=>'0'
+ },
+ ],
+ 'ui.default_access' => '1',
+ 'services.read.mode' => '1',
+ 'services.write.mode' => '0',
+ 'modules.default_access' => '0',
+ 'api.access' => '0',
+ }
+}")
+
+#### Properties
+
+The following properties are available in the `zabbix_role` type.
+
+##### `ensure`
+
+Valid values: `present`, `absent`
+
+The basic property that the resource should be in.
+
+Default value: `present`
+
+##### `readonly`
+
+Valid values: `true`, `false`
+
+Whether the role is readonly
+
+Default value: `false`
+
+##### `rules`
+
+The role rules
+
+##### `type`
+
+Valid values: `User`, `Admin`, `Super admin`
+
+role type
+
+Default value: `User`
+
+#### Parameters
+
+The following parameters are available in the `zabbix_role` type.
+
+* [`name`](#-zabbix_role--name)
+* [`provider`](#-zabbix_role--provider)
+
+##### `name`
+
+namevar
+
+the name of the role
+
+##### `provider`
+
+The specific backend to use for this `zabbix_role` resource. You will seldom need to specify this --- Puppet will
+usually discover the appropriate provider for your platform.
+
### `zabbix_template`
Manage zabbix templates
@@ -6328,6 +6505,145 @@ template_name@host_name
The specific backend to use for this `zabbix_template_host` resource. You will seldom need to specify this --- Puppet
will usually discover the appropriate provider for your platform.
+### `zabbix_user`
+
+%q("Manage zabbix users
+
+zabbix_user{ 'username':
+ ensure => present,
+ firstname => 'firstname',
+ surname => 'surname',
+ passwd => Sensitive(password),
+ autologin => 0,
+ role => 'Admin role',
+ usrgrps => [ 'Group1' ],
+}")
+
+#### Properties
+
+The following properties are available in the `zabbix_user` type.
+
+##### `autologin`
+
+Valid values: `true`, `false`
+
+Whether auto login is enabled or disabled.
+
+Default value: `false`
+
+##### `ensure`
+
+Valid values: `present`, `absent`
+
+The basic property that the resource should be in.
+
+Default value: `present`
+
+##### `firstname`
+
+user firstname
+
+##### `passwd`
+
+user password
+
+##### `role`
+
+user role
+
+Default value: `User role`
+
+##### `surname`
+
+user surname
+
+##### `usrgrps`
+
+user groups
+
+Default value: `[]`
+
+#### Parameters
+
+The following parameters are available in the `zabbix_user` type.
+
+* [`provider`](#-zabbix_user--provider)
+* [`username`](#-zabbix_user--username)
+
+##### `provider`
+
+The specific backend to use for this `zabbix_user` resource. You will seldom need to specify this --- Puppet will
+usually discover the appropriate provider for your platform.
+
+##### `username`
+
+user name
+
+### `zabbix_usergroup`
+
+%q("Manage zabbix usergroups
+
+zabbix_usergroup{ 'group1':
+ ensure => present,
+ gui_access => 2,
+ debug_mode => true,
+ users_status => true,
+}")
+
+#### Properties
+
+The following properties are available in the `zabbix_usergroup` type.
+
+##### `debug_mode`
+
+Valid values: `true`, `false`
+
+Whether debug mode is enabled or disabled.
+
+Default value: `false`
+
+##### `ensure`
+
+Valid values: `present`, `absent`
+
+The basic property that the resource should be in.
+
+Default value: `present`
+
+##### `gui_access`
+
+Valid values: `default`, `internal`, `LDAP`, `none`
+
+The type of access for this group.
+
+Default value: `default`
+
+##### `users_status`
+
+Valid values: `true`, `false`
+
+Whether the user group is enabled or disabled.
+
+Default value: `true`
+
+#### Parameters
+
+The following parameters are available in the `zabbix_usergroup` type.
+
+* [`name`](#-zabbix_usergroup--name)
+* [`provider`](#-zabbix_usergroup--provider)
+
+##### `name`
+
+namevar
+
+usergroup name
+
+##### `provider`
+
+The specific backend to use for this `zabbix_usergroup` resource. You will seldom need to specify this --- Puppet will
+usually discover the appropriate provider for your platform.
+
### `zabbix_userparameters`
Manage zabbix user templates
diff --git a/lib/facter/zbx_admin_passwd_default.rb b/lib/facter/zbx_admin_passwd_default.rb
new file mode 100644
index 000000000..18e60811b
--- /dev/null
+++ b/lib/facter/zbx_admin_passwd_default.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+Facter.add(:zbx_admin_passwd_default) do
+ confine kernel: 'Linux'
+ setcode do
+ require 'zabbixapi'
+ rescue LoadError
+ nil
+ else
+ def ini_parse(file)
+ current_section = {}
+ map = {}
+ file.rewind
+ file.each_line do |line|
+ line = line.split(%r{^|\s;}).first # remove comments
+ section = line.match(%r{^\s*\[([^\[\]]+)\]\s*$}) unless line.nil?
+ if section
+ current_section = section[1]
+ elsif current_section
+ item = line.match(%r{^\s*(.+?)\s*=\s*(.+?)\s*$}) unless line.nil?
+ if item
+ map[current_section] = map[current_section] || {}
+ map[current_section][item[1]] = item[2]
+ end
+ end
+ end
+ map
+ end
+
+ def api_config
+ @api_config ||= ini_parse(File.new('/etc/zabbix/api.conf'))
+ end
+
+ begin
+ protocol = api_config['default']['apache_use_ssl'] == 'true' ? 'https' : 'http'
+ zbx_check = ZabbixApi.connect(
+ url: "#{protocol}://#{api_config['default']['zabbix_url']}/api_jsonrpc.php",
+ user: api_config['default']['zabbix_user'],
+ password: 'zabbix',
+ http_user: api_config['default']['zabbix_user'],
+ http_password: 'zabbix',
+ ignore_version: true
+ )
+ rescue ZabbixApi::ApiError
+ ret = false
+ else
+ ret = true
+ zbx_check.query(method: 'user.logout', params: {})
+ end
+ ret
+ end
+end
diff --git a/lib/puppet/provider/zabbix_authcfg/ruby.rb b/lib/puppet/provider/zabbix_authcfg/ruby.rb
new file mode 100644
index 000000000..57db0718f
--- /dev/null
+++ b/lib/puppet/provider/zabbix_authcfg/ruby.rb
@@ -0,0 +1,146 @@
+# frozen_string_literal: true
+
+require_relative '../zabbix'
+Puppet::Type.type(:zabbix_authcfg).provide(:ruby, parent: Puppet::Provider::Zabbix) do
+ desc 'Puppet provider for managing Zabbix Auth. It uses the Zabbix API to configure authentication.'
+ confine feature: :zabbixapi
+
+ def initialize(value = {})
+ super(value)
+ @property_flush = {}
+ end
+
+ def get_auth_by_id(id)
+ api_auth = zbx.query(
+ method: 'authentication.get',
+ params: {
+ output: 'extend',
+ },
+ id: id
+ )
+ if api_auth.empty?
+ nil
+ else
+ {
+ authentication_type: api_auth['authentication_type'],
+ ldap_configured: api_auth['ldap_configured'],
+ ldap_host: api_auth['ldap_host'],
+ ldap_port: api_auth['ldap_port'],
+ ldap_base_dn: api_auth['ldap_base_dn'],
+ ldap_search_attribute: api_auth['ldap_search_attribute'],
+ ldap_bind_dn: api_auth['ldap_bind_dn'],
+ ldap_case_sensitive: api_auth['ldap_case_sensitive'],
+ ldap_bind_password: api_auth['ldap_bind_password'],
+ }
+ end
+ end
+
+ def auth
+ @auth ||= get_auth_by_id(resource[:id])
+ end
+
+ attr_writer :auth
+
+ def authentication_type
+ auth[:authentication_type]
+ end
+
+ def authentication_type=(int)
+ @property_flush[:authentication_type] = int
+ end
+
+ def ldap_configured
+ auth[:ldap_configured]
+ end
+
+ def ldap_configured=(int)
+ @property_flush[:ldap_configured] = int
+ end
+
+ def ldap_host
+ auth[:ldap_host]
+ end
+
+ def ldap_host=(value)
+ @property_flush[:ldap_host] = value
+ end
+
+ def ldap_port
+ auth[:ldap_port]
+ end
+
+ def ldap_port=(value)
+ @property_flush[:ldap_port] = value
+ end
+
+ def ldap_base_dn
+ auth[:ldap_base_dn]
+ end
+
+ def ldap_base_dn=(value)
+ @property_flush[:ldap_base_dn] = value
+ end
+
+ def ldap_search_attribute
+ auth[:ldap_search_attribute]
+ end
+
+ def ldap_search_attribute=(value)
+ @property_flush[:ldap_search_attribute] = value
+ end
+
+ def ldap_bind_dn
+ auth[:ldap_bind_dn]
+ end
+
+ def ldap_bind_dn=(value)
+ @property_flush[:ldap_bind_dn] = value
+ end
+
+ def ldap_case_sensitive
+ auth[:ldap_case_sensitive]
+ end
+
+ def ldap_case_sensitive=(int)
+ @property_flush[:ldap_case_sensitive] = int
+ end
+
+ def ldap_bind_password
+ auth[:ldap_bind_password]
+ end
+
+ def ldap_bind_password=(value)
+ @property_flush[:ldap_bind_password] = value
+ end
+
+ def flush
+ # ensure => absent is unsupported
+ return if @property_flush[:ensure] == :absent
+
+ return if @property_flush.empty?
+
+ update_auth
+ end
+
+ def update_auth
+ zbx.query(
+ method: 'authentication.update',
+ id: @resource[:id],
+ params: @property_flush
+ )
+ end
+
+ # Unsupported/not needed since authentication with id: 1 is created by installing zabbix
+ def create
+ nil
+ end
+
+ def exists?
+ auth
+ end
+
+ # Unused/absent is unsupported
+ def destroy
+ nil
+ end
+end
diff --git a/lib/puppet/provider/zabbix_role/ruby.rb b/lib/puppet/provider/zabbix_role/ruby.rb
new file mode 100644
index 000000000..97f7ff839
--- /dev/null
+++ b/lib/puppet/provider/zabbix_role/ruby.rb
@@ -0,0 +1,129 @@
+# frozen_string_literal: true
+
+require_relative '../zabbix'
+Puppet::Type.type(:zabbix_role).provide(:ruby, parent: Puppet::Provider::Zabbix) do
+ desc 'Puppet provider for managing Zabbix role, It use the Zabbix API to create, read, update and delete role.'
+ confine feature: :zabbixapi
+
+ def initialize(value = {})
+ super(value)
+ @property_flush = {}
+ end
+
+ def get_id(rolename)
+ zbx.roles.get_id(name: rolename)
+ end
+
+ def get_role_by_name(name)
+ api_role = zbx.roles.get_raw(output: 'extend', selectRules: 'extend', filter: { name: name })
+ if api_role.empty?
+ nil
+ else
+ {
+ name: api_role[0]['name'],
+ type: api_role[0]['type'],
+ readonly: api_role[0]['readonly'],
+ rules: api_role[0]['rules'],
+ }
+ end
+ end
+
+ def role
+ @role ||= get_role_by_name(@resource[:name])
+ @role
+ end
+
+ attr_writer :role
+
+ def name
+ role[:name]
+ end
+
+ def name=(value)
+ @property_flush[:name] = value
+ end
+
+ def type
+ role[:type]
+ end
+
+ def type=(value)
+ @property_flush[:type] = value
+ end
+
+ def readonly
+ role[:readonly]
+ end
+
+ def readonly=(value)
+ @property_flush[:readonly] = value
+ end
+
+ def rules
+ role[:rules]
+ end
+
+ def rules=(hash)
+ @property_flush[:rules] = hash
+ end
+
+ # Defining all rules is cumbersome, this allows for defining just the needed rules, keeping all others to be the zabbix default
+ def check_rules
+ # Merge 'IS' (role[:rules]) with 'SHOULD' (resource[:rules])
+ merged = role[:rules].merge(@resource[:rules]) do |_key, oldval, newval|
+ if oldval.is_a?(Array)
+ # structure:
+ # ui => [ {"name" => "something", value => "something"} ]
+ # In case of an array we want uniqueness by hash-name with 'newval' taking precedence
+ # we also sort by hash-name to making comparing easy
+ (newval + oldval).uniq { |h| h['name'] }.sort_by { |h| h['name'] }
+ else
+ # not an array: use the new value
+ newval
+ end
+ end
+ # make sure the 'IS' (role[:rules]) is sorted to make comparing easy
+ is_sorted = role[:rules].to_h { |k, v| v.is_a?(Array) ? [k, v.sort_by { |h| h['name'] }] : [k, v] }
+ # if merged equals is_sorted it means that 'should' is contained in 'in' and no action is needed
+ # otherwise the value is either wrong or missing so action is needed
+ merged == is_sorted
+ end
+
+ def flush
+ if @property_flush[:ensure] == :absent
+ delete_role
+ return
+ end
+
+ update_role unless @property_flush.empty?
+ end
+
+ def update_role
+ zbx.query(
+ method: 'role.update',
+ params: @property_flush.merge({ roleid: get_id(@resource[:name]) })
+ )
+ end
+
+ def delete_role
+ zbx.roles.delete(get_id(@resource[:name]))
+ end
+
+ def create
+ params = {}
+ params[:name] = @resource[:name]
+ params[:type] = @resource[:type]
+ params[:readonly] = @resource[:readonly] unless @resource[:readonly].nil?
+ params[:rules] = @resource[:rules] unless @resource[:rules].nil?
+
+ zbx.roles.create(params)
+ end
+
+ def exists?
+ role
+ end
+
+ def destroy
+ @property_flush[:ensure] = :absent
+ end
+end
diff --git a/lib/puppet/provider/zabbix_user/ruby.rb b/lib/puppet/provider/zabbix_user/ruby.rb
new file mode 100644
index 000000000..6122e09dd
--- /dev/null
+++ b/lib/puppet/provider/zabbix_user/ruby.rb
@@ -0,0 +1,174 @@
+# frozen_string_literal: true
+
+require_relative '../zabbix'
+Puppet::Type.type(:zabbix_user).provide(:ruby, parent: Puppet::Provider::Zabbix) do
+ desc 'Puppet provider for managing Zabbix User. It uses the Zabbix API to create, read, update and delete user.'
+ confine feature: :zabbixapi
+
+ def initialize(value = {})
+ super(value)
+ @property_flush = {}
+ end
+
+ def get_id(username)
+ zbx.users.get_raw(filter: { username: username }, output: 'userid')[0]['userid']
+ end
+
+ def get_user_by_name(username)
+ api_user = zbx.users.get_raw(filter: { username: username }, selectUsrgrps: 'extend')
+ if api_user.empty?
+ nil
+ else
+ {
+ username: api_user[0]['username'],
+ firstname: api_user[0]['name'],
+ surname: api_user[0]['surname'],
+ autologin: api_user[0]['autologin'],
+ role: zbx.roles.get_raw(filter: { roleid: api_user[0]['roleid'] })[0]['name'],
+ usrgrps: api_user[0]['usrgrps'].map { |h| h['name'] }.compact,
+ }
+ end
+ end
+
+ def user
+ @user ||= get_user_by_name(resource[:username])
+ @user
+ end
+
+ attr_writer :user
+
+ def username
+ user[:username]
+ end
+
+ def username=(value)
+ @property_flush[:username] = value
+ end
+
+ def passwd
+ nil
+ end
+
+ def passwd=(value)
+ @property_flush[:passwd] = value
+ end
+
+ def firstname
+ user[:firstname]
+ end
+
+ def firstname=(value)
+ @property_flush[:name] = value
+ end
+
+ def surname
+ user[:surname]
+ end
+
+ def surname=(value)
+ @property_flush[:surname] = value
+ end
+
+ def autologin
+ user[:autologin]
+ end
+
+ def autologin=(int)
+ @property_flush[:autologin] = int
+ end
+
+ def role
+ user[:role]
+ end
+
+ def role=(value)
+ @property_flush[:role] = value
+ end
+
+ def usrgrps
+ user[:usrgrps]
+ end
+
+ def usrgrps=(array)
+ @property_flush[:usrgrps] = array
+ end
+
+ def flush
+ if @property_flush[:ensure] == :absent
+ delete_user
+ return
+ end
+
+ return if @property_flush.empty?
+
+ update_user
+ end
+
+ def update_user
+ # Get roleid if needs updating
+ unless @property_flush[:role].nil?
+ @property_flush[:roleid] = zbx.roles.get_id(name: @property_flush[:role])
+ @property_flush.delete(:role)
+ end
+ # Get usrgrpids if need updating
+ unless @property_flush[:usrgrps].nil?
+ usrgrp_ids = zbx.usergroups.get_raw(filter: { name: @property_flush[:usrgrps] }, output: 'usrgrpid')
+ @property_flush[:usrgrps] = usrgrp_ids
+ end
+ zbx.query(
+ method: 'user.update',
+ params: {
+ userid: get_id(@resource[:username]),
+ }.merge(@property_flush)
+ )
+ end
+
+ def delete_user
+ zbx.users.delete(get_id(@resource[:username]))
+ end
+
+ def check_password
+ protocol = api_config['default']['apache_use_ssl'] == 'true' ? 'https' : 'http'
+ begin
+ zbx_check = ZabbixApi.connect(
+ url: "#{protocol}://#{api_config['default']['zabbix_url']}/api_jsonrpc.php",
+ user: @resource[:username],
+ password: @resource[:passwd],
+ http_user: @resource[:username],
+ http_password: @resource[:passwd],
+ ignore_version: true
+ )
+ rescue ZabbixApi::ApiError
+ ret = false
+ else
+ ret = true
+ zbx_check.query(method: 'user.logout', params: {})
+ end
+ ret
+ end
+
+ def create
+ # Get role id
+ roleid = zbx.roles.get_id(name: @resource[:role])
+ # Get usrgrp ids
+ usrgrps = @resource[:usrgrps].empty? ? {} : zbx.usergroups.get_raw(filter: { name: @resource[:usrgrps] }, output: 'usrgrpid')
+
+ zbx.users.create(
+ username: @resource[:username],
+ name: @resource[:firstname],
+ surname: @resource[:surname],
+ autologin: @resource[:autologin].nil? ? 0 : @resource[:autologin],
+ roleid: roleid,
+ usrgrps: usrgrps,
+ passwd: @resource[:passwd]
+ )
+ end
+
+ def exists?
+ user
+ end
+
+ def destroy
+ @property_flush[:ensure] = :absent
+ end
+end
diff --git a/lib/puppet/provider/zabbix_usergroup/ruby.rb b/lib/puppet/provider/zabbix_usergroup/ruby.rb
new file mode 100644
index 000000000..6e3f544be
--- /dev/null
+++ b/lib/puppet/provider/zabbix_usergroup/ruby.rb
@@ -0,0 +1,100 @@
+# frozen_string_literal: true
+
+require_relative '../zabbix'
+Puppet::Type.type(:zabbix_usergroup).provide(:ruby, parent: Puppet::Provider::Zabbix) do
+ desc 'Puppet provider for managing Zabbix usergroup. It uses the Zabbix API to create, read, update and delete usergroup.'
+ confine feature: :zabbixapi
+
+ def initialize(value = {})
+ super(value)
+ @property_flush = {}
+ end
+
+ def get_usrgrp_by_name(grpname)
+ api_usrgrp = zbx.usergroups.get(name: grpname)
+ if api_usrgrp.empty?
+ nil
+ else
+ {
+ id: api_usrgrp[0]['id'],
+ name: api_usrgrp[0]['name'],
+ gui_access: api_usrgrp[0]['gui_access'],
+ debug_mode: api_usrgrp[0]['debug_mode'] ? 1 : 0,
+ users_status: api_usrgrp[0]['users_status'],
+ }
+ end
+ end
+
+ def usergroup
+ @usergroup ||= get_usrgrp_by_name(resource[:name])
+ @usergroup
+ end
+
+ attr_writer :usergroup
+
+ def gui_access
+ usergroup[:gui_access]
+ end
+
+ def gui_access=(int)
+ @property_flush[:gui_access] = int
+ end
+
+ def debug_mode
+ usergroup[:debug_mode]
+ end
+
+ def debug_mode=(int)
+ @property_flush[:debug_mode] = int
+ end
+
+ def users_status
+ usergroup[:users_status]
+ end
+
+ def users_status=(int)
+ @property_flush[:users_status] = int
+ end
+
+ def flush
+ if @property_flush[:ensure] == :absent
+ delete_usrgrp
+ return
+ end
+
+ return if @property_flush.empty?
+
+ update_usrgrp
+ end
+
+ def update_usrgrp
+ zbx.query(
+ method: 'usergroup.update',
+ params: {
+ usrgrpid: zbx.usergroups.get_id(name: @resource[:name]),
+ }.merge(@property_flush)
+ )
+ end
+
+ def delete_usrgrp
+ zbx.usergroups.delete(zbx.usergroups.get_id(name: @resource[:name]))
+ end
+
+ def create
+ params = {}
+ params[:name] = @resource[:name]
+ params[:gui_access] = @resource[:gui_access] unless @resource[:gui_access].nil?
+ params[:debug_mode] = @resource[:debug_mode] unless @resource[:debug_mode].nil?
+ params[:users_status] = @resource[:users_status] unless @resource[:users_status].nil?
+
+ zbx.usergroups.create(params)
+ end
+
+ def exists?
+ usergroup
+ end
+
+ def destroy
+ @property_flush[:ensure] = :absent
+ end
+end
diff --git a/lib/puppet/type/zabbix_authcfg.rb b/lib/puppet/type/zabbix_authcfg.rb
new file mode 100644
index 000000000..ac5f7d2f8
--- /dev/null
+++ b/lib/puppet/type/zabbix_authcfg.rb
@@ -0,0 +1,106 @@
+# frozen_string_literal: true
+
+require 'uri'
+
+Puppet::Type.newtype(:zabbix_authcfg) do
+ @doc = %q("Manage zabbix authentication configuration
+
+ zabbix_authcfg { '1':
+ ensure => present,
+ ldap_configured => true,
+ ldap_host => 'ldap://host.name',
+ ldap_port => '389',
+ ldap_base_dn => 'dc=example,dc=com',
+ ldap_search_attribute => 'uid',
+ ldap_bind_dn => 'cn=Manager',
+ ldap_bind_password => Sensitive('password'),
+ ldap_case_sensitive => true,
+ }")
+
+ ensurable do
+ # We should not be able to delete the authentication settings
+ desc 'The basic property that the resource should be in.'
+ newvalues(:present)
+ defaultto :present
+ end
+
+ newparam(:id, namevar: true) do
+ # Zabbix 5, 6 only support updating the default authentication settings (id: 1)
+ desc 'authentication settings id'
+ newvalues(1)
+ end
+
+ newproperty(:authentication_type) do
+ desc 'authentication type'
+ newvalues('internal', 'LDAP')
+ defaultto 'internal'
+ munge do |value|
+ value == 'internal' ? 0 : 1
+ end
+ end
+
+ newproperty(:ldap_configured, boolean: true) do
+ desc 'Enable LDAP authentication'
+ newvalues(true, false)
+ defaultto true
+ munge do |value|
+ value ? 1 : 0
+ end
+ end
+
+ newproperty(:ldap_host) do
+ desc 'LDAP host'
+ validate do |value|
+ raise ArgumentError, "ldap_host must be a valid ldap uri, \"#{value}\" is not" unless value =~ URI::DEFAULT_PARSER.make_regexp
+ end
+ end
+
+ newproperty(:ldap_port) do
+ desc 'LDAP port'
+ validate do |value|
+ raise ArgumentError, "ldap_port must be an Integer, not #{value}" unless value.is_a?(Integer)
+ end
+ end
+
+ newproperty(:ldap_base_dn) do
+ desc 'LDAP base DN'
+ validate do |value|
+ raise ArgumentError, "ldap_base_dn must be a valid DN, not #{value}" unless %r{^((dc|ou|cn)=.+,*)+$}i.match?(value)
+ end
+ end
+
+ newproperty(:ldap_search_attribute) do
+ desc 'LDAP search attribute'
+ newvalues('uid', 'sAMAccountName')
+ end
+
+ newproperty(:ldap_bind_dn) do
+ desc 'LDAP bind DN'
+ validate do |value|
+ raise ArgumentError, "ldap_bind_dn must be a valid DN, not #{value}" unless %r{^((dc|ou|cn)=.+,*)+$}i.match?(value)
+ end
+ end
+
+ newproperty(:ldap_case_sensitive, boolean: true) do
+ desc 'LDAP case sensitive login'
+ newvalues(true, false)
+ defaultto true
+ munge do |value|
+ value ? 1 : 0
+ end
+ end
+
+ newproperty(:ldap_bind_password) do
+ desc 'LDAP bind password'
+ validate do |value|
+ raise ArgumentError, "ldap_bind_password must be an String, not #{value}" unless value.is_a?(String)
+ end
+ end
+
+ def set_sensitive_parameters(sensitive_parameters) # rubocop:disable Naming/AccessorMethodName
+ parameter(:ldap_bind_password).sensitive = true if parameter(:ldap_bind_password)
+ super(sensitive_parameters)
+ end
+
+ autorequire(:file) { '/etc/zabbix/api.conf' }
+end
diff --git a/lib/puppet/type/zabbix_role.rb b/lib/puppet/type/zabbix_role.rb
new file mode 100644
index 000000000..c24f49a8c
--- /dev/null
+++ b/lib/puppet/type/zabbix_role.rb
@@ -0,0 +1,96 @@
+# frozen_string_literal: true
+
+Puppet::Type.newtype(:zabbix_role) do
+ @doc = %q("Manage zabbix role objects
+
+ zabbix_role { 'My custom role':
+ ensure => present,
+ type => 'Admin',
+ readonly => false,
+ rules => {
+ 'ui'=> [
+ {
+ 'name' => 'configuration.actions',
+ 'status' => '1'
+ },
+ {
+ 'name' => 'configuration.discovery',
+ 'status'=>'0'
+ },
+ ],
+ 'ui.default_access' => '1',
+ 'services.read.mode' => '1',
+ 'services.write.mode' => '0',
+ 'modules.default_access' => '0',
+ 'api.access' => '0',
+ }
+ }")
+
+ ensurable do
+ desc 'The basic property that the resource should be in.'
+ defaultvalues
+ defaultto :present
+ end
+
+ newparam(:name, namevar: true) do
+ desc 'the name of the role'
+ validate do |value|
+ raise ArgumentError, "role must be a valid string, \"#{value}\" is not" unless value.is_a?(String)
+ end
+ end
+
+ newproperty(:type) do
+ desc 'role type'
+ newvalues('User', 'Admin', 'Super admin')
+ defaultto 'User'
+
+ type_values = {
+ 'User' => 1,
+ 'Admin' => 2,
+ 'Super admin' => 3,
+ }
+
+ munge do |value|
+ type_values[value]
+ end
+ end
+
+ newproperty(:readonly, boolean: true) do
+ desc 'Whether the role is readonly'
+ newvalues(true, false)
+ defaultto false
+ munge do |value|
+ value ? 1 : 0
+ end
+ end
+
+ newproperty(:rules) do
+ desc 'The role rules'
+
+ def insync?(_is)
+ provider.check_rules
+ end
+
+ def change_to_s(currentvalue, newvalue)
+ md5_cur = Digest::MD5.hexdigest currentvalue.to_s
+ md5_new = Digest::MD5.hexdigest newvalue.to_s
+ Puppet.debug("OLD rules: #{currentvalue}")
+ Puppet.debug("NEW rules: #{newvalue}")
+ "changed #{md5_cur} to #{md5_new}"
+ end
+
+ def is_to_s(currentvalue)
+ Digest::MD5.hexdigest currentvalue.to_s
+ end
+
+ def should_to_s(newvalue)
+ Digest::MD5.hexdigest newvalue.to_s
+ end
+
+ validate do |value|
+ raise ArgumentError, 'rules must be a hash' unless value.is_a?(Hash)
+ end
+ end
+
+ autorequire(:file) { '/etc/zabbix/api.conf' }
+end
diff --git a/lib/puppet/type/zabbix_user.rb b/lib/puppet/type/zabbix_user.rb
new file mode 100644
index 000000000..f687d645f
--- /dev/null
+++ b/lib/puppet/type/zabbix_user.rb
@@ -0,0 +1,89 @@
+# frozen_string_literal: true
+
+Puppet::Type.newtype(:zabbix_user) do
+ @doc = %q("Manage zabbix users
+
+ zabbix_user{ 'username':
+ ensure => present,
+ firstname => 'firstname',
+ surname => 'surname',
+ passwd => Sensitive(password),
+ autologin => 0,
+ role => 'Admin role',
+ usrgrps => [ 'Group1' ],
+ }")
+
+ ensurable do
+ desc 'The basic property that the resource should be in.'
+ defaultvalues
+ defaultto :present
+ end
+
+ newparam(:username, namevar: true) do
+ desc 'user name'
+ validate do |value|
+ raise ArgumentError, 'username must be a string' unless value.is_a?(String)
+ end
+ end
+
+ newproperty(:firstname) do
+ desc 'user firstname'
+ validate do |value|
+ raise ArgumentError, 'firstname must be a string' unless value.is_a?(String)
+ end
+ end
+
+ newproperty(:surname) do
+ desc 'user surname'
+ validate do |value|
+ raise ArgumentError, 'surname must be a string' unless value.is_a?(String)
+ end
+ end
+
+ newproperty(:passwd) do
+ desc 'user password'
+ def insync?(_is)
+ provider.check_password
+ end
+ end
+
+ newproperty(:autologin, boolean: true) do
+ desc 'Whether auto login is enabled or disabled.'
+ newvalues(true, false)
+ defaultto false
+
+ munge do |value|
+ value ? 1 : 0
+ end
+ end
+
+ newproperty(:role) do
+ desc 'user role'
+ defaultto 'User role'
+ validate do |value|
+ raise ArgumentError, 'User role must be a string' unless value.nil? || value.is_a?(String)
+ end
+ end
+
+ newproperty(:usrgrps, array_matching: :all) do
+ desc 'user groups'
+ defaultto []
+ def insync?(is)
+ is.sort == should.sort
+ end
+ end
+
+ def set_sensitive_parameters(sensitive_parameters) # rubocop:disable Naming/AccessorMethodName
+ parameter(:passwd).sensitive = true if parameter(:passwd)
+ super(sensitive_parameters)
+ end
+
+ validate do
+ required_properties = %i[firstname surname usrgrps]
+ required_properties.each do |property|
+ raise ArgumentError, "You must provide a #{property}" if self[:ensure] == :present && self[property].nil?
+ end
+ end
+
+ autorequire(:file) { '/etc/zabbix/api.conf' }
+end
diff --git a/lib/puppet/type/zabbix_usergroup.rb b/lib/puppet/type/zabbix_usergroup.rb
new file mode 100644
index 000000000..45ce011de
--- /dev/null
+++ b/lib/puppet/type/zabbix_usergroup.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+Puppet::Type.newtype(:zabbix_usergroup) do
+ @doc = %q("Manage zabbix usergroups
+
+ zabbix_usergroup{ 'group1':
+ ensure => present,
+ gui_access => 2,
+ debug_mode => true,
+ users_status => true,
+ }")
+
+ ensurable do
+ desc 'The basic property that the resource should be in.'
+ defaultvalues
+ defaultto :present
+ end
+
+ newparam(:name, namevar: true) do
+ desc 'usergroup name'
+ end
+
+ newproperty(:gui_access) do
+ desc 'The type of access for this group.'
+ newvalues('default', 'internal', 'LDAP', 'none')
+ defaultto 'default'
+
+ gui_values = {
+ 'default' => 0,
+ 'internal' => 1,
+ 'LDAP' => 2,
+ 'none' => 3
+ }
+
+ munge do |value|
+ gui_values[value]
+ end
+ end
+
+ newproperty(:debug_mode, boolean: true) do
+ desc 'Whether debug mode is enabled or disabled.'
+ newvalues(true, false)
+ defaultto false
+
+ munge do |value|
+ value ? 1 : 0
+ end
+ end
+
+ newproperty(:users_status, boolean: true) do
+ desc 'Whether the user group is enabled or disabled.'
+ newvalues(true, false)
+ defaultto true
+
+ munge do |value|
+ # 0 = enabled in zabbix api...
+ value ? 0 : 1
+ end
+ end
+
+ autorequire(:file) { '/etc/zabbix/api.conf' }
+end