Tagged: ActiveDirectory

Reset local admin password

You probably want to have random passwords on all your local admin accounts… Wrote a function that generates a complex (and readable) password and another function that sets the local admin password.

Function Get-RandomPassword {
	PARAM (
		$pwdMask = "####-####-####-####-####",
		$pwdCharacters = "abcdefghjkmnopqrstuvwxy23456789ABCDEFGHJKLMNPQRTUVWXYZ"
	)

	$newPassword = ""
	(0 .. (($pwdMask.Length)-1) ) | ForEach-Object  {
		If ( $pwdMask.Chars($_) -eq "#" ) {
			$rndChar = Get-Random -Minimum 0 -Maximum $pwdCharacters.Length
	 		$newPassword += $pwdCharacters.Chars($rndChar)
		} else {
			$newPassword += $pwdMask.Chars($_)
		}
	}

	Return $newPassword
}


Function Set-LocalAdminPassword {
	PARAM (
		[string] $computerName,
		[string] $newPassword
	)

	$adminAccountName = (Get-WmiObject Win32_UserAccount -Filter "LocalAccount = True AND SID LIKE 'S-1-5-21-%-500'" -ComputerName $computerName | Select-Object -First 1 ).Name
	TRY {
		Write-Verbose "Reset password for $($computerName)\$($adminAccountName) to $($newPassword)"
		$adminAccount = [adsi]"WinNT://$($computerName)/$($adminAccountName),user"
		$adminAccount.setPassword($newPassword)
		Return $true
	}
	CATCH {
		Return $false
	}
}

This is how to reset password on a single computer

Set-LocalAdminPassword -computerName "SOMEPC" -newPassword (Get-RandomPassword)

And if you want to process a list of computers (that are online) from the AD

Import-Module ActiveDirectory
$newPwds = @{}
Get-ADComputer -LDAPFilter "(name=PC00*)" | ForEach-Object {
	$computerName = $_.Name
	If (Test-Connection -ComputerName $computerName -Count 1 -ErrorAction SilentlyContinue) {
		$randomPwd = Get-RandomPassword
		If (Set-LocalAdminPassword -computerName $computerName -newPassword $randomPwd ) {
			$newPwds.Add($computerName, $randomPwd)
		}
	}
}

$newPwds | Format-Table -AutoSize

Default Powershell Execution Policy

You can use a GPO to set the ExecutionPolicy to a static value on all machines.

But what if you want to default it to something and then let the users have the ability to change it?

Group Policy Preferences is the easy answer.

Create a GPO targeting your machines and then create a entry under Computer Configuration -> Preferences -> Windows Settings -> Registry that looks like this:
130917-execpolicy-1

130917-execpolicy-2

The paths are:
Key: SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell
Value Name: ExecutionPolicy
ValueData: RemoteSigned

With this setup a machine that doesn’t have any specific exec policy (then the reg key doesn’t exist) setup will get RemoteSigned.
(Another way is to do this with a startup script for the computer)

Bitlocker Info

I’m playing around with pipelining information to functions… and since I needed a function to read Bitlocker information from Active Directory, why not create one. 🙂

Function Get-BitlockerInfo {
	PARAM (
		[Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true)] $Computer
	)

	BEGIN {
		$bitLockerInfo = @()
	}
	
	PROCESS {
		Write-Verbose "Searching $($Computer.DistinguishedName) ..."
		Get-ADObject -LdapFilter "(msFVE-Recoverypassword=*)" -Searchbase $Computer.DistinguishedName -properties msFVE-RecoveryPassword | ForEach-Object {
			$Bitlocker = $_.Name.Split("{")
			$retObj = New-Object -TypeName System.Object
			$retObj | add-Member -memberType NoteProperty -name ComputerDistinguishedName -Value $Computer.DistinguishedName
			$retObj | add-Member -memberType NoteProperty -name BitlockerTime -Value $Bitlocker[0]
			$retObj | add-Member -memberType NoteProperty -name PasswordID -Value $Bitlocker[1].Replace("}", "")
			$retObj | add-Member -memberType NoteProperty -name RecoveryPassword -Value $_."msFVE-RecoveryPassword"

			$bitLockerInfo += $retObj
		}
	}
	
	END {
		Return $bitLockerInfo
	}
}

# Here is how to use it
Get-AdComputer -LdapFilter "(name=WKS012*)" | Get-BitlockerInfo | Format-List

User pictures in AD

Adding pictures to Active Directory is kind of nice, it gives you pictures in Outlook, Lync and few other products…

To add it you need to load a picture as binary and put it in to the attribute thumbnailPhoto on the user.

Function Add-AdThumbnailPhoto {
    PARAM (
        [ValidateScript({Test-Path $_ -PathType Leaf})] [string] $PicturePath,
        $UserAccount
    )
    
    If (!(Test-IsModuleLoaded "ActiveDirectory")) {
        Throw "You need to run: Import-Module ActiveDirectory"
    }
    Write-Verbose "Adding $($PicturePath) to $($UserAccount)"
    $pictureBinary = [byte[]](Get-Content $PicturePath -Encoding byte)
    
    If ([System.Text.Encoding]::ASCII.GetString($pictureBinary).Length -ge 100Kb) {
        Throw "Picture to large, max size is 100Kb"
    }
    
    
    Try {
        Set-AdUser $UserAccount -Replace @{ thumbnailPhoto = $pictureBinary }
    }
    Catch {
        Throw $error[0]
    }
}

Then you can use the function like this:

Add-AdThumbnailPhoto -PicturePath "C:\MyPicture.jpg" -UserAccount "MyUserAccount"

If you like to do it on a oneliner… here is a bit more compressed version of the same code:

Set-AdUser "MyUserAccount" -Replace @{ thumbnailPhoto = ([byte[]](Get-Content "C:\MyPicture.jpg" -Encoding byte) }

Logon Scripts in Powershell – Part3: Storing and retrieving shares to map to/from AD

To keep you logon scripts static one way of storing information on who sould get what mapped is to use the Active Directory.

First of, talk to the AD-guys and “reserve” 3 attributes on groups (or extend the schema and add your own attributes). You need attributes for:
– Share Path
– Display Name
– Type of mapping

In the example I will use:
– displayName = Share Path
– displayNamePrintable = Display Name
– extensionAttribute3 = Type of mapping (I will use a 1 for “Network Location”, 0 for not in use and letters to map to a specific drive)

Then, populate the groups with information:

Import-Module ActiveDirectory

Get-Adgroup "MyGroupName" | Set-ADGroup -Replace @{
	DisplayName="\\SERVER\PathToMapAsNetworkLocation";
	DisplayNamePrintable="Some Description";
	extensionAttribute3=1
}
Get-Adgroup "SomeOtherGroupName" | Set-ADGroup -Replace @{
	DisplayName="\\SERVER\PathToMapAsDrive";
	DisplayNamePrintable="Some Description";
	extensionAttribute3=X
}

Get-Adgroup "ProjXDocs" | Set-ADGroup -Replace @{
	DisplayName="\\SERVER\Projects\ProjectXdocuments";
	DisplayNamePrintable="ProjectX Documents";
	extensionAttribute3=1
}

Then, retrieve the groups for the current user with a recursive LDAP-query.

Function Get-SharesToMap {
	<#
	.SYNOPSIS
		Read groups in AD for a user, then collect information to use when mapping disk
	#>
	PARAM (
		$UserDN = (Get-UserDN)
	)

	$mappedShares = @()
	$ldapFilter = "(&(member:1.2.840.113556.1.4.1941:=$($UserDN))(displayName=*)(extensionAttribute3=*)(!extensionAttribute3=0))"

	Run-LdapQuery -ldapFilter $ldapFilter | ForEach-Object {
		Write-Verbose "Get-SharesToMap: $($ssabMappedSharePath) - Mapping: $($ssabMappedShare)"
		$shareInfo = New-Object -TypeName System.Object
		$shareInfo | add-Member -memberType NoteProperty -name Group -Value $_.Properties.Item("cn")
		$shareInfo | add-Member -memberType NoteProperty -name Path -Value $_.Properties.Item('displayName')
		$shareInfo | add-Member -memberType NoteProperty -name DisplayName -Value $_.Properties.Item('displayNamePrintable')
		$shareInfo | add-Member -memberType NoteProperty -name MappedShare -Value $_.Properties.Item('extensionAttribute3')
		
		$mappedShares += $shareInfo
	}

	Return $mappedShares
}

Now you have a list of all shares to take care of…

Get-SharesToMap | Sort-Object Path | ForEach-Object {
	$Path        = $_.Path
	$MappedShare = $_.MappedShare
	$DisplayName = $_.DisplayName

	# Map UNC path once
	If ($lastMappedPath -ne $Path) {
		Switch -regex ($MappedShare) {
			0        { Write-Verbose "Loginscript:Main: Do not map UNC path $($UncPath)" }
			1        { Create-NethoodShortcut -LinkTarget $Path -LinkName $DisplayName -Description $DisplayName }
			"[A-Z]"  { Map-UncPathToDrive -UncPath $Path -DriveLetter $MappedShare }
			default  { Write-Verbose "Loginscript:Main: $($MappedShare) Do not map path $($Path)" }
		}
		$lastMappedPath = $Path
	}
}

With this in place, no one needs to edit the logonscript to change mappings. 🙂

Sidenote 1:
To speed up the LDAP-queries add indexes on the attributes.
Start MMC
Add the snapin “Active Directory Schema”
Search for the attribute(s)
On the properties-page, check the box “Index this attribute”

Sidenote 2:
To make it easy to search the attributes, check the box “Ambigous Name Resolution (ANR)” in the properties page for the attribute
With ANR enabled you can do a LDAP-query like:
(ANR=SomeNameOfTheShare)
So, if someone wants to be member of the group for the share “ProjectX” just query like (ANR=ProjectX) and the group for that share will show up.