我正在为我们公司构建一个登录脚本。它将执行的操作包括安装打印机、映射驱动器、设置壁纸等。其中许多操作都需要提升权限。其中许多操作还需要用户名(IE 不能以系统身份运行,必须具有用户上下文才能检查组等)。
我首先查看了 Intune/MEM(我们 100% 使用 M365/Azure,没有本地服务器)。您可以转到 AzureAD > 设备 > 脚本并应用脚本,但我的理解是,这只会运行一次,如果发生更改则再次运行,而不是像希望的那样在每次登录时都运行。
第二次我尝试了shell:common startup
。我很快意识到这会导致每次登录时都会出现 UAC 提示(进行注册表更改等)。不行。
然后我尝试了计划任务。这个方法很好,因为你可以以具有“最高权限”切换的用户身份运行它。我在这里遇到的问题是你必须在每个用户下创建任务。我真的需要这个适用于所有用户也未来的用户。例如,如果新用户登录会议室 PC,我需要它立即运行脚本。
所以,这就是我的立场。我有什么选择?有没有办法创建一个系统范围的计划任务,在用户上下文中每个用户登录时运行?我是否遗漏了 Microsoft Endpoint Management 中构成恰当的登录脚本?组策略?我认为startup
文件夹已经完全退出,但这里的选项也开放(但不想禁用 UAC)。
答案1
有一些软件产品允许在提升/管理员模式下运行特定命令。
示例产品(可能存在更多):
钢铁厂 (商业)
使用管理员帐户,同时隐藏使其运行所需的管理员密码。NirSoft AdvancedRun (免费)
更为精致的 RunAs 程序。
另一种解决方案可能是使用任务计划程序来计划以管理员身份运行脚本,但使用从未激活的触发器,并使用以下命令运行它:
schtasks /run /tn "task-name"
更多信息请参阅文章
Windows 7:无需 UAC 提示即可提升程序快捷方式 - 创建。
答案2
以下是基于@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 Drive already mapped if NOT EXIST "C:\Program Files\PowerShell\7\pwsh.exe" ( echo Installing PowerShell 7... start /wait msiexec.exe /i "p:\installs\PowerShell-7.1.3-win-x64.msi" /QN /L "c:\pwshinstall.log" echo Done. ) else ( echo Powershell already installed ) echo Copying files... xcopy "p:\!scripts\companynameLoginScript\ls.launch.ps1" "c:\companyname\scripts" /Y echo Done. 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
这实际上是在启动时运行的。它会下载更新的脚本并执行它,因为脚本本身很难更新。
<# Script: Loginscript Launcher Purpose: This script simply downloads the loginscript and executes Updated: 2021-10-08 #> #----------------------------------------------# #---- ENVIRONMENT/TOOLS SETUP -----------------# #----------------------------------------------# #$env:SEE_MASK_NOZONECHECKS = 1 #this is to avoid security warning prompts when launching tools like baretail $param1=$args[0] if ($param1 -eq "-d" -or $param1 -eq "-D") { $isDev = $true } #todo: Move to config file $logpath = "c:\companyname\logs\loginscript" $scriptpath = "c:\companyname\scripts\" $scripturl = "http://servername/ls.core.ps1" $logfile = "$(Get-Date -Format "yyyy-MM-dd HHmmss").log" #----------------------------------------------# #---- FUNCTIONS -------------------------------# #----------------------------------------------# 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 "ERROR: ($statuscode) COULD NOT UPDATE. Using local script." } else { log "ERROR: Unable to update script and no local script found. Exiting." exit } } } #----------------------------------------------# #---- MAIN CODE BLOCK -------------------------# #----------------------------------------------# if ($isDev) { log "Running in development mode..." } createFolder $logpath createFolder $scriptpath #update scripts updateScripts #execute core loginscript & "$($scriptpath)\ls.core.ps1"
ls.core.ps1
这真实的登录脚本。目前它只是设置公司壁纸并更改 Microsoft Teams 中的设置。它还会记录到标准位置,以便轻松检查问题。最终它将记录到日志聚合系统。
<# Script: ls.core Purpose: core Login script for companyname #> #----------------------------------------------# #---- ENVIRONMENT/TOOLS SETUP -----------------# #----------------------------------------------# $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) #----------------------------------------------# #---- FUNCTIONS -------------------------------# #----------------------------------------------# 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 "ERROR: Could not download $source ($statuscode)" } } function setLockScreenImage() { #TODO: Check if lockscreen already set try { log "Setting Lock Screen ($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 "ERROR: Could not apply registry changes ($statuscode)" } } function setWallpaperImage() { #TODO: Check if wallpaper already set try { log "Setting Wallpaper ($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 "ERROR: Could not apply registry changes ($statuscode)" } } function updateTeams() { $configFile = "$appdata\Microsoft\Teams\desktop-config.json" if (Test-Path -Path $configFile) { #get current value $teamsConfig = (Get-Content $configFile -Raw) | ConvertFrom-Json $isOpenAsHidden = $teamsConfig.appPreferenceSettings.psobject.properties.Where({$_.name -eq "openAsHidden"}).value #only update the file if it's not already set if (!$isOpenAsHidden) { log "Updating $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 "ERROR: Could not update Teams settings ($statuscode)" } & $appdata\Microsoft\Teams\Update.exe --processStart "Teams.exe" } else { log "Teams already set to start hidden" } } else { log "ERROR: Teams config file not found ($configFile)" } } #----------------------------------------------# #---- MAIN CODE BLOCK -------------------------# #----------------------------------------------# log "$user has logged in" if(-!(Test-Path $wppath)) { downloadFile $wpurl $wppath } if(Test-Path $wppath) { setLockScreenImage SetWallpaperImage } updateTeams log "Login script complete"