header image

Archive for PowerShell and Active Directory

Searching Active Directory by email address

Posted by: | November 21, 2019 Comments Off on Searching Active Directory by email address |

I was asked recently about searching Active Directory by email address and returning the first and last names of the users.

First off I needed to populate the first and last name and email fields as they weren’t set in my test domain

Get-ADUser -Filter * -SearchBase ‘OU=UserAccounts,DC=Manticore,DC=org’ |
foreach {
$names = $_.Name -split ‘ ‘

Set-ADUser -Identity $_.DistinguishedName -EmailAddress $_.UserPrincipalName -GivenName $names[1].Trim() -Surname $names[0].Trim()

}

 

Get the users in the UserAccounts domain. Split the name and use that information to set the first (GivenName) and last (Surname) properties. Use the UserprincipalName for the email address.

 

Now create a CSV file with the email addresses

PS>  Get-ADUser -SearchBase ‘OU=UserAccounts,DC=Manticore,DC=org’ -Filter * -Properties EmailAddress | select -Property  EmailAddress | Export-Csv -Path C:\Test\addresses.csv

 

Test the file

PS>  Import-Csv -Path C:\Test\addresses.csv

 

You can’t use the –Identity property when you’re searching by email address. You have to use a filter:

PS>  Get-ADUser -SearchBase ‘OU=UserAccounts,DC=Manticore,DC=org’ -Filter {EmailAddress -eq ‘DorothyJones@manticore.org’}

 

Or LDAP filter

PS>  Get-ADUser -SearchBase ‘OU=UserAccounts,DC=Manticore,DC=org’ -LDAPFilter “(objectclass=user)(mail=DorothyJones@manticore.org)”

 

The difference is that the filter uses the AD cmdlet name for the property but the LDAP filter uses the LDAP name of the proeprty

 

To read the email addresses and find the corresponding names using a filter

Import-Csv -Path C:\Test\addresses.csv |
ForEach-Object {
$address = $_.EmailAddress
$user = Get-ADUser -SearchBase ‘OU=UserAccounts,DC=Manticore,DC=org’ -Filter {EmailAddress -eq $address}

$props = [ordered]@{
FirstName = $user.GivenName
Lastname = $user.Surname
Email = $_.EmailAddress
}
New-Object -TypeName PSobject -Property $props
}

 

Import the addresses. I’ve found it less error prone to create a variable to handle the value I’m searching for rather than trying to substitute into the filter. Once you have the account create the output object.

 

Using an LDAP filter is similar

Import-Csv -Path C:\Test\addresses.csv |
ForEach-Object {
$address = $_.EmailAddress
$user = Get-ADUser -SearchBase ‘OU=UserAccounts,DC=Manticore,DC=org’ -LDAPFilter “(objectclass=user)(mail=$address)”

$props = [ordered]@{
FirstName = $user.GivenName
Lastname = $user.Surname
Email = $_.EmailAddress
}
New-Object -TypeName PSobject -Property $props
}

 

The choice between a filter and an LDAP filter is up to you. Both have quirks and are probably equally awkward to use.

under: PowerShell and Active Directory

Get-AdUser in PowerShell Core

Posted by: | August 29, 2019 Comments Off on Get-AdUser in PowerShell Core |

There has been a problem with Get-ADUser in PowerShell core such that

Get-ADUser -Identity Richard -Properties *

 

Throws an error.

 

The problem is in .NET Core and affects a small number of properties including ProtectedFromAccidentalDeletion

 

The underlying .NET Core issue has been fixed and PowerShell v7 preview 3 on Windows 10.0.18362 will successfully run the command

under: PowerShell 7, PowerShell and Active Directory, PowerShell v6

Putting user information into computer description

Posted by: | March 22, 2019 Comments Off on Putting user information into computer description |

I was recently asked how to add user information – specifically first and last name – into computer description in Active Directory.

First get your user

$user = Get-ADUser -Identity FredBrown

 

Then add the information to the computer’s description

Set-ADComputer -Identity W10ProIp -Description “Used by $($user.Givenname) $($user.Surname)”

 

You need to use the subexpression – $() – syntax to resolve the values rather than getting references to the $user object.

 

Then test the description has been set

Get-ADComputer -Identity W10ProIp -Properties Description

 

If it was me I’d add the samAccountName or other unique identifier as name isn’t sufficient to uniquely identify the user

under: PowerShell and Active Directory

Moving FSMO roles in PowerShell v6.1.1

Posted by: | November 22, 2018 Comments Off on Moving FSMO roles in PowerShell v6.1.1 |

With the Windows Server 2019 media now being available again it’s time to move my test lab over to the new version. I’d built a Windows Server 2019 VM and installed PowerShell v6.1.1. I discovered that in Server 2019 and the Windows 10 October 2018 update that the AD module worked in PowerShell v6.1.1. I decided to try moving FSMO roles in PowerShell v6.1.1 as I updated the domain and removed the old Server 2016 domain controller.

 

The usual schema update went smoothly – updated the schema version to 88 from 87. Installing AD domain services and DNS on the new DC worked. Promoting the Windows 2019 system to be a DC worked with no problems.

 

Time to move the FSMO roles. They would move automatically when the old DC was removed but its always better to control the action.

Import-Module ActiveDirectory

will load the AD module into PowerShell v6.1.1.

 

There are 5 FSMO roles – 2 at the forest level

PS> Get-ADForest | Format-List Name, *master

Name : Manticore.org
DomainNamingMaster : W16DC01.Manticore.org
SchemaMaster : W16DC01.Manticore.org

 

And 3 at the domain level – I only have a single domain to worry about.

PS> Get-ADDomain | Format-List *master, PDC*

InfrastructureMaster : W16DC01.Manticore.org
RIDMaster : W16DC01.Manticore.org
PDCEmulator : W16DC01.Manticore.org

 

The forest level FSMO roles moved:

PS> Move-ADDirectoryServerOperationMasterRole -Identity W19DC01 -OperationMasterRole DomainNamingMaster -Confirm:$false

PS> Move-ADDirectoryServerOperationMasterRole -Identity W19DC01 -OperationMasterRole SchemaMaster -Confirm:$false

PS> Get-ADForest | Format-List Name, *master

Name : Manticore.org
DomainNamingMaster : W19DC01.Manticore.org
SchemaMaster : W19DC01.Manticore.org

 

For the domain level FSMO roles I decided to get ambitious

PS> Move-ADDirectoryServerOperationMasterRole -Identity W19DC01 -OperationMasterRole RIDMaster, InfrastructureMaster, PDCEmulator -Confirm:$false

PS> Get-ADDomain | Format-List *master, PDC*

InfrastructureMaster : W19DC01.Manticore.org
RIDMaster : W19DC01.Manticore.org
PDCEmulator : W19DC01.Manticore.org

 

Moving FSMO roles in PowerShell v6.1.1 was successful

under: PowerShell and Active Directory, PowerShell v6

Active Directory cmdlets in PowerShell v6.1.1

Posted by: | November 16, 2018 Comments Off on Active Directory cmdlets in PowerShell v6.1.1 |

Just discovered that you can run the Active Directory cmdlets in PowerShell v6.1.1 BUT there is a huge caveat.

 

The Windows 10 October 2018 (Windows 10 1809) update includes the RSAT tools (including the AD tools) as optional features. This means that you can easily install the AD tools:

Add-WindowsCapability -Name Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0 –Online

 

The AD PowerShell module now has a 1.0.1.0 version with the PSEdition set as Core,Desk

 

I’m running the Windows 10 Insider Preview build 18282 which has moved on a bit from Windows 10 1809 and PowerShell Core v6.1.1.

 

The AD module can be imported and the commands that I’ve tried have worked.

 

For the AD cmdlets to work in PowerShell Core looks like you need Windows 10 October 2018 update or later and PowerShell v6.1.1 or later. I’m going to say you should upgrade to v6.1.1 if you have v6.1 as the later version fixes a .NET security bug.

 

This is a big step forward for PowerShell Core being an acceptable replacement for Windows PowerShell though there are still a few gaps to be filled.

under: PowerShell and Active Directory, PowerShell v6

Windows Compatibility Pack

Posted by: | January 4, 2018 Comments Off on Windows Compatibility Pack |

As reported last month the Windows Compatibility Pack for .NET core is available. This adds back some of the functionality missing from .NET core. This functionality is ONLY of relevance on Windows machines.

 

A PowerShell module based on the Compatibility Pack is in the works – this will add a number of cmdlets including the WMI cmdlets back into PowerShell v6 on Windows. There’s no ETA on the module at this time.

 

There is a module on the PowerShell gallery that will add the .NET components of the Compatibility Pack into your PowerShell v6 session.

PS>  Find-Module -Name PSCoreWindowsCompat | ft -a

Version Name                Repository Description 
 ------- ----                ---------- ----------- 
 0.1.0.2 PSCoreWindowsCompat PSGallery  Provides the Microsoft.Windows.Compatibility Pack to PowerShell Core.

 

If you want to inspect the module

PS>  Save-Module -Name PSCoreWindowsCompat -Repository PSGallery -Path C:\Source\ -Force

 

To install the module:

PS>  Install-Module -Name PSCoreWindowsCompat -Repository PSGallery -Verbose -Force 
 VERBOSE: Repository details, Name = 'PSGallery', Location = 'https://www.powershellgallery.com/api/v2/'; IsTrusted = 'False'; IsRegistered = 'True'. 
 VERBOSE: Using the provider 'PowerShellGet' for searching packages. 
 VERBOSE: Using the specified source names : 'PSGallery'. 
 VERBOSE: Getting the provider object for the PackageManagement Provider 'NuGet'. 
 VERBOSE: The specified Location is 'https://www.powershellgallery.com/api/v2/' and PackageManagementProvider is 'NuGet'. 
 VERBOSE: Searching repository 'https://www.powershellgallery.com/api/v2/FindPackagesById()?id='PSCoreWindowsCompat'' for ''. 
 VERBOSE: Total package yield:'1' for the specified package 'PSCoreWindowsCompat'. 
 VERBOSE: Performing the operation "Install-Module" on target "Version '0.1.0.2' of module 'PSCoreWindowsCompat'". 
 VERBOSE: The installation scope is specified to be 'AllUsers'. 
 VERBOSE: The specified module will be installed in 'C:\Program Files\PowerShell\Modules'. 
 VERBOSE: The specified Location is 'NuGet' and PackageManagementProvider is 'NuGet'. 
 VERBOSE: Downloading module 'PSCoreWindowsCompat' with version '0.1.0.2' from the repository 'https://www.powershellgallery.com/api/v2/'. 
 VERBOSE: Searching repository 'https://www.powershellgallery.com/api/v2/FindPackagesById()?id='PSCoreWindowsCompat'' for ''. 
 VERBOSE: InstallPackage' - name='PSCoreWindowsCompat', version='0.1.0.2',destination='C:\Users\Richard.MANTICORE\AppData\Local\Temp\51711061' 
 VERBOSE: DownloadPackage' - name='PSCoreWindowsCompat', version='0.1.0.2',destination='C:\Users\Richard.MANTICORE\AppData\Local\Temp\51711061\PSCoreWindowsCompat\PSCoreWindowsCompat.nupkg', uri='https://www.powershellgallery.com/api/v2/package/PSCoreWindowsCompat/0.1.0.2' 
 VERBOSE: Downloading 'https://www.powershellgallery.com/api/v2/package/PSCoreWindowsCompat/0.1.0.2'. 
 VERBOSE: Completed downloading 'https://www.powershellgallery.com/api/v2/package/PSCoreWindowsCompat/0.1.0.2'. 
 VERBOSE: Completed downloading 'PSCoreWindowsCompat'. 
 VERBOSE: InstallPackageLocal' - name='PSCoreWindowsCompat', version='0.1.0.2',destination='C:\Users\Richard.MANTICORE\AppData\Local\Temp\51711061' 
 VERBOSE: Catalog file 'PSCoreWindowsCompat.cat' is not found in the contents of the module 'PSCoreWindowsCompat' being installed. 
VERBOSE: Module 'PSCoreWindowsCompat' was installed successfully to path 'C:\Program Files\PowerShell\Modules\PSCoreWindowsCompat\0.1.0.2'.

 

Notice the installation path is OUTSIDE of the current version of PowerShell v6 so should remain available through any upgrades.

PS>  Import-Module -Name PSCoreWindowsCompat

 

Now you’ve got it how do you use it? The module DOESN’T have any functions – it just loads the .NET namespaces.

 

Its back to PowerShell v1 days – everything is a script instead of a cmdlet. For instance the Compatibility pack contains the System.DirectoryServices so you can script against AD.

 

Let’s say you want to see all the users in the domain:

$dom = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() 
 $root = $dom.GetDirectoryEntry()

$search = [System.DirectoryServices.DirectorySearcher]$root 
 $search.Filter = "(&(objectclass=user)(objectcategory=user))" 
 $search.SizeLimit = 3000 
 $result = $search.FindAll()

foreach ($user in $result){ 
    $user.Properties.distinguishedname 
 }

 

Use System.DirectoryServices.ActiveDirectory.Domain to get the current domain. Create a System.DirectoryServices.DirectorySearcher object and set the filter to users. Find all of the users and display their distinguishedname

 

Its a lot more complicated than using the cmdlet:

PS>  Get-ADUser -Filter * | select DistinguishedName

 

but it gets the job done.

 

If you need to administer AD and you need the cross platform capabilities of PowerShell maybe you should use the  PSCoreWindowsCompat module (with aplogies to A-Team fans everywhere)

under: PowerShell and Active Directory, PowerShell v6

Get an AD user’s manager

Posted by: | November 27, 2017 Comments Off on Get an AD user’s manager |

Interesting question on the forum about finding the manager for a given user in AD – assuming the Manager field is populated of course. If you’ve not worked with the AD cmdlets this is a good introduction to some of their quirks. This is how you get an AD user’s manager.

 

You need the manager property on the AD user account but that’s not one of the default properties that’s returned so you need to use the –Propertie parameter to ensure you get your data. Assuming you have a csv file with userids that looks like this

id userid 
-- ------ 
1  DonBrown 
2  DonFox 
3  JamesBrown 
4  JamesBlack

 

You can use this code

Import-Csv -Path .\names.csv | 
foreach { 
   $user = Get-ADUser -Identity $_.userid -Properties Manager 
   $_ | Add-Member -MemberType NoteProperty -Name 'Manager' -Value $user.Manager 
   $_ 
 }

 

This returns the distinguished name of the manager

id userid     Manager                                           
-- ------     -------                                           
1  DonBrown   CN=HARRIS Fred,OU=UserAccounts,DC=Manticore,DC=org 
2  DonFox     CN=HARRIS Fred,OU=UserAccounts,DC=Manticore,DC=org 
3  JamesBrown CN=HARRIS Fred,OU=UserAccounts,DC=Manticore,DC=org 
4  JamesBlack CN=HARRIS Fred,OU=UserAccounts,DC=Manticore,DC=org

 

if you prefer to have their name then you need an extra step

Import-Csv -Path .\names.csv | 
foreach { 
   $user = Get-ADUser -Identity $_.userid -Properties Manager 
   $manager = Get-ADUser -Identity $user.Manager 
   
   $_ | Add-Member -MemberType NoteProperty -Name 'Manager' -Value $manager.Name 
   $_ 
 }

 

Which gives output like this

id userid     Manager    
-- ------     -------    
1  DonBrown   HARRIS Fred 
2  DonFox     HARRIS Fred 
3  JamesBrown HARRIS Fred 
4  JamesBlack HARRIS Fred

 

If you need to output the data to a csv file then just add Export-Csv

Import-Csv -Path .\names.csv | 
foreach { 
   $user = Get-ADUser -Identity $_.userid -Properties Manager 
   $manager = Get-ADUser -Identity $user.Manager 
   
   $_ | Add-Member -MemberType NoteProperty -Name 'Manager' -Value $manager.Name 
   $_ 
 } | Export-Csv -Path names2.csv –NoTypeInformation
under: PowerShell and Active Directory

Comparing AD group membership on EmployeeId

Posted by: | August 30, 2017 Comments Off on Comparing AD group membership on EmployeeId |

Back in this post – http://itknowledgeexchange.techtarget.com/powershell/comparing-group-membership/ I showed how to compare the membership of two groups using Compare-Object. The comparison was based on the samAccountName. A question raised the issue of comparing AD group membership on EmployeeId

In the case in particular users have multiple accounts BUT the EmployeeId is correct on all and will therefore show matching users. Assuming the EmployeeId is correct on all accounts it still leaves a problem.

When you run Get-ADGroupMember you get a very limited number of properties returned:

PS>  Get-ADGroupMember -Identity Testgroup1

distinguishedName : CN=JONES James,OU=UserAccounts,DC=Manticore,DC=org 
 name              : JONES James 
objectClass       : user 
objectGUID        : 027cb406-a3b0-4f45-9bbd-db47ccfb9212 
 SamAccountName    : JamesJones 
 SID               : S-1-5-21-759617655-3516038109-1479587680-1225

First thing I needed to do was set up some users with an EmployeeId

$ei = 1 
 Get-ADUser -Filter {Name -like "*Jones*"} -Properties EmployeeId | 
foreach { 
  $id =  23945 + $ei 
  
  $psitem | Set-ADUser -EmployeeID $id

  $ei = $ei + (Get-Random -Minimum 3 -Maximum 12) 
 }

Get a set of users – including the EmployeeId – and forech of them set the id. The id is randomly generated based on a starting value and increment.

Now that the users have an Employeeid you can use that for comparison purposes

$group1 = Get-ADGroupMember -Identity Testgroup1 | 
foreach { 
  Get-ADUser -Identity $psitem.distinguishedName -Properties EmployeeId | 
  select -ExpandProperty EmployeeId 
 }

$group2 = Get-ADGroupMember -Identity Testgroup2 | 
foreach { 
  Get-ADUser -Identity $psitem.distinguishedName -Properties EmployeeId | 
  select -ExpandProperty EmployeeId 
 }


 Compare-Object -ReferenceObject $group1 -DifferenceObject $group2 -IncludeEqual |             
 where SideIndicator -eq "==" |            
foreach {            
  $id = ($_.InputObject)        
            
  Get-ADUser -Filter {EmployeeId -eq $id} -Properties EmployeeId            
             
 }

Get the membership of the first group and for each member use Get-ADUser to return the EmployeeId. Repeat for the second group.

Use  Compare-Object to compare the two sets of group members – you’re looking for matches indicated by “==”

Foreach match get the AD user account filtering on the EmployeeID.

The PROBLEM with this approach is that you’ll get all user accounts returned that have the particular EmployeeId.   You can replace the line

Get-ADUser -Filter {EmployeeId -eq $id} -Properties EmployeeId

with

Get-ADUser -Filter {EmployeeId -eq $id} -Properties EmployeeId, MemberOf | where {$_.MemberOf -like “*Testgroup1*” -AND $_.MemberOf -like  “*Testgroup2*”}

Which should resolve the problem

under: PowerShell and Active Directory

Get-ADUser filtering

Posted by: | August 9, 2017 Comments Off on Get-ADUser filtering |

Saw a question on the forums that revolved around Get-ADUser filtering.

Initial code was like this

Import-Csv .\users.txt |
foreach {
  Get-ADUser -Filter {Name -like $_.Name}
}

which on the face of it seems reasonable. However, you get errors like this

Get-ADUser : Property: ‘Name’ not found in object of type:
‘System.Management.Automation.PSCustomObject’.
At line:3 char:3
+   Get-ADUser -Filter {Name -like $_.Name}
+   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Get-ADUser], ArgumentException
    + FullyQualifiedErrorId : ActiveDirectoryCmdlet:System.ArgumentException,Microsoft
   .ActiveDirectory.Management.Commands.GetADUser

Change –like to –eq and you’ll still get the error.

This won’t work either:

Import-Csv .\users.txt |
foreach {
  Get-ADUser -Filter {Name -like $($_.Name)}
}

You get messages about path errors.

Import-Csv .\users.txt |
foreach {
  Get-ADUser -Filter {Name -like "$($_.Name)"}
}

will run but won’t return any data.

This will run and return the correct data.

Import-Csv .\users.txt |
foreach {
  $name = $_.Name
  Get-ADUser -Filter {Name -like $name}
}

Alternatively, you can use quotes so that the filter is a string

Import-Csv .\users.txt |
foreach {
  Get-ADUser -Filter "Name -like ‘$($_.Name)’"
}

Another option is to use the LDAP filter syntax

Import-Csv .\users.txt |
foreach {
  $name = $_.Name
  Get-ADUser -LDAPFilter "(Name=$name)"
}

Import-Csv .\users.txt |
foreach {
  Get-ADUser -LDAPFilter "(Name=$($_.Name))"
}

The help file about_activedirectory_filter is well worth reading. It doesn’t seem to be installed with the AD module on Windows Server 2016. You can read it on line at https://technet.microsoft.com/en-us/library/hh531527(v=ws.10).aspx

You’ll also see links to

about_ActiveDirectory  – overview of module

about_ActiveDirectory_Identity

about_ActiveDirectory_ObjectModel

Get-ADUser filtering isn’t the most obvious of topics to get your head round but these examples should help you make your filters work. If you’re building a complex filter build it up a step at a time so you can test each stage.

under: PowerShell and Active Directory

Are your domain controllers real?

Posted by: | May 9, 2017 Comments Off on Are your domain controllers real? |

A question on the forum asked about discovering if domain controllers are physical or virtual machines.

This will do the job

foreach ($domain in (Get-ADForest).domains) {
 Get-ADDomainController -filter * -server $domain |
 sort hostname  |
 foreach {
 Get-CimInstance -ClassName Win32_ComputerSystem -ComputerName $psitem.Hostname |
 select PSComputerName, Manufacturer, Model
 }
 }

 

Get the domains in your forest and then for each domain get the domain controllers. Get-ADDomainController outputs an object with a property of hostname – but you need a computername for Get-CimInstance. So, use a foreach-object and use the Hostname property as shown (you could create a property ComputerName on the pipeline object but its more work) and get the results. A virtual machine will show under the Model. You can sort or whatever once you have the results.

under: PowerShell and Active Directory, PowerShell and CIM

Older Posts »

Categories