Puppet: Adding multiple AD users to local group - puppet

I am trying to add multiple AD users on a Windows Server 2012 to the Administrators group, but it's throwing an error. If I specify only a single user in the params.pp file, then it works fine.
params.pp
$user_to_add = [
'ad8\iisuser',
'ad8\dbuser',
],
$group_name = 'Administrators',
add_user_to_local_group.pp
class common::add_user_to_local_group (
$user_to_add = $common::params::user_to_add,
$group_name = $common::params::group_name,
) inherits common::params {
$user_to_add.each |$user_name| {
group { "Add $user_name to local group":
ensure => present,
name => $group_name,
members => [ $user_name ],
}
}
}
Error:
Error: Could not retrieve catalog from remote server: Error 500 on SERVER: {"message":"Server Error: Evaluation Error: Error while eval
uating a Resource Statement, Cannot alias Group[Add ad8\\dbuser to local group] to [\"Administrators\"] at /etc/puppetlabs/code/en
vironments/automation/modules/common/manifests/add_user_to_local_group.pp:6; resource [\"Group\", \"Administrators\"] already declared
at /etc/puppetlabs/code/environments/automation/modules/common/manifests/add_user_to_local_group.pp:6 at /etc/puppetlabs/code/environme
nts/automation/modules/common/manifests/add_user_to_local_group.pp:6:9 on node lab.ad8.com","issue_kind":"RUNTIME_ERROR","stacktrace
":["Warning: The 'stacktrace' property is deprecated and will be removed in a future version of Puppet. For security reasons, stacktrac
es are not returned with Puppet HTTP Error responses."]}

You are trying to circumvent resource uniqueness/multiple declarations by providing a different title for the two resources, but resources must also have unique namevars https://docs.puppet.com/puppet/4.9/lang_resources.html#namenamevar. The namevar for the group resource is name, which is aliased from the title if not specified in the attributes (hence the error message output being what it is) https://docs.puppet.com/puppet/latest/type.html#group-attribute-name.
Thus, when you declare two resources for
group { "Add $user_name to local group":
ensure => present,
name => $group_name,
members => [ $user_name ],
}
with the same name attribute like you are doing when you iterate over the hash (since $group_name is the same for both), then you will have a multiple declaration error thrown. This is also why it works for you when you specify only a single user, since you then have namevar uniqueness.
To fix this, you need to have only one group resource that adds both users simultaneously instead of sequentially.
class common::add_user_to_local_group (
$user_to_add = $common::params::user_to_add,
$group_name = $common::params::group_name,
) inherits common::params {
group { $group_name:
ensure => present,
members => $user_to_add,
}
}
I would also recommend pluralizing the use of the word 'user' for clarification ($user_to_add --> $users_to_add). Another improvement may be to allow passing in multiple groups and iterating over those with an associated member hash, but you can decide that for yourself.

Related

Assign variable from hiera as username (with uid and gid) inside puppet class

Hello Community members,
We have a puppet file which has a main class like below:
class profile::Profname::application::classname {
$appname_homedir = hiera('appname::appname_homedir')
$appname_installdir = hiera('appname::appname_installdir')
$java_optional_args = "${java_keystore_args} ${java_proxy_args} ${java_timezone_args}"
..
..
$user = hiera('appname::appname_user')
$gid = hiera('appname::appname_user_gid')
$uid = hiera('appname::appname_user_uid')
exec { "Many blocks":
command => "commands",
..
}
exec { "Many blocks":
command => "commands",
..
}
# Install and configure app
class { 'app':
user => hiera('classname::appname_user'),
jvm_opts => $java_optional_args,
download_url => "s3://${s3_bucket}/${APPDIR}/appname",
dbserver => hiera('appname::db-endpoint'),
notify => Exec["Change appname gid and uid"],
require => [ Class['java'], Exec['Create Homedir'], Exec['Create Appdir']],
}
#other exec blocks
exec { "blocks"
..
..
}
}
Now I have two more variables in hiera those are: uid and gid for the appname_user.
How can I create the user "appname_user" in the inner class 'app'?
I was trying to refer https://puppet.com/docs/puppet/4.10/quick_start_user_group.html but not sure how can I do this inside the inner class. Any help would be greatly appreciated.
I did another exec block of changing the uid and gid for the user later when user is created but that way does not seem to be working for some files already present. Though puppet execution is successful. Hence I want to create the user with uid, and gid at the time of creation.
Thanks and Regards,
Saha
How can I create the user "appname_user" in the inner class 'app'?
My best guess is that you mean within this construct:
class { 'app':
user => hiera('classname::appname_user'),
jvm_opts => $java_optional_args,
download_url => "s3://${s3_bucket}/${APPDIR}/appname",
dbserver => hiera('appname::db-endpoint'),
notify => Exec["Change appname gid and uid"],
require => [ Class['java'], Exec['Create Homedir'], Exec['Create Appdir']],
}
The answer to that is simple: you can't. But there is a variety of related things you could do.
First things first: "inner class" is not a term used in Puppet. There can be nested classes, but
that's not what you have presented, and
nesting classes is widely regarded as poor style.
What you have shown is a declaration of class app inside the definition of class profile::Profname::application::classname. A class declaration, when evaluated, tells Puppet to include the designated class in the catalog then under construction, and it optionally binds values to some that class's parameters. There must be a definition of the class elsewhere to tell Puppet what that means. If you want it to declare a resource that it does not already declare, then it is the definition that must be modified.
You would typically manage a user by declaring a User resource (inside one class definition or another). In your case, you could find the definition of class app and put it there, but you could also put it in your profile class, alongside the existing declaration of class app. Which one would serve you best depends on what you're trying to accomplish.

Iterate a puppet resource collector

I am trying to develop a puppet class with a defined resource which creates the configuration for a website.
One of the things that the defined resource has to do is assign the IP address of the website to a dummy interface. Due to constraints of the project this is done with NetworkManager.
So I have to generate a file like
[connection]
id=dummydsr
uuid=50819d31-8967-4321-aa34-383f4a658789
type=dummy
interface-name=dummydsr
permissions=
[ipv4]
method=manual
#IP Addresses come here
ipaddress1=1.2.3.4/32
ipaddress2=5.6.7.8/32
ipaddress3=8.7.6.5/32
[ipv6]
method=ignore
There is to be a line ipaddressX=... for every instance of the defined resource.
My problem is how do I track the number of times the defined resource has been instantiated so I can somehow increment a counter and generate the ipaddress lines.
Or for each instantiated defined resource, append the IP address to an array which I can later use to build the file
If I understand you, and I'm not certain that I do, but I think you would want to do something like this:
define mytype(
Integer $count,
...
) {
file { 'some_network_manager_file':
content => template(...)
}
}
And then you would have a loop:
$mystuff.each |$count, $data| {
mytype { ...:
count => $count,
...
}
}
Key insight here may be that the each function has some magic in it that allows you to get the index if you need it, see also this answer.
Now I think that's how it will work, without me spending time researching NetworkManager. If you provide more of your code, I may be able to update this to be more helpful.
This is less than ideal since I would prefer to have it inside the defined resource, but since I instantiate the defined resource with the data from a hash I use said hash to iterate that part.
class xxx_corp_webserver (
Hash $websites ={}
){
create_resources('xxx_corp_webserver::website', $websites)
# This would be nicer inside the defined class, but I did not find any other way
# Build and array with the IP addresses which are for DSR
$ipaddresses = $websites.map | $r | {
if $r[1]['enabledsr'] {
$r[1]['ipaddress']
}
}
# For each DSR address add the line
$ipaddresses.each | Integer $index , String $ipaddress | {
$num = $index+1
file_line{"dummydsr-ipaddress${num}":
ensure => present,
path => '/etc/NetworkManager/system-connections/dummydsr',
line => "address${num} = ${ipaddress}/32",
match => "^address.* = ${ipaddress}/32",
after => '# IP Addresses come here',
notify => Service['NetworkManager'],
require => File['/etc/NetworkManager/system-connections/dummydsr'],
}
}
}

Iterate over a Puppet Hiera hash in a puppet manifest

Update 1
I've changed the structure of the Hiera data a little and trying a different manifest style.
I'm trying to iterate over the following Hiera hash in a Puppet manifest:
windows-10.yaml:
---
message: "This node is using Windows 10 data"
#profile::hiera_test::backups_enabled: false
profile::hiera_test::firewall:
rule1:
groupName: 'Cortana'
profileNames: ['Private','Public']
action: 'Deny'
rule2:
groupName: 'Microsoft Photos'
profileNames: 'Public'
action: 'Deny'
Although I've updated the data structure and puppet lookup... returns what appears to be valid, I'm not entirely confident in the structure.
I have tried multiple permutations of the manifest. The latest of which looks like the following (based on this answer given by Matt Schuchard):
hiera_test.pp:
class profile::hiera_test (
Hash $data = lookup('profile::hiera_test::firewall', "merge" => 'hash'),
){
$data.each | String $key, Hash $value = {}|{
notify {
default:
name => "Demo_${key}",
message => 'Item DEFAULT',
;
$key:
* => $value,
}
}
}
And the error / output from the above:
PS C:\Users\LocalAdmin> puppet agent -t
Notice: Local environment: 'production' doesn't match server specified node environment 'development', switching agent to 'development'.
Info: Retrieving pluginfacts
Info: Retrieving plugin
Info: Retrieving locales
Info: Loading facts
Error: Could not retrieve catalog from remote server: Error 500 on SERVER: Server Error: no parameter named 'groupName' (file: /etc/puppetlabs/code/environments/development/site-modules/profile/manifests/hiera_test.pp, line: 31) on Notify[rule1] (file: /etc/puppetlabs/code/environments/development/site-modules/profile/manifests/hiera_test.pp, line: 31) on node winnode1.domain.com
Warning: Not using cache on failed catalog
Error: Could not retrieve catalog; skipping run
Ideally, I want it to work inside a class declaration (why?, because that's about as far as my Puppet learning has got, but happy to further my learning. I'm also using Puppet Enterprise (2019.0.2)).
There are several similar questions around the internet, but they are either out of date (Hiera <5), have incomplete examples including this, or I can't work out how to transpose them into what I need. Apparently create_resources is due for depreciation
?
If anyone can tell me where I'm going wrong that would be great.
I have got a working solution, but hopefully someone can chime in with a better way.
With the example in the question, the line * => $value substitutes * with keys from the Hiera hash. These keys are used as parameters of the notify resource and as per the error, there is no such parameter groupName for notify. I'd either have to change the keys in my Hiera data to match the parameters of notify which wouldn't make much sense or learn how to use the Hiera data keys as parameters...
Anyway, the Hiera data is the same as in the question, but the class is as follows:
class profile::hiera_test (
Hash $data = lookup('profile::hiera_test::firewall', "merge" => 'hash'),
){
$data.each | String $key, Hash $value = {}|{
notify {
default:
name => "Demo_${key}",
message => 'Item DEFAULT',
;
$key:
name => "Demo_${key}",
message => "Output: GroupName: ${value['groupName']}, Profile Names: ${value['profileNames']}, Action: ${value['action']}",
}
}
}
As you can see, I've replaced * with valid parameters of the notify resource.
And the output:
Notice: Output: GroupName: Cortana, Profile Names: [Private, Public], Action: Deny
Notice: Output: GroupName: Microsoft Photos, Profile Names: Public, Action: Deny
Now to replace Notify with the real exec resource. Converting the profileNames array to PowerShell will be fun, I have no doubt.

Puppet tries to rename sites-avaialbe folder

I am running Puppet on staging server, for some reasons puppet starts trying removing sites-available folder and I have no idea why. Any hint will be helpful.
Error: Could not set 'file' on ensure: Is a directory # rb_file_s_rename - (/etc/nginx/sites-available20180808-12536-11p54v, /etc/nginx/sites-available) at 12:/etc/puppet/modules/nginx/manifests/vhost.pp
Error: Could not set 'file' on ensure: Is a directory # rb_file_s_rename - (/etc/nginx/sites-available20180808-12536-11p54v, /etc/nginx/sites-available) at 12:/etc/puppet/modules/nginx/manifests/vhost.pp
Code:
define nginx::vhost($docroot, $port = 80, $template = 'nginx/vhost_php.erb', $allow = [], $deny = [], $aliases = [])
{
include nginx
file { "/etc/nginx/sites-available/${name}":
owner => 'root',
group => 'root',
mode => 644,
content => template($template),
require => Package['nginx'],
notify => Service['nginx'],
}
file { "/etc/nginx/sites-enabled/${name}":
ensure => 'link',
target => "/etc/nginx/sites-available/${name}",
require => File["/etc/nginx/sites-available/${name}"],
notify => Service['nginx'],
}
}
As #MattSchuchard remarked in comments, the error messages show that Puppet thinks you've asked it to convert a directory into a file. Furthermore, it appears to be associating that action with the first File resource in your manifest, which declares
file { "/etc/nginx/sites-available/${name}":
# ...
}
You will note that that resource appears to be trying to manage a file inside the directory, rather than the directory itself, but that discrepancy would be resolved if the automagic $name variable happened to take an empty string as its value. That's what I presume is happening.
You don't show the relevant declaration(s) of the nginx::vhost resources, but I think you'll find that the problem is there. The (slightly) broader context of those error messages would probably confirm this diagnosis: it normally contains a path-like specification of the resource in which the error occurred, and that would include the relevant resource title.

Puppet; Call another .pp

So I am using the https://forge.puppetlabs.com/pdxcat/nrpe module to try to figure out automation of NRPE across hosts.
One of the available usages is
nrpe::command {
'check_users':
ensure => present,
command => 'check_users -w 5 -c 10';
}
Is there anyway to make a "group" of these commands and have them called on specific nodes?
For example:
you have 5 different nrpe:command each defining a different check, and then call those specific checks?
I am basically trying to figure out if I could group certain checks/commands together instead of setting up a ton of text in the main sites.pp file. This would also allow for customized templates/configurations across numerous nodes.
Thanks!
EDIT:
This is the command and what it's supposed to do when called on with the 'check_users' portion. If I could have a class with a set of "nrpe:command" and just call on that class THROUGH the module, it should work. Sorry, though. Still new at puppet. Thanks again.
define nrpe::command (
$command,
$ensure = present,
$include_dir = $nrpe::params::nrpe_include_dir,
$libdir = $nrpe::params::libdir,
$package_name = $nrpe::params::nrpe_packages,
$service_name = $nrpe::params::nrpe_service,
$file_group = $nrpe::params::nrpe_files_group,
) {
file { "${include_dir}/${title}.cfg":
ensure => $ensure,
content => template('nrpe/command.cfg.erb'),
owner => root,
group => $file_group,
mode => '0644',
require => Package[$package_name],
notify => Service[$service_name],
}
}
What version are you talking about? In puppet latest versions, inheritance is deprecated, then you shouldn't use it.
The easiest way would be to use "baselines".
Assuming you are using a manifests directory (manifest = $confdir/manifests inside your puppet.conf), simply create a $confdir/manifests/minimal.pp (or $confdir/manifests/nrpe_config.pp or whatever class name you want to use) with the content below:
class minimal {
nrpe::command { 'check_users':
ensure => present,
command => 'check_users -w 5 -c 10',
}
}
Then just call this class inside your node definitions (let's say in $confdir/manifests/my_node.pp) :
node 'my_node.foo.bar' {
include minimal
}

Resources