Remotely locking a workstation is not working, why? - powershell

So about a year ago, I discovered this PowerShell script:
Function Lock-Workstation
{
param(
$Computername,
$Credential
)
if(!(get-module taskscheduler)){Import-Module TaskScheduler}
New-task -ComputerName $Computername -credential:$Credential |
Add-TaskTrigger -In (New-TimeSpan -Seconds 30) |
Add-TaskAction -Script `
{
$signature = #"
[DllImport("user32.dll", SetLastError = true)]
public static extern bool LockWorkStation();
"#
$LockWorkStation = Add-Type -memberDefinition $signature `
-name "Win32LockWorkStation" `
-namespace Win32Functions `
-passthru
$LockWorkStation::LockWorkStation() | Out-Null
} | Register-ScheduledTask TestTask -ComputerName $Computername `
-credential:$Credential
}
I cannot get it working. I get all kinds of weird errors. It starts with errors about not being able to find the terminating "#, and once I get that fixed it starts throwing errors in the TaskScheduler module, specifically that it cannot load the type [__ComObject] (which is used in a couple of the TaskScheduler scripts, and I can't find any documentation on it).
I am trying to get this working in PowerShell v2.
Anyone got any ideas?
EDIT 1:
So I've done some more testing, and it appears that technically it is working (there was a typo in the TaskScheduler module that was causing it to fail completely), but despite the task being scheduled on the remote workstation, the execution of that task fails, kinda. Frequently the task will "run" but with no results, despite having the credentials of the currently logged on user.
EDIT 2:
Downvotes? Really? I'm having issues, I've described the errors I am getting, and rather than offer a suggestion you downvote the question? What is this, Reddit?

can't test your code at the moment but you may want to use a simpler approach (not tested):
... | Add-TaskAction -Script { rundll32.exe user32.dll,LockWorkStation }

Related

Start-Job connecting to Exchange Online

I need to run parallel Search-Mailbox cmdlets against 100's mailboxes to delete the content but they need to fit certain parameters first like certain CAS protocols enabled and a forwarding address present. I've also parameterised it so I can pass a $maxJobCount int to it so the runner can specify a maximum number of concurrently running jobs to allow so as to account for resources on their machine.
Got the thing working then got to the start-job component which is a pretty simple function.
function _StartJob {
param (
$mailAddress
)
Start-Job -Name $mailAddress -Scriptblock {
Get-EXOMailbox $mailAddress -PropertySets Delivery
}
}
That's returning an error saying I need to run Connect-ExchangeOnline before using the cmdlets which is where I learned script blocks in Start-Job are actually new PowerShell.exe processes so don't inherit modules and session options.
Does anyone know an easier way around this? In an MFA environment, it either means sitting there and pasting the password in a few hundred times or convincing the Change board and Secops dept to let me setup a graph application with delete rights... both painful
Thanks for any advice
You just have to pass in the creds into the block however you want.
$kvCertName = 'Cert'
#I am using azure automation here to get the cert its different for keyvault
$kvCertPFX = Get-AutomationCertificate -Name $kvCertName
$tenantid = 'yourcompany.onmicrosoft.com'
$appid = '00000000-46da-6666-5555-33333cfe77ec'
$startDate = ([datetime]::Today).AddDays(-7)
#Build the script block
$block = {
Param(
$kvCert,
$appID,
$tenantID,
$n,
$startdate
)
$newCertPFX = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($kvCert)
Connect-ExchangeOnline -Certificate ([System.Security.Cryptography.X509Certificates.X509Certificate2]$newCertPFX) -AppID $appID -Organization $tenantID -ErrorAction Stop
Search-AdminAuditLog -StartDate $startDate.adddays($n) -EndDate $($startDate.AddDays($n) | get-date -Hour 23 -Minute 59 -Second 59) -ExternalAccess:$false -ResultSize 250000
Disconnect-ExchangeOnline -confirm:$false
}
#Remove all jobs created
Get-Job | Remove-Job
#Run All the Parrallel Jobs
$num = 0..6
$kvCert = $kvCertPFX.Export(3)
foreach($n in $num){Start-Job -Scriptblock $Block -ArgumentList #($kvCert,$appID,$tenantid,$n,$startdate)}
#Wait for all jobs to finish.
do {start-sleep 1}
until ($(Get-Job -State Running).count -eq 0)
#Get information from each job.
$adminPowerShellAuditLog = $null
foreach($job in Get-Job){$adminPowerShellAuditLog+= Receive-Job -Id ($job.Id)}
Write-Output $adminPowerShellAuditLog

Running few Powershell background jobs in the same time, the result is not as expected

I tried to run few Azure cmdlets in background (i tried it by switch parameter "-asJob" and by with Start-Job cmdlet), i push cmdlet operations to the hashtable with certian keys and then iterate running jobs.
The problem is that all cmdlet change state to "Completed" in the same time, even if some job in hashtable actually ended by "Completed".
Feels like that all jobs end with last ending job.
Below i write some example code with problem
$vmssInstances = Get-AzVmssVM -ResourceGroupName $(agentPoolName) -VMScaleSetName $(agentPoolName)
$groupedVmssInstancesByComputerName = #{}
foreach($vmssInstance in $vmssInstances)
{
$groupedVmssInstancesByComputerName[$vmssInstance.OsProfile.ComputerName] = #{vmmsInstance = $vmssInstance; agents = #(); isFullyUpdated = $false}
}
foreach($key in $groupedVmssInstancesByComputerName.Keys)
{
$vmssInstance = $groupedVmssInstancesByComputerName[$key]["vmmsInstance"]
Write-Host "Trying to reimage instance $($vmssInstance.InstanceId)..."
$groupedVmssInstancesByComputerName[$key]["reimageOperation"] = Set-AzVmssVM -Reimage -InstanceId $vmssInstance.InstanceId -ResourceGroupName $(agentPoolName) -VMScaleSetName $(agentPoolName) -AsJob
}
while($true)
{
Get-Job
Start-Sleep -Seconds 10
}
I cant understand what is going on.
Maybe i dont know some features of powershell.
Please help me, guyes!
Try using Wait-Job - Suppresses the command prompt until one or all of the PowerShell background jobs running in the session are completed.
Get-Job | Wait-Job
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/wait-job?view=powershell-6

Import-PSSession - throwing credentials-mask

I got a problem with a powershell-script used in our domain to create new users:
Helpdesk will call a .bat as administrator, this bat calls a script-file to automate the creation. In this script, two sessions are created and imported, to use the Exchange- and AD-cmdlets locally.
During/after the import a second/third credential-mask gets thrown, but clicking "cancel" will do nothin, the script will run through without any issues. Nevertheless this annoys the helpdesk..
When running the .ps1 directly from the ISE, the mask won't be shown. Also, when C&Ping the Create-/Import part of the script to a new file and calling it the same way as before also won't show these mask..
Here a part of the .ps1-file:
<#
.DESCRIPTION
Creates a new standard user
.NOTES
Requires : Exchange 2016 Remote Session
Req.OS Version : not tested
Req.PS Version : not tested
.EXAMPLE
Create-User.ps1 -datapath \\path\to\userdata.csv -credentialobject $cred
#>
Param (
[string]$datapath, <#Folder where the CSVs sit #>
[System.Management.Automation.CredentialAttribute()]$credentialobject = $null
)
#region SET global var definitions
$ErrorActionPreference = "Continue"
Start-Transcript -path $ScriptLogPath # | out-null
#endregion
#region SET var definitions
$userfile = "$datapath\userdata.txt"
$groupfile = "$datapath\groupdata.txt"
#Exchange
$MSXremotingserver = "exchangehostname"
$MSXdatabasenames = #("msx_db")
#AD
$domaincontroller = "dchostname"
$ADremotingserver = $domaincontroller
$BaseDN = "OU=Users,DC=domain,DC=local"
#endregion
#region Import Userdata
# CSV's are getting imported here - WORKING
#endregion
#region INIT Remotesession
#Get AD Creds / use given AD Creds
if (($credentialobject -ne $null) -and (($credentialobject.GetType()).name -eq "PSCredential")){
$UserCredential = $credentialobject
}else{
$UserCredential = Get-Credential
# Get credentials to create the remote-sessions. Seems to be working.
}
$MSXSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionURI http://$MSXremotingserver/powershell -Credential $UserCredential
echo "import..."
$null = Import-PSSession $MSXSession -AllowClobber -DisableNameChecking # | out-null
# After the import (Progress bars running through on top of the PS) another credential-mask appearing, "Cancel" makes the script run through without further errors.
echo "OK"
$ADSession = New-PSsession -Computername $ADremotingserver -Credential $UserCredential
Invoke-Command -Command {Import-Module ActiveDirectory -DisableNameChecking} -Session $ADSession # | out-null
echo "import..."
Import-PSSession -Session $ADSession -Module "ActiveDirectory" -Prefix Remote -AllowClobber -DisableNameChecking # | out-null
# After the import (Progress bars running through on top of the PS) another credential-mask appearing, "Cancel" makes the script run through without further errors.
echo "OK"
#AD-user already existing?
if ([bool](get-remoteaduser -LDAPFilter "(SamAccountName=$($userdata.Kuerzel))")){
#Throw custom error - AD-User bereits vorhanden!
}
#build Account...
# AD-user and Mailbox are created and configured. WORKING!
#endregion
#region END Script
Get-PSSession | Remove-PSSession
Stop-Transcript
Write-Host "Beende Skript..."
start-sleep -Seconds 3
exit 10000
#endregion
And here's how the .ps1 is being called:
%systemroot%\System32\WindowsPowerShell\v1.0\powershell.exe -executionpolicy bypass -file \\helpdeskserver\powershell_userdata$\Create-User.ps1 \\helpdeskserver\path\to\csv"
I don't know what to do. Tried many different versions of each command, tried piping the in/output, nothing will do..
Google doesn't seem to know that behaviour, neither anyone here on Stackoverflow..
Thanks for any tips and help, I'll apprechiate!
Regards, Ting3l
Edit: When starting the .bat-file without administrative rights (Or with right-click -> other user.. -> admin-account) the second/third credential-dialog won't appear, instead I get an "Index out of range"-exception.
Maybe it's unimportant, but you can try to exit the session by Exit-PSSession. After that use exit 1000. Becoaser when you use exit in the session it completes the session (wherein all code after will be ignored, but script will have successful completed)

Powershell remote script error Error during CryptAcquireContext

I just run a simple script with invoke-command -computer [servername] -scriptblock {powershell.exe D:\test\script.ps1}
If I run the script manually in the box and then run the remote script again the error does not appear anymore but I don't like having to login manually to the box and run the script to be able fix this error especially with so many servers. Can anyone help me on this. Thanks
Error during CryptAcquireContext. [servername] :
Error msg : The requested operation cannot be completed. The computer must be trusted for delegation and the current user account must be configured to allow delegation.
Error code : 80090345
The script running on the server that gets the error part
$fciv = "D:\test\fciv.exe"
$fcivLog = "D:\test\fcivLog.txt"
$xmlPath = "D:\test\server.xml"
& $fciv -v -bp "\\servername\folder1" -XML $xmlPath | Out-File $fcivLog
Here is a PowerShell function, that should work on PowerShell version 2.0, to calculate MD5 hashes:
function Get-MD5FileHash {
[CmdletBinding()]
param (
[string] $Path
)
$MD5 = [System.Security.Cryptography.MD5]::Create();
$Stream = [System.IO.File]::OpenRead($Path);
$ByteArray = $MD5.ComputeHash($Stream);
[System.BitConverter]::ToString($ByteArray).Replace('-','').ToLower();
$Stream.Dispose();
}
Get-MD5FileHash -Path C:\test\test.xlsx;
I tested it out on PowerShell 4.0 on Windows 8.1, and it works great!
This question is quite old, and a work around has been found. But it still does not resolve the primary issue of delegation for programs using CryptAcquireContext
I had the very same problem with another program (BiosConfigUtility, from HP).
I solved it by allowing delegation between my computer, and remote computers.
To enable delegation on your client :
Enable-WSManCredSSP -Role Client -DelegateComputer host.domain.com -Force
To enable delegation on the remote computer :
Enable-WSManCredSSP -Role Server –Force
See this post : https://devblogs.microsoft.com/scripting/enable-powershell-second-hop-functionality-with-credssp/ for more info
You can always use scheduled tasks instead. This script changes the bios from legacy to uefi boot using biosconfigutility64 (or erase setup password for surplusing). Remotely running it directly will give that cryptacquirecontext error.
# usage: .\hpuefi.ps1 comp1,comp2,comp3
$s = new-pssession $args[0]
$src = 'Y:\hp-bios-new'
$dst = 'c:\users\admin\documents\hp-bios-new'
icm $s { if (! (test-path $using:dst)) { mkdir $using:dst > $null } }
$s | % { copy $src\biosconfigutility64.exe,$src\pass.bin,$src\uefi.bat,$src\uefi.txt $dst -tosession $_ }
icm $s {
# ERROR: Error during CryptAcquireContext. LastError = 0x80090345
# & $using:dst\uefi.bat
# 2>&1 must go last
$action = New-ScheduledTaskAction -Execute 'cmd' -argument '/c c:\users\admin\documents\hp-bios-new\uefi.bat > c:\users\admin\documents\hp-bios-new\uefi.log 2>&1'
Register-ScheduledTask -action $action -taskname uefi -user system > $null
Start-ScheduledTask -TaskName uefi
# wait
while ((Get-ScheduledTask -TaskName uefi).State -ne 'Ready') {
Write-Verbose -Message 'Waiting on scheduled task...' }
Get-ScheduledTask uefi | Get-ScheduledTaskInfo | ft
# remove-scheduledtask uefi
# shutdown /r /t 0
}
uefi.bat:
%~dp0BiosConfigUtility64.exe /set:"%~dp0uefi.txt" /cspwdfile:"%~dp0pass.bin"
exit /b %errorlevel%

Can this be made to work on a remote computer?

I've been killing myself trying to get this to work on a remote computer, is it even possible? If so, can someone point me in the right direction?
Here's the code:
Function Lock-WorkStation {
#Requires -Version 2.0
$signature = #"
[DllImport("user32.dll", SetLastError = true)]
public static extern bool LockWorkStation();
"#
$LockWorkStation = Add-Type -memberDefinition $signature -name "Win32LockWorkStation" -namespace Win32Functions -passthru
$LockWorkStation::LockWorkStation() | Out-Null
}
I can't test here, but for me it can NOT work because, as you can read in Microsoft documentation, the LockWorkStation function is callable only by processes running on the interactive desktop. In addition, the user must be logged on.
So when you connect to a remote computer using PSSession as far as I understand you are not in the interactive session.
Nothing to do with this, but it can help in Windows Vista/7 2008/R2, you can use the command tsdiscon.exe to lock a Remote Desktop session or your workstation.
Here is a sample where, logged as adminstrator domain on my computer, I first list, then lock the console session on my server.
PS> query session /server:WM2008R2ENT
SESSION UTILISATEUR ID ÉTAT TYPE PÉRIPHÉRIQUE
services 0 Déco
console jpb 2 Actif
PS> tsdiscon 2 /server:WM2008R2ENT
It's possible. But you need a workaround to connect to the interactive session.
Download the PowerShellPack and install it. You only need one module called "TaskScheduler".
I've tested the following code:
Function Lock-Workstation
{
param(
$Computername,
$Credential
)
if(!(get-module taskscheduler)){Import-Module TaskScheduler}
New-task -ComputerName $Computername -credential:$Credential |
Add-TaskTrigger -In (New-TimeSpan -Seconds 30) |
Add-TaskAction -Script `
{
$signature = #"
[DllImport("user32.dll", SetLastError = true)]
public static extern bool LockWorkStation();
"#
$LockWorkStation = Add-Type -memberDefinition $signature `
-name "Win32LockWorkStation" `
-namespace Win32Functions `
-passthru
$LockWorkStation::LockWorkStation() | Out-Null
} | Register-ScheduledTask TestTask -ComputerName $Computername `
-credential:$Credential
}
You can use it like this:
Lock-Workstation "NameOfTheComputer" (Get-Credential)
or like this:
Lock-Workstation "NameOfTheComputer"
If you receive an error in Connect-ToTaskScheduler when specifying a credential, it's because there is a typo in the module (edit Connect-ToTaskScheduler.ps1 and replace "$NetworkCredentail.Domain," with "$NetworkCredential.Domain,"