Вопрос или проблема
Я разрабатываю скрипт входа для нашей компании. Некоторые из задач, которые он будет выполнять, включают установку принтеров, маппинг дисков, установку обоев и т.д. Многие из этих задач потребуют повышенных привилегий. Также для многих из этих задач потребуется имя пользователя (например, не может быть запущено от имени системы, необходимо наличие контекста пользователя для проверки групп и т.д.).
Сначала я посмотрел на Intune/MEM (мы на 100% используем M365/Azure, без локальных серверов). Вы можете перейти в AzureAD > Устройства > Скрипты и применить скрипт, но, насколько я понимаю, это выполняется только один раз, а затем снова только в случае изменения, а не при каждом входе, как хотелось бы.
Затем я попробовал shell:common startup
. Я быстро осознал, что это приведет к появлению запроса UAC при каждом входе (внося изменения в реестр и т.д.). Это не сработает.
Потом я попробовал создать запланированное задание. Это хорошо работает, так как вы можете выполнить его от имени пользователя с переключателем “высшие привилегии”. Проблема, с которой я столкнулся, заключается в том, что вам нужно создать задачу для каждого пользователя. Мне действительно нужно, чтобы это применялось ко всем пользователям также как к будущим пользователям. Например, если новый пользователь входит в компьютер конференц-зала, мне нужно, чтобы скрипт выполнялся немедленно.
Вот в чем я сейчас нахожусь. Каковы мои варианты? Есть ли способ создать запланированное задание для всей системы, которое будет выполняться при входе каждого пользователя в контексте пользователя? Я не упускаю ли что-то в Microsoft Endpoint Management, что составляет правильный скрипт входа? Групповая политика? Я думаю, что папка startup
полностью не подходит, но также открыт для других вариантов (хотя не хочу отключать UAC).
Существуют программные продукты, которые позволяют выполнять конкретные команды в режиме повышенных прав/администратора.
Примеры продуктов (может быть больше):
-
Steel Run As (коммерческий)
Использует учетную запись администратора, скрывая требуемый пароль администратора, необходимый для его работы. -
NirSoft AdvancedRun (бесплатный)
Более продвинутая программа RunAs.
Еще одно решение может заключаться в использовании Планировщика задач для запланированного выполнения скрипта от имени администратора, но с триггером, который никогда не активируется, и запуском его с командой:
schtasks /run /tn "task-name"
Для получения дополнительной информации смотрите статью
Windows 7: Создание ярлыка программы с повышенными правами без запроса UAC.
Вот базовое решение, основанное на ответе @harrymc: (Отформатировано для удобства чтения)
schtasks.exe /Create
/SC ONLOGON
/RL HIGHEST
/ru "BUILTIN\Users"
/TN "companyname\Login Script"
/TR "'C:\Program Files\PowerShell\7\pwsh.exe'
-windowstyle hidden
-noninteractive
-ExecutionPolicy Bypass
-File C:\companyname\scripts\ls.launch.ps1"
/F
Вот полное решение, которое я реализовал, если кому-то интересно. Похоже, что оно работает хорошо. Мы используем политики Azure для большинства задач, но приятно иметь это запущенным на каждом компьютере на случай, если это будет нужно.
ls.install.bat
Устанавливает PowerShell, копирует необходимые скрипты и устанавливает запланированное задание.
@echo off IF NOT EXIST c:\companyname mkdir c:\companyname IF NOT EXIST c:\companyname\scripts mkdir c:\companyname\scripts IF NOT EXIST c:\companyname\logs mkdir c:\companyname\logs IF NOT EXIST p:\!scripts\ net use p: \\10.1.0.20\public /user:asdf asdf IF EXIST p:\!scripts\ echo Диск уже замаплен if NOT EXIST "C:\Program Files\PowerShell\7\pwsh.exe" ( echo Установка PowerShell 7... start /wait msiexec.exe /i "p:\installs\PowerShell-7.1.3-win-x64.msi" /QN /L "c:\pwshinstall.log" echo Готово. ) else ( echo PowerShell уже установлен ) echo Копирование файлов... xcopy "p:\!scripts\companynameLoginScript\ls.launch.ps1" "c:\companyname\scripts" /Y echo Готово. Установка задачи... schtasks.exe /Create /SC ONLOGON /RL HIGHEST /ru "BUILTIN\Users" /TN "companyname\Login Script" /TR "'C:\Program Files\PowerShell\7\pwsh.exe' -windowstyle hidden -noninteractive -ExecutionPolicy Bypass -File C:\companyname\scripts\ls.launch.ps1" /F
ls.launch.ps1
Это то, что действительно запускается при старте. Он загружает обновленный скрипт и выполняет его, потому что было трудно сделать так, чтобы скрипт обновлял сам себя.
<# Скрипт: Лаунчер скрипта входа Цель: Этот скрипт просто загружает скрипт входа и выполняет его. Обновлено: 2021-10-08 #> #----------------------------------------------# #---- УСТРОЙСТВО/НАСТРОЙКА ПРИБОРОВ -----------# #----------------------------------------------# #$env:SEE_MASK_NOZONECHECKS = 1 #это для избежания предупреждений о безопасности при запуске инструментов, таких как baretail $param1=$args[0] if ($param1 -eq "-d" -or $param1 -eq "-D") { $isDev = $true } #todo: Переместить в файл конфигурации $logpath = "c:\companyname\logs\loginscript" $scriptpath = "c:\companyname\scripts\" $scripturl = "http://servername/ls.core.ps1" $logfile = "$(Get-Date -Format "yyyy-MM-dd HHmmss").log" #----------------------------------------------# #---- ФУНКЦИИ -------------------------------# #----------------------------------------------# Function log($message) { Write-Output "[$(Get-Date -Format "yyyy-MM-dd HHmmss")] $message" | Out-file "$($logpath)\$($logfile)" -append if ($isDev) { Write-Host "[$(Get-Date -Format "yyyy-MM-dd HHmmss")] $message" } } Function createFolder($path) { if (-!(Test-Path $path)) { New-Item -Type Directory -Path $path } } function updateScripts() { try { Invoke-WebRequest -uri $scripturl -OutFile "$($scriptpath)\ls.core.ps1" } catch { $statuscode = $_.Exception.Response.StatusCode Write-Host "StatusCode:" $statuscode if (Test-Path "$($scriptpath)\ls.core.ps1") { log "ОШИБКА: ($statuscode) НЕ УДАЛОСЬ ОБНОВИТЬ. Используется локальный скрипт." } else { log "ОШИБКА: Не удалось обновить скрипт и локальный скрипт не найден. Выход." exit } } } #----------------------------------------------# #---- ОСНОВНОЙ БЛОК КОДА --------------------# #----------------------------------------------# if ($isDev) { log "Запуск в режиме разработки..." } createFolder $logpath createFolder $scriptpath #обновить скрипты updateScripts #выполнить основной скрипт входа & "$($scriptpath)\ls.core.ps1"
ls.core.ps1
Это реальный скрипт входа. В настоящее время он просто устанавливает обои компании и меняет настройки в Microsoft Teams. Он также ведет логи в стандартное место, поэтому легко проверять наличие проблем. В конечном итоге он будет логироваться в систему агрегации логов.
<# Скрипт: ls.core Цель: основной скрипт входа для companyname #> #----------------------------------------------# #---- УСТРОЙСТВО/НАСТРОЙКА ПРИБОРОВ -----------# #----------------------------------------------# $user = (Get-CimInstance CIM_ComputerSystem).Username $wpfilename = "companyname_Wallpaper.jpg" $wppath = "c:\companyname\$wpfilename" $wpurl = "http://downloads.companyname.com/$wpfilename" $appdata = [Environment]::GetFolderPath([Environment+SpecialFolder]::ApplicationData) #----------------------------------------------# #---- ФУНКЦИИ -------------------------------# #----------------------------------------------# function isAdministrator { $user = [Security.Principal.WindowsIdentity]::GetCurrent() (New-Object Security.Principal.WindowsPrincipal $user).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) } Function log($message) { Write-Output "[$(Get-Date -Format "yyyy-MM-dd HHmmss")] $message" | Out-file "$($logpath)\$($logfile)" -append if ($isDev) { Write-Host "[$(Get-Date -Format "yyyy-MM-dd HHmmss")] $message" } } function downloadFile($source, $destination) { try { Invoke-WebRequest -uri $source -OutFile $destination } catch { $statuscode = $_.Exception.Response.StatusCode log "ОШИБКА: Не удалось загрузить $source ($statuscode)" } } function setLockScreenImage() { #TODO: Проверить, если экран блокировки уже установлен try { log "Установка экрана блокировки ($wppath)" REG ADD HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\PersonalizationCSP /f REG ADD HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\PersonalizationCSP /v LockScreenImagePath /t REG_SZ /d $wppath /f REG ADD HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\PersonalizationCSP /v LockScreenImageUrl /t REG_SZ /d $wppath /f REG ADD HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\PersonalizationCSP /v LockScreenImageStatus /t REG_DWORD /d 1 /f } catch { $statuscode = $_.Exception.Response.StatusCode log "ОШИБКА: Не удалось применить изменения реестра ($statuscode)" } } function setWallpaperImage() { #TODO: Проверить, если обои уже установлены try { log "Установка обоев ($wppath)" reg add "HKEY_CURRENT_USER\Control Panel\Desktop" /v Wallpaper /t REG_SZ /d $wppath /f rundll32.exe user32.dll, UpdatePerUserSystemParameters 1, True } catch { $statuscode = $_.Exception.Response.StatusCode log "ОШИБКА: Не удалось применить изменения реестра ($statuscode)" } } function updateTeams() { $configFile = "$appdata\Microsoft\Teams\desktop-config.json" if (Test-Path -Path $configFile) { #получить текущее значение $teamsConfig = (Get-Content $configFile -Raw) | ConvertFrom-Json $isOpenAsHidden = $teamsConfig.appPreferenceSettings.psobject.properties.Where({$_.name -eq "openAsHidden"}).value #обновить файл только если он еще не установлен if (!$isOpenAsHidden) { log "Обновление $configFile" Stop-Process -name "Teams" try { $currSettings = Get-Content $configFile $currSettings.replace('"openAsHidden":false', '"openAsHidden":true') | Set-Content $configFile } catch { $statuscode = $_.Exception.Response.StatusCode if (!$statuscode) { $statuscode = "UNKNOWN CODE"} log "ОШИБКА: Не удалось обновить настройки Teams ($statuscode)" } & $appdata\Microsoft\Teams\Update.exe --processStart "Teams.exe" } else { log "Teams уже настроен на скрытый запуск" } } else { log "ОШИБКА: Файл конфигурации Teams не найден ($configFile)" } } #----------------------------------------------# #---- ОСНОВНОЙ БЛОК КОДА --------------------# #----------------------------------------------# log "$user вошел в систему" if(-!(Test-Path $wppath)) { downloadFile $wpurl $wppath } if(Test-Path $wppath) { setLockScreenImage SetWallpaperImage } updateTeams log "Скрипт входа завершен"
Ответ или решение
Для запуска скрипта PowerShell при входе в систему для всех текущих и будущих пользователей с повышенными привилегиями без запроса UAC, вам подойдет использование планировщика задач Windows. Этот подход гарантирует, что скрипт будет выполняться в контексте пользователя и с необходимыми правами, не запрашивая подтверждение на каждом входе.
Шаги для реализации:
-
Создайте основной скрипт логина. Например, мы создаем PowerShell скрипт
ls.launch.ps1
, который будет выполнять ваши требования (например, установка драйверов принтеров, подключение сетевых дисков и т.д.). -
Создайте .bat файл для установки задач в планировщике. Данный файл будет содержать команды для создания задачи в планировщике, которая будет запускать ваш PowerShell скрипт при каждом входе пользователя.
-
Команда для создания задачи. Используйте следующую команду
schtasks.exe
для создания задачи:
schtasks.exe /Create /SC ONLOGON /RL HIGHEST /RU "BUILTIN\Users" /TN "companyname\Login Script" /TR "'C:\Program Files\PowerShell\7\pwsh.exe' -windowstyle hidden -noninteractive -ExecutionPolicy Bypass -File C:\companyname\scripts\ls.launch.ps1" /F
Пример .bat Скрипта:
@echo off
IF NOT EXIST c:\companyname mkdir c:\companyname
IF NOT EXIST c:\companyname\scripts mkdir c:\companyname\scripts
IF NOT EXIST c:\companyname\logs mkdir c:\companyname\logs
echo Copying files...
xcopy "p:\!scripts\companynameLoginScript\ls.launch.ps1" "c:\companyname\scripts" /Y
echo Installing task...
schtasks.exe /Create /SC ONLOGON /RL HIGHEST /RU "BUILTIN\Users" /TN "companyname\Login Script" /TR "'C:\Program Files\PowerShell\7\pwsh.exe' -windowstyle hidden -noninteractive -ExecutionPolicy Bypass -File C:\companyname\scripts\ls.launch.ps1" /F
Содержимое ls.launch.ps1
:
# Ваша логика выполнения, например:
& "C:\companyname\scripts\ls.core.ps1"
Преимущества использования этого подхода:
-
Автоматизация: Скрипт будет выполняться при каждом входе пользователя без необходимости ручной настройки для новых пользователей.
-
Гибкость: Вы можете обновлять внутренности
ls.launch.ps1
без необходимости изменения настроек планировщика задач. -
Управляемость: Все действия логируются, и вы можете видеть, что выполнился весь процесс.
Заключение
Данный подход позволяет эффективно и без дополнительных накладных расходов создавать централизованные логин-скрипты для вашей компании. Убедитесь, что тестируете этот скрипт на пилотной группе пользователей перед его широкомасштабным развертыванием, чтобы гарантировать, что все выполняется ожидаемо и без ошибок. Если в будущем потребуется изменить или расширить функционал, всегда можно легко модифицировать созданные PowerShell скрипты.
Эта методология также может быть дополнительно адаптирована под требования вашей корпоративной инфраструктуры, что делает ее универсальным решением для управления пользовательскими настройками и автоматизацией различных задач.