Поиск активных сессий и залогиненных пользователей с Powershell

Иногда может понадобится проверить какие в данный момент залогинены в системе, то есть являются активными. Такую информацию можно использовать для аудита, например для проверки какие учетные записи за какими компьютерами сидят или для последующей перезагрузки сервера, что бы не останавливать работу коллег. В примерах ниже рассмотрено как выполнять удаленные команды для получения активных пользователей и возврата включенных пользователей Active Directory.
Навигация по посту
- Получение имени залогиненного пользователя
- Получение списка компьютеров
- Удаленное получение залогиненных пользователей
- Получение включенных пользователей AD
- Whoami или WMI
Получение имени залогиненного пользователя
Я не могу вспомнить готовую команду Powershell, которая бы вернула логин пользователя, но такая возможность есть через WMI:
Get-WmiObject -Class Win32_ComputerSystem | Select-Object UserName
Есть еще вариант использовать CIM, который может работать немного быстрее:
Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object UserName

Обе команды могут работать удаленно если добавить атрибут ComputerName:
Get-WmiObject -ComputerName 'localhost' -Class Win32_ComputerSystem | Select-Object UserName

Если вы планируете выполнять эти команды удаленно, то может понадобится выполнить предварительные настройки в виде открытия портов и необходимых правах.
Получить только имя пользователя можно так:
$userinfo = Get-WmiObject -ComputerName 'localhost' -Class Win32_ComputerSystem $user = $userinfo.UserName -split '\\' $user[1]

Как вы знаете в Winodws есть так же параллельные сеансы сервисов. Если вам нужно вернуть имена этих аккаунтов нужно использовать класс «win32_LoggedOnUser»:
Get-CimInstance -ComputerName 'localhost' -Class win32_LoggedOnUser | ft

Получение списка компьютеров
Если у вас нет списка компьютеров к которым вы планируете подключиться и узнать активного пользователя — это можно сделать через AD. На примере ниже будут возвращены все компьютеры:
Get-ADComputer -Filter *

Операция по получению списка компьютеров может быть очень долгой, если у вас большой парк ПК в AD. Вы можете вернуть только компьютеры, которые не отключены в AD (Disable) следующим способом:
Get-ADComputer -Filter * | where Enabled -eq $True

Можно использовать и фильтрацию. Так я верну компьютеры имена которые начинаются на «CL»:
Get-ADComputer -Filter | where Enabled -eq $True

Получить список имен мы можем так:
$pc = Get-ADComputer -Filter | where Enabled -eq $True $pc.Name

Если у вас список компьютеров не относится к AD или имеет другой формат — то просто преобразуйте его в массив:
# Текст с именами компьютеров $pc = 'Computer1,Computer2,Computer3' # Преобразование в массив $pc_array = $pc -split ',' $pc_array
Удаленное получение залогиненных пользователей
Выше уже рассматривался вариант получения имени пользователя удаленно используя WMI и CIM. Если обе команды, то всех пользователей активных в данный момент мы можем получить так:
# Текст с именами компьютеров $computers = (Get-AdComputer -Filter *).Name # Удаленное получение пользователей foreach ($computer in $computers)
Такой подход может привести к ошибкам так как мы не проверяем включены ли компьютеры:

Мы можем просто не выводить ошибки с помощью «-ErrorAction SilentlyContinue» или заранее пинговать (что было бы правильнее с точки зрения времени выполнения). В примере ниже я так же разбиваю имя компьютера и логин в более удобный формат:
# Текст с именами компьютеров $computers = (Get-AdComputer -Filter *).Name # Удаленное получение имен учетных записей foreach ($computer in $computers) < $result = Get-CimInstance -ComputerName $computer -ClassName Win32_ComputerSystem -ErrorAction SilentlyContinue $computer_login = $result.UserName -split '\\' if ($computer_login)< Write-Host 'ComputerName: ' $computer_login[0] Write-Host 'UserName: ' $computer_login[1] >>

Если вы не хотите выполнять команды удаленно через WMI, то вы можете использовать PSRemoting. От так же требует предварительных настроек, которые описаны в статье «Удаленное управление через Powershell». Команда, которая использует PSRemoting, будет выглядеть примерно так же:
# Текст с именами компьютеров $computers = (Get-AdComputer -Filter *).Name # Удаленное получение имен учетных записей foreach ($computer in $computers) < $result = Invoke-Command -ComputerName $computer ` -ScriptBlock < Get-WMIObject -ClassName Win32_ComputerSystem ` -ErrorAction SilentlyContinue >` -ErrorAction SilentlyContinue $computer_login = $result.UserName -split '\\' if ($computer_login) < Write-Host 'ComputerName: ' $computer_login[0] Write-Host 'UserName: ' $computer_login[1] >>

Получение включенных пользователей AD
Если вам нужно вернуть учетные записи, которые включены в AD выполните следующую команду:
Get-AdUser -Filter * | where 'Enabled' -eq $True

Для возврата только отключенных учетных записей используйте $False.
Такой подход работает и с объектами компьютеров в AD:
Get-AdComputer -Filter * | where 'Enabled' -eq $False
Для последующей выгрузки данных в Excel почитайте статью «Как в Powershell выгрузить из AD пользователей и группы CSV».
Whoami или WMI
Вы можете вспомнить команду, которая так же возвращает имя пользователя:
whoami

Как можно увидеть она вернет ту же информацию, что и класс WMI. Ситуация меняется, когда эти команды используются удаленно:
# Пользователь вошедший в Windows и открывший Powershell whoami # Пользователь, который будет удаленно подключатся через Powershell $new_psuser = Get-Credential 'admin' # Способ с whoami Invoke-Command -ComputerName 'localhost' ` -Credential $new_psuser ` -ScriptBlock # Способ с WMI Invoke-Command -ComputerName 'localhost' ` -Credential $new_psuser ` -ScriptBlock

Как видно, в случае с whoami у нас вернулось имя учетной записи выполнившей команду Powershell, а с WMI пользователь Windows.
Windows: узнаём, кто где залогинен

Хорошо, если у вас есть инструмент а-ля BgInfo или ваши пользователи знают про шорткат Windows+Pause/Break и умеют его нажимать. Встречаются даже редкие экземпляры, которые успели выучить имя своей машины. Но часто у звонящего вдобавок к его основной проблеме появляется вторая: узнать имя/IP-адрес компьютера. И нередко на решение этой второй проблемы уходит куда больше времени, чем первой (а надо было всего лишь обои поменять или вернуть пропавший ярлык :).
А ведь намного приятнее услышать что-то вроде:
— Татьяна Сергеевна, не беспокойтесь, уже подключаюсь…
А надо для этого не так уж и много. Специалисту техподдержки достаточно лишь выучить наизусть имена машин и помнить, кто за какой работает.
Перед описанием решения, которым мы пользуемся сейчас, я кратко рассмотрю другие варианты, чтобы раскритиковать их в пух и прах объяснить свой выбор.
- BgInfo, Desktop Info и им подобные. Если много денег, есть и платные. Суть в том, что на десктоп выводится техническая информация: имя машины, IP-адрес, логин и т.д. В Desktop Info можно даже графики производительности запилить на половину экрана.
Не устраивает то, что для того же Bginfo, например, пользователю нужно сворачивать окна, чтобы увидеть нужные данные. Еще мы с коллегами не раз наблюдали у BgInfo характерный артефакт, когда новый текст выводится поверх старого.
Некоторых пользователей раздражает тот факт, что админырисуют пугающее 192.168.0.123 на мордочке растянувшегося на рабочем столе котикапортят эстетику фоновой картинки, и, разумеется, это жутко демотивирует и напрочь убивает рабочий настрой. - Ярлык а-ля «Кто я» (не пытайтесь добавить ему знак вопроса в конце :). Классический ярлык на рабочем столе, за которым прячется аккуратный или не очень скрипт, выводящий нужную информацию в виде диалогового окна. Иногда вместо ярлыка на рабочий стол кладут сам скрипт, что ИМХО моветон.
Недостаток в том, что для запуска ярлыка, как и в первом случае, нужно сворачивать все открытые окна (баловней судьбы, у которых на рабочей машине открыто единственное окно с пасьянсом, в расчет не берём). Кстати, а ваши пользователи знают, куда нужно тыкнуть, чтобы свернуть все окна?Правильно, пальцем в глаз админу.
Душу излил, а теперь к делу.
За основу была взята идея хабровчанина mittel из этой статьи.
Суть задумки в том, что при входе пользователя в Windows логон-скрипт заносит нужную информацию (время и имя машины) в определенный атрибут учётной записи пользователя. А при выходе из системы отрабатывает аналогичный логофф-скрипт.
- Групповая политика, в которой прописаны логон- и логофф-скрипты для пользователей, применяется ко всему домену, поэтому скрипты будут отрабатывать на любой машине, на которую логинятся пользователи. Если у вас наряду с рабочими станциями используются терминальные решения (например, Microsoft RDS или продукты Citrix), такой подход будет неудобным.
- Данные заносятся в атрибут Department учетной записи пользователя, на который у рядового пользователя есть доступ только на чтение. Помимо атрибута учётной записи пользователя, скрипт также вносит изменения в атрибут Department учётной записи компьютера, который по умолчанию пользователи также менять не могут. Поэтому чтобы решение работало, автор предлагает изменить стандартые настройки безопасности для объектов AD.
- Формат даты зависит от настроек локализации на конечной машине, поэтому с одной машины можем получить 10 ноября 2018 14:53, а с другой 11/10/18 2:53 p.m.
- GPO линкуется не к домену, а к OU с машинами (я разделяю пользователей и машины по разным OU и другим советую). При этом для loopback policy processing mode выставлен режим merge.
- Скрипт будет заносить данные только в учетную запись пользователя в атрибут Info, который пользователь может менять самостоятельно для своей учётной записи.
- Изменен кусок кода, генерирующий значение атрибута
Теперь скрипты выглядят так:
SaveLogonInfoToAdUserAttrib.vbs
On Error Resume Next Set wshShell = CreateObject("WScript.Shell") strComputerName = wshShell.ExpandEnvironmentStrings("%COMPUTERNAME%") Set adsinfo = CreateObject("ADSystemInfo") Set oUser = GetObject("LDAP://" & adsinfo.UserName) strMonth = Month(Now()) If Len(strMonth) < 2 then strMonth = "0" & strMonth End If strDay = Day(Now()) If Len(strDay) < 2 then strDay = "0" & strDay End If strTime = FormatDateTime(Now(),vbLongTime) If Len(strTime) < 8 then strTime = "0" & strTime End If strTimeStamp = Year(Now()) & "/" & strMonth & "/" & strDay & " " & strTime oUser.put "info", strTimeStamp & " " & " @ " & strComputerName oUser.Setinfo
SaveLogoffInfoToAdUserAttrib.vbs
On Error Resume Next Set wshShell = CreateObject("WScript.Shell") strComputerName = wshShell.ExpandEnvironmentStrings("%COMPUTERNAME%") Set adsinfo = CreateObject("ADSystemInfo") Set oUser = GetObject("LDAP://" & adsinfo.UserName) strMonth = Month(Now()) If Len(strMonth) < 2 then strMonth = "0" & strMonth End If strDay = Day(Now()) If Len(strDay) < 2 then strDay = "0" & strDay End If strTime = FormatDateTime(Now(),vbLongTime) If Len(strTime) < 8 then strTime = "0" & strTime End If strTimeStamp = Year(Now()) & "/" & strMonth & "/" & strDay & " " & strTime oUser.put "info", strTimeStamp & " " & " @ " & strComputerName oUser.Setinfo
Кто первым найдет все отличия между логон- и логофф-скриптом, тому плюс в карму. 🙂
Также для получения наглядной информации создан такой небольшой PS-скрипт:
Get-UsersByPCsInfo.ps1
$OU = "OU=MyUsers,DC=mydomain,DC=com" Get-ADUser -SearchBase $OU -Properties * -Filter * | Select-Object DisplayName, SamAccountName, info | Sort DisplayName | Out-GridView -Title "Информация по логонам" -Wait
Итого всё настраивается на раз-два-три:
- создаем GPO с нужными настройками и линкуем его к подразделению с рабочими станциями пользователей:

- идем пить чай (если AD с большим количеством пользователей, то чая нужно много 🙂
- запускам PS-скрипт и получаем результат:

В верхней части окна есть удобный фильтр, в котором можно отбирать данные по значениям одного или нескольких полей. Клик по столбцам таблицы сортирует записи по значениям соответствующих полей.

Можно красиво «упаковать» наше решение.
Для этого добавим ярлык для запуска скрипта специалистам техподдержки, у которого в поле «объект» будет что-то такое:
powershell.exe -NoLogo -ExecutionPolicy Bypass -File "\\server\share\Scripts\Get-UsersByPCsInfo.ps1"
Если сотрудников техподдержки много, то можно раздать ярлык с помощью GPP.

Несколько замечаний напоследок.
- На машине, откуда запускается PS-скрипт, должен быть установлен модуль Active Directory для PowerShell (для этого достаточно добавить средства администрирования AD в компонентах Windows).
- бОльшую часть атрибутов своей учётной записи пользователь по умолчанию редактировать не может. Учитывайте это, если решите использовать атрибут, отличный от Info.
- Проинформируйте всех причастных коллег о том, какой атрибут будете использовать. Например, тот же Info используется для интерактивного добавления заметок к ящику пользователя в админке Exchange Server и кто-то легко может его затереть, либо опечалился, когда добавленную им информацию затрет ваш скрипт.
- Если у вас несколько сайтов Active Directory, то делайте поправку на задержки репликации. Например, если вы хотите получить актуальную информацию о пользователях с AD-сайта A, а запускаете скрипт с машины из AD-сайта B, то можно сделать так:
Get-ADUser -Server DCfromSiteA -SearchBase $OU -Properties * -Filter * | Select-Object DisplayName, SamAccountName, info | Sort DisplayName | Out-GridView -Title "Информация по логонам" -WaitDCfromSiteA — имя контроллера домена сайта A (по умолчанию командлет Get-AdUser подключается к ближайшему контроллеру домена)
Буду признателен, если вы пройдете короткий опрос ниже.
Получаем информацию о последнем входе пользователя
Как системному администратору, отвечающему за Active Directory, мне иногда приходится отвечать на вопросы типа ″куда и когда заходил вот этот пользователь″. Чтобы иметь возможность быстро получить требуемую информацию, можно записывать данные о входе прямо в свойства пользователя. Для этого надо всего лишь определить имя компьютера и текущее время и занести эти данные в один из свободных атрибутов пользователя в AD, например в описание (Description).
Задача, на первый взгляд, простая и легко решаемая с помощью PowerShell. Но, скрипт будет выполняться на клиентских рабочих станциях, где использовать PowerShell модуль для работы с AD нельзя. Ведь его там просто нет Поэтому придется использовать подручные средства.
С помощью переменных окружения получаем из локальной системы данные о пользователе и компьютере.
$user = $env:USERNAME;
$computer = $env:COMPUTERNAME;
Для получения текущего времени воспользуемся статическим .Net классом DateTime:
Все это в виде строки помещаем в переменную $string.
$string = ″Logged on $computer at $time″;
Теперь нам надо найти в AD пользователя. Для поиска воспользуемся .Net классом Directory Searcher. Создаем объект DirectorySearcher:
$Searcher = New-Object DirectoryServices.DirectorySearcher;
Искать будем по всему домену, поэтому в качестве корневой директории поиска указываем имя домена:
Дополнительно задаем поиск по всем вложенным объектам:
Теперь создаем фильтр для поиска пользователя:
Используя результаты поиска, получаем из AD объект пользователя:
Помещаем в поле Description строку с полученной информацией и сохраняем изменения:
Сохраняем скрипт и с помощью групповых политик добавляем его в Logon Scripts. Теперь скрипт будет отрабатывать при каждом входе пользователя в систему, в результате получится что то вроде этого.
PowerShell: системное администрирование и программирование
Всё о PowerShell в Windows и на Linux. Системное администрирование Windows
Как в PowerShell узнать имя пользователя
В операционной системе Windows и Windows Server можно выделить различные типы пользователей:
- пользователь, выполнивший вход в систему
- зарегистрированный владелец установленной ОС и имя зарегистрированного пользователя операционной системы
- имеющиеся учётные записи в данной ОС
Рассмотрим, как получить имя каждого вида пользователя.
Как узнать имя пользователя, выполнившего вход
Самым очевидным кажется способ обратиться к переменной окружения:
$env:UserName
Дополнительно могут заинтересовать ещё парочка переменных окружения. В следующей содержится имя компьютера или имя домена, если компьютер является частью домена.
$env:UserDomain
А эта команда выведет имя компьютера:
$env:ComputerName
Недостаток этого метода в том, что пользователь может подделать значение переменной окружения %UserName%.
Ещё одним недостатком может быть то, что, возможно, вам нужно имя пользователя вида «ДОМЕН\ПОЛЬЗОВАТЕЛЬ» или «КОМПЬЮТЕР\ПОЛЬЗОВАТЕЛЬ».
Этого недостатка лишена следующая команда, которая выведет полное имя пользователя, вместе с компьютером или доменом, к которому данный пользователь относится:
[System.Security.Principal.WindowsIdentity]::GetCurrent().Name
Ещё одним способом получить имя пользователя в PowerShell является команда:
Get-ComputerInfo -Property CsUserName
CsUserName ---------- HACKWARE-WINDOW\MiAl
Если вы хотите более компактный вывод, то используйте следующий синтаксис:
(Get-ComputerInfo).CsUserName
HACKWARE-WINDOW\MiAl
Как вывести список всех пользователей Windows
Чтобы просмотреть список всех учётных записей, используйте командлет:
Чтобы показать только имена, используйте синтаксис:
(Get-LocalUser).Name
Чтобы вывести только активные учётные записи, используйте команду:
Get-LocalUser | Where-Object
Следующая команда покажет только имена активных учётных записей:
(Get-LocalUser | Where-Object ).Name
Владельцы и зарегистрированные пользователи Windows
Вы можете найти информацию о других видах пользователей:
Get-ComputerInfo -Property WindowsRegisteredOwner Get-ComputerInfo -Property CsPrimaryOwnerName Get-ComputerInfo -Property OsRegisteredUser
- WindowsRegisteredOwner — имя зарегистрированного владельца этой установки Windows из реестра Windows.
- CsPrimaryOwnerName — имя основного владельца системы
- OsRegisteredUser — имя зарегистрированного пользователя операционной системы
Универсальная команда whoami
Следующая команда покажет имя пользователя как в PowerShell, так и в CMD:
whoami
Имя пользователя она берёт из переменной окружения.
Write-Host "Текущий пользователь:" Write-Host $(whoami)
Ещё один пример:
Write-Host "Текущий пользователь: " -NoNewline; Write-Host $(whoami)