ADLockoutViewer.ps1
<#PSScriptInfo
.VERSION 1.0.1 .GUID 467f76b7-4e41-4c1f-b3d5-cf064d56aba3 .AUTHOR Chris Carter .COMPANYNAME .COPYRIGHT 2016 Chris Carter .TAGS ActiveDirectory Unlock UnlockUser UnlockAccount AccountLock UserLock GUI .LICENSEURI http://creativecommons.org/licenses/by-sa/4.0/ .PROJECTURI https://gallery.technet.microsoft.com/View-and-Unlock-Active-ef0d5757 .ICONURI .EXTERNALMODULEDEPENDENCIES ActiveDirectory .REQUIREDSCRIPTS .EXTERNALSCRIPTDEPENDENCIES .RELEASENOTES #> <# .SYNOPSIS Displays a GUI of Active Directory users that are locked out and can be unlocked. .DESCRIPTION ADLockoutViewer displays a GUI of Active Directory users in specific OUs. Any user that is locked will have a check by their name. Unchecking the box will unlock the user. OUs can be chosen from the drop down menu. The ComputerName parameter is the computer that has the ActiveDirectory PowerShell module installed if the local computer does not. This command can be used with the CredSSP authentication method. This is useful when you want to use it on a computer that doesn't have the ActiveDirectory PowerShell module installed, but the computer being connected to is not a domain controller. When that happens the CredSSP authentication method has to be used, and the Fully Qualified Domain Name (FQDN) of the remote computer must be used for the ComputerName parameter. Also, the user will be required to enter Administrator level credentials in a separate dialog. .PARAMETER Authentication Specifies the mechanism that is used to authenticate the user's credentials. Valid values are "Default", "Basic", "Credssp", "Digest", "Kerberos", "Negotiate", and "NegotiateWithImplicitCredential". The default value is "Default". For more information about the values of this parameter, see the description of the System.Management.Automation.Runspaces.AuthenticationMechanism enumeration in the MSDN (Microsoft Developer Network) library at http://go.microsoft.com/fwlink/?LinkID=144382. Caution: Credential Security Support Provider (CredSSP) authentication, in which the user's credentials are passed to a remote computer to be authenticated, is designed for commands that require authentication on more than one resource, such as accessing a remote network share. This mechanism increases the security risk of the remote operation. If the remote computer is compromised, the credentials that are passed to it can be used to control the network session. .PARAMETER ComputerName The name of the computer that has the ActiveDirectory PowerShell module installed. Note: If the CredSSP authentication method is used, the Fully Qualified Domain Name (FQDN) of the computer must be specified. .PARAMETER IconPath Specifies the path to an icon file to be used in the GUI. .INPUTS ADLockoutViewer accepts no inputs. .OUTPUTS ADLockoutViewer produces no output. .EXAMPLE PS C:> ADLockoutViewer This command will display the GUI and is assuming the ActiveDirectory PowerShell module is installed on the local machine. .EXAMPLE PS C:> ADLockoutViewer -ComputerName DC01 This command will display the GUI by sending remote commands to the computer named DC01. .EXAMPLE PS C:> ADLockoutViewer -Authentication Credssp -ComputerName WK01.domain.local This command will display the GUI by sending remote commands to the computer named WK01 using CredSSP. Note the use of the Fully Qualified Domain Name (FQDN) used which is required when using CredSSP. .NOTES The remote computer specified in the ComputerName parameter must have PowerShell Remoting enabled if it is not the local computer. #> #Requires -Version 3.0 [CmdletBinding(HelpURI='https://gallery.technet.microsoft.com/View-and-Unlock-Active-ef0d5757')] Param( [System.Management.Automation.Runspaces.AuthenticationMechanism] $Authentication="Default", [Alias("CN")] [String]$ComputerName="$env:COMPUTERNAME", [Alias("Icon")] [String]$IconPath ) #Add assemblies needed for GUI presentation Add-Type -AssemblyName PresentationCore,PresentationFrameWork,System.Windows.Forms,System.Drawing #------------------------------ Begin Region Helper Functions ------------------- #Helper function to create message boxes for alerts, errors, etc. Function Create-MessageBox { Param ( [Parameter (Mandatory=$true)][string]$Message, [Parameter (Mandatory=$true)][string]$Title, [Parameter (Mandatory=$false)][System.Windows.Forms.MessageBoxButtons]$Buttons="OK", [Parameter (Mandatory=$false)][System.Windows.Forms.MessageBoxIcon]$Icon="Information" ) [System.Windows.Forms.MessageBox]::Show($Message, $Title, $Buttons, $Icon) } Function Add-CommandAndKeyBinding ($CanExec,$Exec,$Key,$KeyModifiers,$KeyDesc,$CommandName,$CommandDesc,$CommandBindTarget,$KeyBindTarget) { #Build KeyGesture for Command $KeyGesture = New-Object System.Windows.Input.KeyGesture -ArgumentList ` $Key, $KeyModifiers, $KeyDesc $GestColl = New-Object System.Windows.Input.InputGestureCollection $GestColl.Add($KeyGesture) | Out-Null #Create Routed Command $Command = New-Object System.Windows.Input.RoutedUICommand -ArgumentList $CommandDesc,$CommandName,([Type]"System.Object"),$GestColl #Create Command Binding $CommandBind = New-Object System.Windows.Input.CommandBinding -ArgumentList $Command,$Exec,$CanExec #Add to target $CommandBindTarget.CommandBindings.Add($CommandBind) | Out-Null #Add Key Binding to target if ($KeyBindTarget) { $KeyBindTarget.Command = $Command } } #------------------------------- End Region Helper Functions -------------------- #------------------------------- Begin Region Admin Check ----------------------- $noAdminMsg = "This script is not being run with Administrator privileges. ` This may cause it to fail when making changes to the database. ` If you choose 'Yes', you will be prompted for Administrator credentials. ` Do you want to continue?" $noAdminTitle = "Administrator Privileges Required" #Check for admin if (!([System.Security.Principal.WindowsPrincipal][System.Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(` [System.Security.Principal.WindowsBuiltInRole]::Administrator)) { $dialog = Create-MessageBox -Message $noAdminMsg -Title $noAdminTitle -Buttons YesNo switch ($dialog) { "Yes" { #$adminCred = Get-Credential Start-Process -FilePath PowerShell.exe -Verb RunAs -ArgumentList "-WindowStyle Hidden", "-File $PSCommandPath" exit } "No" { exit } } } #------------------------------- End Region Admin Check ------------------------- #------------------------------- Begin Region Setup CredSSP and Remote Sesssion - #Test for Remote Computer to use for Active Directory commands if ($ComputerName -ne $env:COMPUTERNAME) { #Set flag to use for testing for remote session requirement $useRemote = $true #Test for CredSSP to turn it on if ($Authentication -eq "CredSSP") { #Turn on CredSSP server on management computer Invoke-Command -ScriptBlock {Enable-WSManCredSSP -Role Server -Force} -ComputerName chris-pc #Turn on CredSSP client to allow managment computer to delegate credentials Enable-WSManCredSSP -Role Client -DelegateComputer $ComputerName -Force $credSplat = @{Credential=(Get-Credential)} } #Create a new session using CredSSP to be used when running remote commands $session = New-PSSession -ComputerName $ComputerName -Authentication $Authentication @credSplat } #-------------------------------- End Region Setup CredSSP and Remote Session --- #------------------------------- Begin Region Load Collections ------------------- $obCollOUs = New-Object 'System.Collections.ObjectModel.ObservableCollection[System.Object]' $obCollUsers = New-Object 'System.Collections.ObjectModel.ObservableCollection[System.Object]' #To mitigate installing RSAT on every computer, connect to the management computer to use #its copy of the Active Directory PowerShell Module if the local computer is not the management computer $sb = {Get-ADOrganizationalUnit -Filter * | Where-Object {$_.Name -ne 'Domain Controllers'} | Select-Object -Property DistinguishedName, Name | Sort-Object -Property Name} #Check if a remote session is being used if ($useRemote) { #Use session created earlier to get the users from the management computer $result = Invoke-Command -Session $session -ScriptBlock $sb } else { #No remote session, invoke scriptblock $result = & $sb } foreach ($r in $result) {$obCollOUs.Add($r)} <# #> #-------------------------------- End Region Load Collections -------------------- #-------------------------- Begin Region XAML ----------------------------------------- [xml]$xaml = @' <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" Title="Active Directory Lockout Viewer" Height="350" Width="525"> <Window.Resources> <Style x:Key="ColumnsElementStyle"> <Setter Property="FrameworkElement.VerticalAlignment" Value="Center"/> <Setter Property="FrameworkElement.Margin" Value="5,2"/> </Style> <Style x:Key="TextColumnsStyle" BasedOn="{StaticResource ColumnsElementStyle}"> <Setter Property="FrameworkElement.HorizontalAlignment" Value="Left"/> </Style> <Style x:Key="CheckBoxColumnsStyle" BasedOn="{StaticResource ColumnsElementStyle}"> <Setter Property="FrameworkElement.HorizontalAlignment" Value="Center"/> </Style> </Window.Resources> <DockPanel> <Menu DockPanel.Dock="Top"> <MenuItem x:Name="MiHelp" Header="_Help"> <MenuItem x:Name="MiSubHelp" Header="_Help"/> <MenuItem x:Name="MiAbout" Header="_About"/> </MenuItem> </Menu> <ComboBox x:Name="CmbOU" DockPanel.Dock="Top" Margin="10,10,0,10" Width="200" HorizontalAlignment="Left" DisplayMemberPath="Name"/> <DataGrid x:Name="DtgUsers" AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="False" HorizontalGridLinesBrush="LightGray" VerticalGridLinesBrush="LightGray"> <DataGrid.Columns> <DataGridCheckBoxColumn Header="Locked" Binding="{Binding LockedOut}" ElementStyle="{StaticResource CheckBoxColumnsStyle}"/> <DataGridTextColumn Header="Account Name" Binding="{Binding SamAccountName}" ElementStyle="{StaticResource TextColumnsStyle}" IsReadOnly="True"/> <DataGridTextColumn Header="Name" Binding="{Binding DisplayName}" ElementStyle="{StaticResource TextColumnsStyle}" IsReadOnly="True"/> <DataGridTextColumn Header="Description" Binding="{Binding Description}" ElementStyle="{StaticResource TextColumnsStyle}" IsReadOnly="True"/> </DataGrid.Columns> </DataGrid> </DockPanel> </Window> '@ #About Window XAML [xml]$aboutXaml = @' <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="About Active Directory Lockout Viewer" ResizeMode="NoResize" SizeToContent="WidthAndHeight"> <Window.Resources> <Style x:Key="{x:Type Hyperlink}" TargetType="{x:Type Hyperlink}"> <Setter Property="Foreground"> <Setter.Value> <SolidColorBrush Color="White"/> </Setter.Value> </Setter> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Foreground"> <Setter.Value> <SolidColorBrush Color="WhiteSmoke"/> </Setter.Value> </Setter> </Trigger> </Style.Triggers> </Style> </Window.Resources> <StackPanel Width="292"> <DockPanel> <Image Source="" Width="48" Height="48" Margin="25,20,15,20" Name="AboutIcon" /> <TextBlock TextWrapping="Wrap" Text="Active Directory Lockout Viewer" FontSize="20" Margin="0" VerticalAlignment="Center" Name="AboutHeaderTitle"> <TextBlock.Foreground> <SolidColorBrush Color="#34495e"/> </TextBlock.Foreground> </TextBlock> </DockPanel> <StackPanel Background="LightBlue"> <TextBlock TextWrapping="Wrap" Margin="25,20,0,20" FontSize="14"> <TextBlock.Foreground> <SolidColorBrush Color="White"/> </TextBlock.Foreground> <Run Text="ADLockoutViewer.ps1"/><LineBreak/> <Run FontSize="12" Text="Version: 1.1"/><LineBreak/> <Run FontSize="12" Text="License: TechNet"/><LineBreak/> <Run FontSize="12" Text="© 2016 "/> <Hyperlink x:Name="ProfileLink" NavigateUri="https://social.technet.microsoft.com/profile/chris%20carter%2079/">Chris Carter</Hyperlink> </TextBlock> </StackPanel> </StackPanel> </Window> '@ #Help Window XAML [xml]$helpXaml = @' <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Help" Height="350" Width="366" ResizeMode="CanResize" MinWidth="366" MaxWidth="500" MinHeight="353" MaxHeight="600"> <DockPanel HorizontalAlignment="Left" VerticalAlignment="Top"> <DockPanel DockPanel.Dock="Top"> <Image Source="" Width="32" Height="32" Margin="25,25,10,15" Name="HelpHeaderIcon" /> <TextBlock TextWrapping="Wrap" Text="Active Directory Lockout Viewer Help" VerticalAlignment="Center" FontSize="16" Foreground="#34495e" Name="HelpHeaderTitle"/> </DockPanel> <ScrollViewer x:Name="SVHelp" VerticalScrollBarVisibility="Auto" DockPanel.Dock="Top"> <DockPanel Margin="0,0,0,25"> <TextBlock TextWrapping="Wrap" Margin="25,0,25,5" Text="Description" FontSize="14" FontWeight="Bold" DockPanel.Dock="Top"/> <TextBlock TextWrapping="Wrap" Margin="25,0,25,10" Text="The Active Directory Lockout Viewer is a PowerShell script that is used to view Active Directory users' lockout status, and unlock them if desired." DockPanel.Dock="Top"/> <TextBlock TextWrapping="Wrap" Margin="25,0,25,5" Text="Unlocking Users" FontSize="14" FontWeight="Bold" DockPanel.Dock="Top"/> <TextBlock TextWrapping="Wrap" Margin="25,0,25,10" DockPanel.Dock="Top" Text="Unchecking the box beside a user will unlock the user. There is no command to lock a user, so checking the box beside a user will not do anything, but unchecking it again would send the unlock command again."></TextBlock> </DockPanel> </ScrollViewer> </DockPanel> </Window> '@ #------------------------------ End Region XAML --------------------------------- #------------------------------- Begin Region Event Handlers --------------------- Function On-LockedCheckBoxUnchecked { $source = $this.DataContext #Check if remote session is being used if ($useRemote) { #Use the previous session to connect to the server through the management computer Invoke-Command -Session $session -ScriptBlock {Unlock-ADAccount -Identity $args[0]} -Args $source.SamAccountName } else { #No remote session, run locally Unlock-ADAccount -Identity $source.SamAccountName } } Function On-CmbSelectionChanged { #Clear the collection of previous entries $obCollUsers.Clear() $sb = {Get-ADUser -Filter * -SearchBase $args[0].SelectedItem.DistinguishedName -Properties * | Where-Object {$_.Enabled} | Select-Object -Property LockedOut, SamAccountName, DisplayName, Description, Enabled | Sort-Object -Property SamAccountName } #Check if remote session is being used if ($useRemote) { #Use session created earlier to get the users from the server through the management computer $result = Invoke-Command -Session $session -ScriptBlock $sb -Args $this } else { #No remote session, invoke scriptblock $result = & $sb $this } #Load the users into the observable collection foreach ($r in $result) {$obCollUsers.Add($r)} } #About Window Event Handlers Function On-ProfileRequestNavigate { [System.Diagnostics.Process]::Start($_.Uri.ToString()) } #Modal Dialog Creation Functions Function New-AboutDialogBox { #Load About Window $aboutXmlReader = New-Object System.Xml.XmlNodeReader -ArgumentList $aboutXaml $AboutWindow = [System.Windows.Markup.XamlReader]::Load($aboutXmlReader) $AboutWindow.Owner = $this $AboutWindow.WindowStyle = "ToolWindow" #Set Big Icon for Presentation #Remove Title Bar Icon $BigIcon = $AboutWindow.FindName("AboutIcon") $HeaderTitle = $AboutWindow.FindName("AboutHeaderTitle") if ($icon) { $BigIcon.Source = $icon } else { $BigIcon.Visibility = "Collapsed" $HeaderTitle.Margin = New-Object System.Windows.Thickness -ArgumentList 25,20,0,20 } #About Window Controls $ProfileLink = $AboutWindow.FindName("ProfileLink") #About Window Evnts $ProfileLink.add_RequestNavigate({On-ProfileRequestNavigate}) $AboutWindow.ShowDialog() } Function New-HelpDialogBox { #Load Help Window $helpXmlReader = New-Object System.Xml.XmlNodeReader -ArgumentList $helpXaml $HelpWindow = [System.Windows.Markup.XamlReader]::Load($helpXmlReader) $HelpWindow.Owner = $this $BigIcon = $HelpWindow.FindName("HelpHeaderIcon") $HeaderTitle = $HelpWindow.FindName("HelpHeaderTitle") if ($icon) { $BigIcon.Source = $icon $HelpWindow.Icon = $icon } else { $BigIcon.Visibility = "Collapsed" $HeaderTitle.Margin = New-Object System.Windows.Thickness -ArgumentList 25,25,0,15 } $HelpWindow.ShowDialog() } #------------------------------- End Region Event Handlers ----------------------- #-------------------------------- Begin Region Load Window and UI Components ----- #Read and Load the XAML definition to get the Window object $xmlReader = New-Object System.Xml.XmlNodeReader -ArgumentList $xaml $Window = [System.Windows.Markup.XamlReader]::Load($xmlReader) $MenuAbout = $Window.FindName("MiAbout") $MenuHelp = $Window.FindName("MiSubHelp") $DtgUsers = $Window.FindName("DtgUsers") $DtgUsers.ItemsSource = $obCollUsers $CmbOu = $Window.FindName("CmbOU") $CmbOu.ItemsSource = $obCollOUs $CmbOu.add_SelectionChanged({On-CmbSelectionChanged}) #-------------------------------- End Region Load Window and UI Components ------- #-------------------------------- Begin Region Attach Event Handlers ------------- #Construct an EventSetter for the Report DataGrid's CheckBox column and add Unchecked event handler #Unchecked Event Setter creation $evSetterUnchkd = New-Object System.Windows.EventSetter $evSetterUnchkd.Event = [System.Windows.Controls.CheckBox]::UncheckedEvent $evSetterUnchkd.Handler = [System.Windows.RoutedEventHandler]{On-LockedCheckBoxUnchecked} #Add EventSetters to Style and add Style to CheckBox column $chkBoxCellStyle= New-Object System.Windows.Style $chkBoxCellStyle.Setters.Add($evSetterUnchkd) $DtgUsers.Columns[0].CellStyle = $chkBoxCellStyle #-------------------------------- End Region Attach Event Handlers -------------- #-------------------------------- Begin Region Add Menu Commands and Key Bindings $AutoTrueCanExec = {$_.CanExecute = $true} #Command for About $AboutExec = {New-AboutDialogBox} Add-CommandAndKeyBinding $AutoTrueCanExec $AboutExec "A" ("Control","Shift") "" "About" "_About" $Window $MenuAbout #Command for Help $HelpExec = {New-HelpDialogBox} Add-CommandAndKeyBinding $AutoTrueCanExec $HelpExec "F1" "" "" "Help" "_View Help" $Window $MenuHelp #-------------------------------- End Region Add Menu Commands and Key Bindings - #Icon File if (Test-Path $IconPath) { $icon = New-Object System.Windows.Media.Imaging.BitmapImage -ArgumentList $IconPath } if ($icon) {$Window.Icon = $icon} #Start UI $Window.ShowDialog() | Out-Null #-----------------------Begin Region Remove Session and Disable CredSSP---------- #Remove remote session and Disable CredSSP if used if ($useRemote) { Remove-PSSession $session if ($Authentication -eq "CredSSP") { Disable-WSManCredSSP -Role Client Invoke-Command -ScriptBlock {Disable-WSManCredSSP -Role Server} -ComputerName $ComputerName } } |