Tag Archives: Scripting

Active Directory Friday: Find groups with no members

Occasionally groups may become obsolete or are never populated with members. It can be interesting to find out how many groups are in your organization that have no members, as action can be taken on it based on the output.

Overview of articles in this series
Active Directory Friday: Find groups with no members
Active Directory Friday: Principal group membership
Active Directory Friday: User account group membership

Because of the nature of how group membership is defined this article will be the first in a series of three. In this article I will show how group membership can be determined using an LDAP queries. The next article in this series will go into principal group membership and its implications and the final article will go into constructed attributes and how to work with constructed attributes, specifically the memberof attribute.

In this article I will give a a number of examples that can be used to determine which groups are empty. Using Get-ADGroup the following command can be executed to retrieve memberless groups:

Get-ADGroup -LDAPFilter '(!(member=*))'

Get-ADGroupNotMemberAnything

Alternatively the DirectoryServices.DirectorySearcher object can be used to achieve a similar result:

(New-Object DirectoryServices.DirectorySearcher -Property @{
 Filter = '(&(objectClass=group)(!(member=*)))'
 PageSize = 100
}).FindAll()

The [adsisearcher] type accelerator is another interesting alternative for this purpose, here is an example:

([adsisearcher]'(&(objectClass=group)(!(member=*)))').FindAll()

The problem with the above examples however, is that some groups will show up as being empty, for example the Domain Users group. Next week I will go into Principal group membership, what this is and how to query for this and by doing so generate more accurate results in regards to group membership.

For more information about the topics discussed in this article, please have a look at the following resources:

Active Directory Friday: Find groups with no members
Get-ADGroup
JaapBrasser.com – Active Directory Friday
Free ebook – Active Directory Friday All Articles
DirectoryServices.DirectorySearcher

New script Remove-ScheduledTask in TechNet Script Gallery

One of the things that keeps me busy is maintaining the library of scripts I have made available in the TechNet Script Library. Earlier this week I noticed a request for my Get-ScheduledTask script, a script that gets a list containing the scheduled tasks from either the local machine or a remote system. The request stated if it was possible to create a function that could delete scheduled tasks.

Based on this question I had a look at the Get-ScheduledTask script and decided that it requried an additional property in the custom object that it outputs, the computer name. Using both the computer name and the path of a scheduled task I am able to delete scheduled tasks on both local systems as well as remotely.

Here is an example of Remove-ScheduledTask in combination with Get-ScheduledTask:

Get-ScheduledTask-Remove

.EXAMPLE
.\Get-ScheduledTask.ps1 | Where-Object {$_.State -eq 'Disabled'} | Remove-ScheduledTask -WhatIf

Description
-----------
Get-ScheduledTask will list all the disabled tasks on a system and the Remove-ScheduledTask function will list all the actions that could be taken

For more information or the direct download links of these scripts please refer to the links below. Feel free to leave a comment either here or in the TechNet Script Library.

TechNet Script Gallery
My entries in TechNet Script Gallery
Get-ScheduledTask
Remove-ScheduledTask

 

New versions of Connect-Mstsc and Get-OrphanHomeFolder in TechNet Script Gallery

One of the things that keeps me busy is maintaining the library of scripts I have made available in the TechNet Script Library. I get a good number of questions and requests about my scripts there and I enjoy picking out some of the interesting or useful requests to implement them in my scripts. Recently I received some requests for new functionality in some of my scripts so I decided two of my scripts for the latest round of updates.

For Connect-Mstsc I have updated both the PowerShell 3.0 and up version as well as the version that is backwards compatible with PowerShell 2.0. A new parameter has been introduced, -Public, which corresponds with the /public switch of the mstsc.exe tool. It runs Remote Desktop in public mode, which was requested by MSFTW. Here is an example of this switch parameter in action:

.EXAMPLE   
Connect-Mstsc -ComputerName server01:3389 -User contoso\jaapbrasser -Password supersecretpw -Public

Description 
-----------     
A RDP session to server01 at port 3389 will be created using the credentials of contoso\jaapbrasser and the /public switch will be set for mstsc

Get-OrphanHomeFolder has been updated to support wildcards/regular expressions to be able to exclude folders. This came from a request of martin_i who has a lot of folders named .v2 which he would like to exclude instead of manually specifying each path. Here is an example:

.EXAMPLE    
.\Get-OrphanHomeFolder.ps1 -HomeFolderPath \\Server02\Fileshare\Home -MoveFolderPath \\Server03\Fileshare\MovedHomeFolders -ExcludePath '\.v2$' -RegExExclude 
 
Description: 
Will list all the folders in the \\Server02\Fileshare\Home folder and will move orphaned folders using robocopy, excluding folders that end with .v2

For more information or the direct download links of these scripts please refer to the links below. Feel free to leave a comment either here or in the TechNet Script Library.

TechNet Script Gallery
My entries in TechNet Script Gallery
Script to get orphaned home folders and folder size
Connect-Mstsc – Open RDP Session with credentials
Connect-Mstsc – Open RDP Session with credentials (PowerShell 2.0)

Active Directory Friday: Distribution group membership for AD User

To get a list of distribution groups an Active Directory user account is a member of of we can query Active Directory. For example by combining the Get-ADUser and Get-ADGroup cmdlets. To generate this list the following code can be used:

1
2
3
Get-ADUser -Identity JaapBrasser -property memberof |
Select-Object -ExpandProperty memberof | Get-ADGroup |
Where-Object {$_.groupcategory -eq 'Distribution'}

The Get-ADUser cmdlet gets all the groups Jaap Brasser is a member of, the Select-Object cmdlet expands the MemberOf attribute which is then piped into the Get-ADGroup cmdlet. The last step is using the Where-Object cmdlet to filter out only the Distribution groups to get the desired results.

Alternatively the DirectoryServices DirectorySearcher object can be used. This object does not require the Active Directory module to be installed and can run on any version of PowerShell. The following code can be used:

1
2
3
4
5
6
7
8
9
10
$ADSearcher = New-Object DirectoryServices.DirectorySearcher -Property @{
    Filter = "(samaccountname=JaapBrasser)"
} | ForEach-Object {
    $_.FindOne().Properties.memberof | ForEach-Object {
        $CurrentGroup = [adsi]"LDAP://$_"
        if (-not ([int](-join $CurrentGroup.Properties.grouptype) -band 0x80000000)) {
            $CurrentGroup.Properties.name
        }
    }
}

This sample works by querying Active Directory for the samaccountname JaapBrasser. Of this user account the distinguishedname of each group object is retrieved. The group type is explained in last weeks post as well, in which I explained about the hex codes which defines whether a group is a Security Group or a Distribution group. The article is available here: Creating Active Directory groups using PowerShell

For more information on this subject please refer to the following links:

Distribution group membership
Get-ADGroup
Get-ADUser
Understanding Groups
2.2.12 Group Type Flags
Creating Active Directory groups using PowerShell

Active Directory Friday: Creating Active Directory groups using PowerShell

Creating a group in Active Directory using PowerShell is relatively simple when using the Active Directory module. To create a Global Distribution Group the following code can be executed:

1
New-ADGroup -Name NewGlobalDG_1 -GroupScope Global -GroupCategory Distribution

When creating a Domain Local Security Group the GroupScope can be changed to DomainLocal and GroupCategory can be omitted since the default is a Security Group:

1
New-ADGroup -Name NewDLSG_1 -GroupScope DomainLocal

Creating groups using the [adsi] provider is a three step process. First we bind to the OU in which the group should be created. Secondly we enter the name and the properties of the group that should be created. And by finally calling the SetInfo() method to create the group. The following code will create a group:

1
2
3
4
5
$TargetOU = [adsi]'LDAP://OU=Groups,DC=jaapbrasser,DC=com'
$Group = $TargetOU.Create('group','cn=System_Operators')
$Group.put('grouptype',0x80000004)
$Group.put('samaccountname','System_Operators')
$Group.SetInfo()

To specify the Group Type a hexadecimal value is required as specified in the following MSDN article: 2.2.12 Group Type Flags. The following table lists all the possible values:

Symbolic name Value
GROUP_TYPE_BUILTIN_LOCAL_GROUP 0x00000001
GROUP_TYPE_ACCOUNT_GROUP 0x00000002
GROUP_TYPE_RESOURCE_GROUP 0x00000004
GROUP_TYPE_UNIVERSAL_GROUP 0x00000008
GROUP_TYPE_APP_BASIC_GROUP 0x00000010
GROUP_TYPE_APP_QUERY_GROUP 0x00000020
GROUP_TYPE_SECURITY_ENABLED 0x80000000

It is important to note that only four values are relevant to us when creating Active Directory accounts:

  • GROUP_TYPE_ACCOUNT_GROUP – 0x00000002
  • GROUP_TYPE_RESOURCE_GROUP – 0x00000004
  • GROUP_TYPE_UNIVERSAL_GROUP – 0x00000008
  • GROUP_TYPE_SECURITY_ENABLED – 0x80000000

To simplify the creation of groups these values can be place in a hashtable:

1
2
3
4
5
6
$GroupType = @{
    Global      = 0x00000002
    DomainLocal = 0x00000004
    Universal   = 0x00000008
    Security    = 0x80000000
}

Using the values stored in the hash table it is now possible to create any of the three group scopes as either a distribution group or security group. The following example uses the -bor operator to combine the values to create a Universal Security Group:

1
2
3
4
5
$TargetOU = [adsi]'LDAP://OU=Groups,DC=jaapbrasser,DC=com'
$Group = $TargetOU.Create('group','cn=Universal_Operators')
$Group.put('grouptype',($GroupType.Universal -bor $GroupType.Security))
$Group.put('samaccountname','Universal_Operators')
$Group.SetInfo()

That is all there is to it, using this methodology it is possible to create any type of Active Directory group using either the Active Directory module or the [adsi] type accelerator. Below I have included some links in regards to this topic.

Creating Active Directory Groups
New-ADGroup
Understanding Groups
2.2.12 Group Type Flags

Disconnect-LoggedOnUser – Disconnect a RDP session remotely

I created a new function that wraps around the rwinsta commandline-tool to remotely disconnect RDP sessions. I created this function to simplify cleaning up open RDP sessions, for example when an administrator leaves the company there might be disconnected RDP sessions by this user. This scripts works in combination with the Get-LoggedOnUser script to simplify finding and disconnecting these sessions. The script is available for download in the TechNet Script Gallery: Disconnect-LoggedOnUser.

A simple example of how to use this function is as follows:

.EXAMPLE 
Disconnect-LoggedOnUser -ComputerName server01 -Id 5 
 
Description 
----------- 
Disconnect Session Id 5 on server01 

In the next example the output of the Get-LoggedOnUser script is filtered by Where-Object and then piped into the Disconnect-LoggedOnUser script:

.EXAMPLE 
.\Get-LoggedOnUser.ps1 -ComputerName server01,server02 | Where-Object {$_.UserName -eq 'JaapBrasser'} | Disconnect-LoggedOnUser -Verbose 
 
Description 
----------- 
Use the Get-LoggedOnUser script to gather the user sessions on server01 and server02. Where-Object filters out only the JaapBrasser user account and then disconnects the session by piping the results into Disconnect-LoggedOnUser while displaying verbose information. 

The complete function is available in the TechNet Script Library. To view this script or to participate in the discussions about this script either comment here or in the TechNet Script Gallery.

TechNet Script Gallery
My entries in TechNet Script Gallery
Disconnect-LoggedOnUser
Get-LoggedOnUser