diff --git a/README.md b/README.md index ffe9dc3ba..089b00ff6 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) @@ -365,6 +369,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/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..46994b256 --- /dev/null +++ b/lib/puppet/provider/zabbix_authcfg/ruby.rb @@ -0,0 +1,145 @@ +# frozen_string_literal: true + +require_relative '../zabbix' +Puppet::Type.type(:zabbix_authcfg).provide(:ruby, parent: Puppet::Provider::Zabbix) do + 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..c9784c880 --- /dev/null +++ b/lib/puppet/provider/zabbix_role/ruby.rb @@ -0,0 +1,128 @@ +# frozen_string_literal: true + +require_relative '../zabbix' +Puppet::Type.type(:zabbix_role).provide(:ruby, parent: Puppet::Provider::Zabbix) do + 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].map { |k, v| v.is_a?(Array) ? [k, v.sort_by { |h| h['name'] }] : [k, v] }.to_h + # 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..99f566980 --- /dev/null +++ b/lib/puppet/provider/zabbix_user/ruby.rb @@ -0,0 +1,173 @@ +# frozen_string_literal: true + +require_relative '../zabbix' +Puppet::Type.type(:zabbix_user).provide(:ruby, parent: Puppet::Provider::Zabbix) do + 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..9ac03dc89 --- /dev/null +++ b/lib/puppet/provider/zabbix_usergroup/ruby.rb @@ -0,0 +1,99 @@ +# frozen_string_literal: true + +require_relative '../zabbix' +Puppet::Type.type(:zabbix_usergroup).provide(:ruby, parent: Puppet::Provider::Zabbix) do + 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..92380c0f3 --- /dev/null +++ b/lib/puppet/type/zabbix_authcfg.rb @@ -0,0 +1,105 @@ +# 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 + 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..9f7b0f7da --- /dev/null +++ b/lib/puppet/type/zabbix_role.rb @@ -0,0 +1,95 @@ +# 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 + 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..92ac821c3 --- /dev/null +++ b/lib/puppet/type/zabbix_user.rb @@ -0,0 +1,88 @@ +# 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 + 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..fc05a5787 --- /dev/null +++ b/lib/puppet/type/zabbix_usergroup.rb @@ -0,0 +1,61 @@ +# 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 + 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