Administrate Windows local user groups in Puppet

Windows local user groups in Puppet

 

I recently had a challenge where I needed to add an item to an existing array in Puppet, after some searching on internet I didn’t find a proper native solution to add it. On some Stackoverflow post I saw a mention to join/split the array and create it this way. The example wasn’t that clear so I try to make it a bit more readable.
In the end a very simple solution but it took me a few tries to get it right.

Use case: 

I want to manage the Windows local user groups with Puppet, currently for example the “administrators” group has a few local user accounts (LocUserX) and a few domain group accounts (DomGroupX). This is a specific set of users that doesn’t change and is our base.

For each server we want to add a specific variable domain user group (DomGroupServer1).

Plan:

To manage groups in Puppet we can use the Group resource type. Let’s have a look how the group resource is handled now.
Let’s start on a Windows server where Puppet is installed, open a command prompt and use “puppet resource group” to list all current groups and settings.


PS C:\WINDOWS\system32> puppet resource group
group { 'Administrators':
ensure => 'present',
gid => 'S2342315-1235',
members => ['administrator', 'Domain Admins', 'LocUser1', 'DomGroup1', 'DomGroup2', 'DomGroup3'],
}

That’s the information we need.

Now we now how the information is interpreted and displayed. Use the Group to see if anything extra is needed for your environment

Building:

Setting the backend

In our environment we use Hiera as backend, so this is where we store the ‘static’ data. So first I create some entries in the yaml file to use in the new Puppet profile.


local_groups:
- administrators:
name: 'Administrators'
ensure: 'present'
members: ['administrator', 'Domain Admins', 'LocUser1', 'DomGroup1', 'DomGroup2', 'DomGroup3']
auth_membership: true

As you may notice I also added the last line “auth_membership” this option will enforce the group settings as described. So  accounts not mentioned to be in the group will be removed. If you leave it off only users are added, but nothing is done with any existing users in the group.

Create the profile

Because Puppet runs on Linux and Windows I started with a case to simply only apply this to windows machines, else give a warning message.

I added some comments to make it clearer what happens where. By using a for each loop, we can simply add more groups in our Hiera backend and the profile will loop through it.


class profile::windows::local_groups{

case $::operatingsystem { #Case switch to only apply on Windows
'windows': {

$local_groups = hiera('local_groups',undef) #Get local groups from Hiera

$local_groups.each|$local_group|{ #For each group in local_groups do apply group

group { $local_group['name']: #make use of the group resource type
ensure => $local_group['ensure'], #use the proper value from Hiera
members => $local_group['members'], #use the proper value from Hiera
auth_membership => $local_group['auth_membership'], #use the proper value from Hiera
}

}

}

default: { #If operatingsystem is not windows give an error and do nothing
warning("This operating system is NOT supported by ${module_name}")
}
}

So this this is the basic start, now I want to add an item to the $local_group[‘name’].

As mentioned before this can be done by a join/split construction. First I start to put all the members in a seperate variable:


$members = $local_group['members']  #now contains : ['administrator', 'Domain Admins', 'LocUser1', 'DomGroup1', 'DomGroup2', 'DomGroup3']

#So let's create a string from this array, in a new variable

$local_admin_group = join($members,',') #this will create a string from $members with a comma as separator. I used space at the start, but this will cut domain admins also in half. So I switched to comma.

# Now let's add the new variable to the string

$new_local_admin_group = "${local_admin_group},DomGroupServer1"

#This will create a string containing: administrator,Domain Admins,LocUser1, DomGroup1, DomGroup2,DomGroup3,DomGroupServer1

#Now we need to rebuild the array for further processing

$final_local_admin_group = split($new_local_admin_group,',')

#This will create the new array ['administrator', 'Domain Admins', 'LocUser1', 'DomGroup1', 'DomGroup2', 'DomGroup3','DomGroupServer1<strong>'</strong>]

#Now apply this to our group reference

group { $local_group['name']:
ensure =&gt; $local_group['ensure'],
members =&gt; $final_local_admin_group,
auth_membership =&gt; $local_group['auth_membership'],
}

 

Now we put all things together:


### Class: profile::windows::local_groups
#
# == Description :
# This profile manages the local groups of a Windows system
#
# === Supported Operating Systems
# * Windows 2012 R2

class profile::windows::local_groups{

case $::operatingsystem {
'windows': {

#Get local group from Hiera
$local_groups = hiera('local_groups',undef)

#Loop trough all groups and if the group is administrators, and additional
#adminstator group for environments is added
$local_groups.each|$local_group|{
if $local_group['name'] == 'administrators'{
$members = $local_group['members']
#Create a new string splitted by comma(beacuse domain admins has a
# space in the name)
$local_admin_group = join($members,',')
#Add the environment group to the string
$new_local_admin_group = "${local_admin_group},DomGroupServer1"
#Recreate an array from the new_local_admin_group string
$final_local_admin_group = split($new_local_admin_group,',')
group { $local_group['name']:
ensure => $local_group['ensure'],
members => $final_local_admin_group,
auth_membership => $local_group['auth_membership'],
}
}
else {

#If the group doesn't match the administrators group the standard
#groups are applied.
group { $local_group['name']:
ensure => $local_group['ensure'],
members => $local_group['members'],
auth_membership => $local_group['auth_membership'],
}
}
}
}
default: {
warning("This operating system is NOT supported by ${module_name}")
}
}
}

Summary for adding item to array


$members = ['administrator', 'Domain Admins', 'LocUser1', 'DomGroup1']

$local_admin_group = join($members,',')

$new_local_admin_group = "${local_admin_group},DomGroupServer1"

$final_local_admin_group = split($new_local_admin_group,',')

Hope this can help with someones issue, took me a few hits before I got it working properly. Could be my experience at the moment, but hey you gotta learn sometime 🙂 Probably other people are learning too..

 

Logging Function v2.0

As I created an earlier post with a simple logging function in poweshell, a while ago I upgraded this with a specific Error, Success and info switch. This way I can put it with simple colors as output on screen to see if something goes wrong or good. And also logging will be better searchable.

 

$Logfile = "D:\LogFile.txt"

Function LogWrite{
&lt;#
<span style="font-size: 0.95em; line-height: 1.6em;">.SYNOPSIS This functions is used to generate a log file
</span><span style="font-size: 0.95em; line-height: 1.6em;">.DESCRIPTION Function creates output on screen depending on given switch and writes this with error code to the logfile
.PARAMETER $Logstring Location to the logfile
.PARAMETER $Error Switch to identify an error message
.PARAMETER $Success Switch to identify a success message
.PARAMETER $Info Switch to identify an info message
.EXAMPLE PS C:\&gt; Logwrite -error "This is an error"
</span> .INPUTS
System.String,System.Switch
.OUTPUTS
System.String
#&gt;

[CmdletBinding()]
[OutputType([System.String])]
Param(
[string]$Logstring,
[switch]$Error,
[switch]$Success,
[switch]$Info

)
try {
if ($Error){
$logstring = (Get-Date).ToString() + " ERROR: " + $logstring
Write-Host -ForegroundColor red $logstring
}
elseif ($Success){
$logstring = (Get-Date).ToString() + " SUCCESS: " + $logstring
Write-Host -ForegroundColor green $logstring
}
elseif ($Info){
$logstring = (Get-Date).ToString() + " INFO: " + $logstring
Write-Host $logstring
}
else {
$logstring = (Get-Date).ToString() + " INFO: " + $logstring
Write-Host $logstring
}
Add-content $Logfile -value $logstring
}
catch {
throw
}
}

#Example

logwrite -success "Success creating user: $user"
logwrite -error "Error creating user $user"
logwrite -info"Success quering user: $user"

vCenter 6 creating global roles with PowerCLI

While middle in the migration from a vCenter 5.1 environment to a vCenter 6.x environment I wanted to use the Global Roles so I don’t have to set them per vCenter anymore.

So how do I create those global roles?

Well the important thing is to connect to your vCenter (Connect-VIServer) using the administrator@vsphere.local user (or your SSO user if you configured a different one)

Because you login with the SSO user you can create the global roles by just using the New-VIRole command.

Example:
So in with the function below I tried to create a simple function with parameters -From and -To to simply recreate the roles from vCenter1 to vCenter2.
I make use of the logwrite function I posted earlier to spam some messages on screen and to a text file

Before:
– I expect you to be connected to both vCenters using the Connect-VIServer cmdlet.

function Migrate-VIrole{
	<#
		.SYNOPSIS
			Migrates the VCenter roles from one vCenter to another
		.DESCRIPTION
			A detailed description of the function.
		.PARAMETER  $From
			This is the vCenter to read from
		.PARAMETER  $To
			This is the vCenter to build the datacenter on
		.EXAMPLE
			PS C:\> Migrate-VIRole -From vCenter1 -To vCenter2
		.INPUTS
			System.String
		.OUTPUTS
			System.String
	#>
	[CmdletBinding()]
	[OutputType([System.String])]
	param(
		[Parameter(Position=1, Mandatory=$true)]
		[ValidateNotNull()]
		[System.String]
		$From,
		[Parameter(Position=2, Mandatory=$true)]
		[ValidateNotNull()]
		[System.String]
		$To
	)
	try{
	#Grabbing roles from an to in array
	$ArrRolesFrom = Get-VIRole -Server $From |?{$_.IsSystem -eq $False}
	$ArrRolesTo = Get-VIRole -Server $To |?{$_.IsSystem -eq $False}
	
	#Checking for existing roles
	foreach ($Role in $ArrRolesFrom){
		if($ArrRolesTo|where{$_.Name -like $role})
			{
		Logwrite -Error "$Role already exists on $To"
		logwrite -Info "Checking permissions for $role"
			[string[]]$PrivsRoleFrom = Get-VIPrivilege -Role (Get-VIRole -Name $Role -Server $From) |%{$_.id}
			[string[]]$PrivsRoleTo = Get-VIPrivilege -Role (Get-VIRole -Name $Role -Server $To) |%{$_.id}
				foreach ($Privilege in $PrivsRoleFrom){
					if ($PrivsRoleTo | where {$_ -Like $Privilege})
					{
					Logwrite -Error "$Privilege already exists on $role"
					}
					else
					{
						#Setting privileges
						Set-VIRole -Role (Get-VIRole -Name $Role -Server $To) -AddPrivilege (Get-VIPrivilege -Id $PrivsRoleFrom -Server $To)|Out-Null
						Logwrite -Success "Setting $privilege on $role"
					}
				}
			}
			else
			{
				#Creating new empty role
				New-VIrole -Name $Role -Server $To|Out-Null
				Logwrite -Success "Creating $Role on $To" 
				Logwrite -Info "Checking permissions for $role"
				[string[]]$PrivsRoleFrom = Get-VIPrivilege -Role (Get-VIRole -Name $Role -Server $From) |%{$_.id}
				[string[]]$PrivsRoleTo = Get-VIPrivilege -Role (Get-VIRole -Name $Role -Server $To) |%{$_.id}
				foreach ($Privilege in $PrivsRoleFrom)
				{
					if ($PrivsRoleTo|where {$_ -Like $Privilege})
					{
						Logwrite -Error "$Privilege already exists on $role"
					}
					else
					{
					#Setting privileges
					Set-VIRole -role (get-virole -Name $Role -Server $To) -AddPrivilege (get-viprivilege -id $PrivsRoleFrom -server $To)|Out-Null
					logwrite -success "Setting $privilege on $role"
					}
				}
			}
		}
	}
	catch 
	{
		throw
	}
}

Exporting CSV files with your default delimiter

Normally when you export a csv file in powershell it uses the default delimiter comma, unless you use the -delimiter parameter to use another delimiter.

Depending on your language settings tools like Excel use a different delimiter. You can find these settings in your region/language settings -> Formats -> additional settings. Here is a field with “List seperator” which shows you the list separator selected.

You also can retrieve this with the powershell command below:

[Powershell](Get-Culture).TextInfo.ListSeparator[/powershell]

Because of this and switching between servers/workstations with different language settings this can be frustrating sometimes. This is why the –useculture parameter is available

Use the list separator for the current culture as the item delimiter. The default is a comma (,).

This parameter is very useful in scripts that are being distributed to users worldwide. To find the list separator for a culture, use the following command:

export-csv -useculture

This uses your local delimiter as delimiter for your export CSV, so as soon as you doubleclick it and it opens in excel it is already correctly seperated.

Removing RDM’s through mirroring to VMDK

While we are trying to reduce the use of RDM’s we also started to active migrate them to VMDK’s where possible. This helps us to keep the environment dynamic.

This is a short tutorial how we use the Windows Mirror feature to mirror the disk and then remove it.

First start with gathering the disk size about the disk you want to remove. Create a new VMDK  disk with the same size as the RDM disk. Go to Windows Disk management, initialize and create a dynamic disk.
*Note both disks should be dynamic

 

<<MMM somehow my text got removed will add it again>>

Add or subtract date/time in Excel and Powershell

Excel

Manipulate the date

Let’s say we have a field with a date, for example I take an easy date like : 1-1-2011 I put it in field A1
Now how to add/subtract this value in another field.

Depending on your language :

Enter the following formula to create a new date property field (of course another field)

=DATE(YEAR(A1);MONTH(A1);DAY(A1))

1-1-2014 1-1-2014

So now we have 2 fields with the same date. Now let’s add something to the original date.

For this example I add +5 to the Year, Month and Day.

=DATE(YEAR(A1)+5;MONTH(A1)+5;DAY(A1)+5)

This results in :

1-1-2014 6-6-2019

The same trick can be used to only edit one property of course, to subtract we use -5

=DATE(YEAR(A1)-5;MONTH(A1)-5;DAY(A1)-5)

1-1-2014 27-7-2008

Manipulate the time

As you can see it’s pretty easy the same can be done with the time property

=TIME(HOUR(A2)+5;MINUTE(A2)+5;SECOND(A2)+5)

Or

=TIME(HOUR(A2)5;MINUTE(A2)5;SECOND(A2)5)

Powershell

Let’s create a date/time variable

$date = get-date

The $date variable contains a lot of properties

PS C:\> $date|select *
DisplayHint : DateTime
DateTime    : maandag 3 november 2014 14:02:14
Date        : 3-11-2014 0:00:00
Day         : 3
DayOfWeek   : Monday
DayOfYear   : 307
Hour        : 14
Kind        : Local
Millisecond : 447
Minute      : 2
Month       : 11
Second      : 14
Ticks       : 635506201344476042
TimeOfDay   : 14:02:14.4476042
Year        : 2014

Now we can use the “add Methodes to manipulate the $date variable”

PS C:\> $date|gm
TypeName: System.DateTime

Name                 MemberType     Definition
----                 ----------     ----------
Add                  Method         datetime Add(timespan value)
AddDays              Method         datetime AddDays(double value)
AddHours             Method         datetime AddHours(double value)
AddMilliseconds      Method         datetime AddMilliseconds(double value)
AddMinutes           Method         datetime AddMinutes(double value)
AddMonths            Method         datetime AddMonths(int months)
AddSeconds           Method         datetime AddSeconds(double value)
AddTicks             Method         datetime AddTicks(long value)
AddYears             Method         datetime AddYears(int value)

etc.

As you can see there are a lot things that can be changed.

Manipulate the date

With the example of the Excel commands I just add +5 to the Year,Month,Day

$date.addyears(+5).addMonths(+5).addDays(+5)
woensdag 8 april 2020 14:02:14

Or to subtract use

$date.addyears(-5).addMonths(-5).addDays(-5)
vrijdag 29 mei 2009 14:02:14

Manipulate the time

This can be done the sameway like described above

$date.addHours(+5).addMinutes(+5).addSeconds(+5)
maandag 3 november 2014 19:07:19

Or to subtract use

$date.addHours(-5).addMinutes(-5).addSeconds(-5)
maandag 3 november 2014 8:57:09

Hopefully someone finds it useful.

Powershell logging function with date and time stamp

Logging Function

While I made this function a few times on demand with different purposes, I gathered a nice clean script somewhere (not sure where I found it) to start logging to a file.
I added the date/time stamp together with a write-host command to get the logging also on screen. This is a script I know always copy in my scripts to get a fast and simple logging function

 


#Variable to set the logfile location
$Logfile = "D:\LogFile.txt"

Function LogWrite{
<#
.SYNOPSIS
Simple function to write to a appending logfile with date and time stamp

.PARAMETER  $LogString
Input parameter which holds the data which goes to the $logfile

.PARAMETER  $Logfile
Parameter which contains the location of the logfile the information is written to

.EXAMPLE
LogWrite "Starting with $StrTitle files"
LogWrite "Found $StrTitle files with an amount of $TotalSize MB."

.NOTES
Version:        1.0
Creation Date:  26-08-2014
Purpose/Change: PRD
#>
Param ([string]$logstring)
Add-content $Logfile -value (((Get-Date).ToString()) + " " + $logstring)
Write-Host (((Get-Date).ToString()) + " " + $logstring)
}

Exection is as simple as :

logwrite "This line is being sent to screen and text file while a date & time stamp is added"

Powershell shows:

PowerCLI C:\> logwrite "This line is being sent to screen and text file while a date & time stamp is added"
30-8-2014 10:05:52 This line is being sent to screen and text file while a date & time stamp is added

And the text file:

30-8-2014 10:05:52 This line is being sent to screen and text file while a date & time stamp is added

Hopefully it can help someone just copy/paste and adjust where needed:)

Slow mouse performance Windows Server 2012 VM

Slow mouse ? Windows Server 2012 RDP ?

Recently we deployed some Windows 2012 server VM’s. Somehow I noticed that in the console but also RDP the mouse lags and is slow. Apparently I’m some one who notices this pretty quick, after a quick check I saw VMware tools where installed properly, correct drivers are loaded so this shouldn’t cause the problem.

After a little google search I found a blog with a simple solution we probably forgot while deploying the 2012 machines. It was just the little mouse pointer shadow :S

How do I turn this off ?

Go to the “control panel” and open “System”

Open “Advanced system settings and then click the “Settings” button at the performance field. In the tab “Visual effects” you see an option “Show shadows under mouse pointer”. After disabling this, the performance of the mouse was back to normal again.

I also decided to set the settings to “Adjust for best performance” because this will decrease any other almost not noticable lag 🙂

Powershell: Parameter input e-mail script validation

This is a little trick I used for doing an e-mail check verification in  a powershell script. The reason why I did choose for ValidateScript is that you can customize the error message a bit.

It’s possible to use ValidatePattern with the  same RegEx but, when there is no match you will get a  strange error that your input doesn’t fit with the RegEx used

Param(
[Parameter()][ValidateScript({
If ($_ -match "\w+@\w+\.\w+") {
$True
}
else {
Throw "$_ is not a valid e-mail address "
}})][String]$StrMailAddressManager
)
1 2 3