Running a Powershell command to another ws that requires administrator privileges - powershell

I'm required to run a TPM command (requires admin access).
Lets use these for the legend:
Angelo - Standard user
AngeloAdmin - Admin user
Windows7 - Computer that has a standard user in it
How can I use my standard account to run the script as an Administrator to execute a script to another computer with a standard account remotely?
Heres the partial code I will run:
Set-Variable -Name BuildLog -Scope Global -Force
Set-Variable -Name Errors -Value $null -Scope Global -Force
Set-Variable -Name LogFile -Scope Global -Force
Set-Variable -Name Phase -Scope Global -Force
Set-Variable -Name RelativePath -Scope Global -Force
Set-Variable -Name Sequence -Scope Global -Force
Set-Variable -Name Title -Scope Global -Force
Function ConsoleTitle ($Title){
$host.ui.RawUI.WindowTitle = $Title
}
Function DeclareGlobalVariables {
$Global:BuildLog = $Env:windir+"\Logs\BuildLogs\Build.csv"
$Global:LogFile = $Env:windir+"\Logs\BuildLogs\TPM_On.log"
$Global:Phase = "Final Build"
$Global:Sequence = ""
$Global:Title = "TPM Clear Ownership"
}
Function GetRelativePath {
$Global:RelativePath = (split-path $SCRIPT:MyInvocation.MyCommand.Path -parent)+"\"
}
Function ClearTPM {
#Declare Local Memory
Set-Variable -Name ClassName -Value "Win32_Tpm" -Scope Local -Force
Set-Variable -Name Computer -Value $env:COMPUTERNAME -Scope Local -Force
Set-Variable -Name NameSpace -Value "ROOT\CIMV2\Security\MicrosoftTpm" -Scope Local -Force
Set-Variable -Name oTPM -Scope Local -Force
$oTPM = Get-WmiObject -Class $ClassName -ComputerName $Computer -Namespace $NameSpace
$Output = "Clearing TPM Ownership....."
Write-Host "Clearing TPM Ownership....." -NoNewline
$Temp = $oTPM.SetPhysicalPresenceRequest(5)
If ($Temp.ReturnValue -eq 0) {
$Output = "Success"
Write-Host "Success" -ForegroundColor Yellow
} else {
$Output = "Failure"
Write-Host "Failure" -ForegroundColor Red
$Global:Errors++
}
Out-File -FilePath $Global:LogFile -InputObject $Output -Append -Force
#Cleanup Local Memory
Remove-Variable -Name oTPM -Scope Local -Force
}
Function ProcessLogFile {
If ((Test-Path $Env:windir"\Logs") -eq $false) {
New-Item -ItemType Directory -Path $Env:windir"\Logs"
}
If ((Test-Path $Env:windir"\Logs\ApplicationLogs") -eq $false) {
New-Item -ItemType Directory -Path $Env:windir"\Logs\ApplicationLogs"
}
If ((Test-Path $Env:windir"\Logs\BuildLogs") -eq $false) {
New-Item -ItemType Directory -Path $Env:windir"\Logs\BuildLogs"
}
If ($Global:Errors -eq $null) {
If (Test-Path $Global:LogFile) {
Remove-Item $Global:LogFile -Force
}
$File1 = $Global:LogFile.Split(".")
$Filename1 = $File1[0]+"_ERROR"+"."+$File1[1]
If (Test-Path $Filename1) {
Remove-Item $Filename1 -Force
}
$Global:Errors = 0
} elseIf ($Global:Errors -ne 0) {
If (Test-Path $Global:LogFile) {
$Global:LogFile.ToString()
$File1 = $Global:LogFile.Split(".")
$Filename1 = $File1[0]+"_ERROR"+"."+$File1[1]
Rename-Item $Global:LogFile -NewName $Filename1 -Force
}
} else {
$date = get-date
$LogTitle = $Global:Phase+[char]9+$Global:Sequence+[char]9+$Global:Title+[char]9+$date.month+"/"+$date.day+"/"+$date.year+" "+$date.hour+":"+$date.minute
Out-File -FilePath $Global:BuildLog -InputObject $LogTitle -Append -Force
}
}
Function ExitPowerShell {
If (($Global:Errors -ne $null) -and ($Global:Errors -ne 0)) {
Exit 1
}
}
cls
GetRelativePath
DeclareGlobalVariables
ConsoleTitle $Global:Title
ProcessLogFile
ClearTPM
ProcessLogFile
Start-Sleep -Seconds 5
ExitPowerShell

If you want to run the script as a different user you can 'SHIFT+Right click > Run as a different user' but that works only on applications, so you have to run Powershell as a different user and then the script, you can make a batch file to do that. Here is an example of what you will need in the batch file.
runas /user:yourdomain.com\administrator powershell
If you dont have a domain use the computer name
runas /noprofile /user:computername\administrator powershell

Related

Powershell workflows, installing software locally after disabling UAC?

So I'm bored and trying to learn about WorkFlows in Powershell but somethings just not clicking. So I've got a script that prompts for admin creds to open a new POSH window, then makes the registry changes to disable UAC (only for the duration of the script), then called some exe and MSI installers located on a mapped drive.
import-module ActiveDirectory
$erroractionpreference = 'SilentlyContinue'
#Elevate running of script to Administrative
param([switch]$Elevated)
function Check-Admin {
$currentUser = New-Object Security.Principal.WindowsPrincipal $([Security.Principal.WindowsIdentity]::GetCurrent())
$currentUser.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
}
if ((Check-Admin) -eq $false) {
if ($elevated)
{
# could not elevate, quit
}
else {
Start-Process powershell.exe -Verb RunAs -ArgumentList ('-noprofile -noexit -file "{0}" -elevated' -f ($myinvocation.MyCommand.Definition))
}
exit
}
#Workflow to restart the pc
Workflow reboot_and_continue {
#The following section allows for changing the UAC settings for the duration of the script
{
New-Variable -Name Key
New-Variable -Name PromptOnSecureDesktop_Name
New-Variable -Name ConsentPromptBehaviorAdmin_Name
Function Set-RegistryValue($key, $name, $value, $type="Dword") {
If ((Test-Path -Path $key) -Eq $false) { New-Item -ItemType Directory -Path $key | Out-Null }
Set-ItemProperty -Path $key -Name $name -Value $value -Type $type
}
Function Get-RegistryValue($key, $value) {
(Get-ItemProperty $key $value).$value
}
$Key = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System"
$ConsentPromptBehaviorAdmin_Name = "ConsentPromptBehaviorAdmin"
$PromptOnSecureDesktop_Name = "PromptOnSecureDesktop"
Function Get-UACLevel(){
$ConsentPromptBehaviorAdmin_Value = Get-RegistryValue $Key $ConsentPromptBehaviorAdmin_Name
$PromptOnSecureDesktop_Value = Get-RegistryValue $Key $PromptOnSecureDesktop_Name
If($ConsentPromptBehaviorAdmin_Value -Eq 0 -And $PromptOnSecureDesktop_Value -Eq 0){
"Never notIfy"
}
ElseIf($ConsentPromptBehaviorAdmin_Value -Eq 5 -And $PromptOnSecureDesktop_Value -Eq 0){
"NotIfy me only when apps try to make changes to my computer(do not dim my desktop)"
}
ElseIf($ConsentPromptBehaviorAdmin_Value -Eq 5 -And $PromptOnSecureDesktop_Value -Eq 1){
"NotIfy me only when apps try to make changes to my computer(default)"
}
ElseIf($ConsentPromptBehaviorAdmin_Value -Eq 2 -And $PromptOnSecureDesktop_Value -Eq 1){
"Always notIfy"
}
Else{
"Unknown"
}
}
Function Set-UACLevel() {
Param([int]$Level= 2)
New-Variable -Name PromptOnSecureDesktop_Value
New-Variable -Name ConsentPromptBehaviorAdmin_Value
If($Level -In 0, 1, 2, 3) {
$ConsentPromptBehaviorAdmin_Value = 5
$PromptOnSecureDesktop_Value = 1
Switch ($Level)
{
0 {
$ConsentPromptBehaviorAdmin_Value = 0
$PromptOnSecureDesktop_Value = 0
}
1 {
$ConsentPromptBehaviorAdmin_Value = 5
$PromptOnSecureDesktop_Value = 0
}
2 {
$ConsentPromptBehaviorAdmin_Value = 5
$PromptOnSecureDesktop_Value = 1
}
3 {
$ConsentPromptBehaviorAdmin_Value = 2
$PromptOnSecureDesktop_Value = 1
}
}
Set-RegistryValue -Key $Key -Name $ConsentPromptBehaviorAdmin_Name -Value $ConsentPromptBehaviorAdmin_Value
Set-RegistryValue -Key $Key -Name $PromptOnSecureDesktop_Name -Value $PromptOnSecureDesktop_Value
Get-UACLevel
}
Else{
"No supported level"
}
}
Export-ModuleMember -Function Get-UACLevel
Export-ModuleMember -Function Set-UACLevel
}
set-uaclevel 0
#This section calls the installers from a network share and runs them locally
Invoke-Command -ComputerName $env:COMPUTERNAME -ScriptBlock { Start-Process -FilePath "g:\IT\software\ChromeSetup.exe" -ArgumentList "/s" -Wait }
if ($setup.exitcode -eq 0)
Invoke-Command -ComputerName $env:COMPUTERNAME -ScriptBlock { Start-Process -FilePath "g:\IT\support\NSG_Vipre_agent_ssi-mi.msi" -ArgumentList "-silent" -Wait }
if ($setup.exitcode -eq 0)
Invoke-Command -ComputerName $env:COMPUTERNAME -ScriptBlock { Start-Process -FilePath "G:\IT\software\spiceworks\install_collection.bat" -Wait }
if ($setup.exitcode -eq 0)
Line 25 is where I'm thinking of trying to introduce the work flow.. but on second thought it might be better to encompass the entire thing in the workflow... I just don't know.
I've tried searching but I just can't find any examples of how to install software using a workflow. the idea is to run a powershell window with admin creds, disabled UAC then reboot. Once started back up call the various installers then re-enable UAC and reboot again.
Thanks!

Updating the current logged on user, HKEY_USERS hive registry, with system account

This script makes changes to all users' profiles.
Here is the script:
# Get each user profile SID and Path to the profile
$UserProfiles = Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\*" |
Where {$_.PSChildName -match "S-1-5-21-(\d+-?){4}$" } |
Select-Object #{Name="SID"; Expression={$_.PSChildName}}, #{Name="UserHive";Expression={"$($_.ProfileImagePath)\NTuser.dat"}}
# Loop through each profile on the machine
foreach ($UserProfile in $UserProfiles) {
# Load User ntuser.dat if it's not already loaded
if (($ProfileWasLoaded = Test-Path Registry::HKEY_USERS\$($UserProfile.SID)) -eq $false) {
Start-Process -FilePath "CMD.EXE" -ArgumentList "/C REG.EXE LOAD HKU\$($UserProfile.SID) $($UserProfile.UserHive)" -Wait -WindowStyle Hidden
}
}
# Manipulate the registry
$key = "Registry::HKEY_USERS\$($UserProfile.SID)\Software\SomeArchaicSoftware\Configuration"
New-Item -Path $key -Force | Out-Null
New-ItemProperty -Path $key -Name "LoginURL" -Value "https://www.myCompany.local" -PropertyType String -Force | Out-Null
New-ItemProperty -Path $key -Name "DisplayWelcome" -Value 0x00000001 -PropertyType DWORD -Force | Out-Null
$key = "$key\UserInfo"
New-Item -Path $key -Force | Out-Null
New-ItemProperty -Path $key -Name "LoginName" -Value "$($ENV:USERDOMAIN)\$($ENV:USERNAME)" -PropertyType STRING -Force | Out-Null
# Unload NTuser.dat
if ($ProfileWasLoaded -eq $false) {
[GC]::Collect()
Start-Sleep 1
Start-Process -FilePath "CMD.EXE" -ArgumentList "/C REG.EXE UNLOAD HKU\$($UserProfile.SID)" -Wait -WindowStyle Hidden| Out-Null
}
I only need changes to the current logged on user HKEY_USERS hive.
Can anyone help me change the script so it's only the current logged in user who gets the changes?
You can determine the SID of a currently logged-in user via WMI. Check for the owner of a running explorer.exe process, then resolve the account name to its SID:
$user = (Get-WmiObject Win32_Process -Filter "Name='explorer.exe'").GetOwner()
$fltr = "Name='{0}' AND Domain='{1}'" -f $user.User, $user.Domain
$sid = (Get-WmiObject Win32_UserAccount -Filter $fltr).SID
Still, I think a logon script would be a better place for changes to a user's registry settings.

Show Task Manager details in Windows 8.1

This script restarts Taskmanager and show more details in its window and works on Windows 10
If (!(Test-Path HKCU:\Software\Microsoft\Windows\CurrentVersion\TaskManager))
{
New-Item -Path HKCU:\Software\Microsoft\Windows\CurrentVersion\TaskManager -Force
}
$preferences = Get-ItemProperty -Path HKCU:\Software\Microsoft\Windows\CurrentVersion\TaskManager -Name Preferences -ErrorAction SilentlyContinue
If (!($preferences))
{
$taskmgr = Get-Process Taskmgr -ErrorAction SilentlyContinue
If ($taskmgr)
{
$taskmgr | Stop-Process -Force
}
Start-Process -FilePath Taskmgr
}
$preferences = Get-ItemProperty -Path HKCU:\Software\Microsoft\Windows\CurrentVersion\TaskManager -Name Preferences -ErrorAction SilentlyContinue
$preferences.Preferences[28] = 0
New-ItemProperty -Path HKCU:\Software\Microsoft\Windows\CurrentVersion\TaskManager -Name Preferences -Type Binary -Value $preferences.Preferences -Force
If ($taskmgr)
{
$taskmgr | Stop-Process -Force
}
Start-Process -FilePath Taskmgr
but stucks on Windows 8.1 with an error
Unable to index in array NULL
+ $preferences.Preferences[28] = 0
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : NullArray
A few suggestions:
Try/Catch is better than -ErrorAction SilentlyContinue in most cases
You should validate the objects ie. before referring to items in arrays (like the length of an array)
If you remove the ErrorAction you might get an exception in this case which likely would show that the path does not exist. Or, $Preferences.Preferences does not have 28 items in the array.
/Patrik
I can testify that killing Task Manager forcedly using Stop-Process does not create Preferences registry value (running Windows 8.1/64).
Creation Preferences registry value requires regular completion of Task Manager, i.e. user's cooperation (see $taskmgr | Wait-Process in the following script).
$taskmgrPath = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\TaskManager'
If (!(Test-Path $taskmgrPath))
{
New-Item -Path $taskmgrPath -Force
}
$taskmgr = Get-Process Taskmgr -ErrorAction SilentlyContinue
If ($taskmgr)
{
#$taskmgr | Stop-Process -Force
$taskmgr | Wait-Process
}
$preferences = Get-ItemProperty -Path $taskmgrPath -Name Preferences -ErrorAction SilentlyContinue
If (!($preferences))
{
Start-Process -FilePath Taskmgr
$taskmgr = Get-Process Taskmgr -ErrorAction SilentlyContinue
If ($taskmgr)
{
#$taskmgr | Stop-Process -Force
$taskmgr | Wait-Process
}
$preferences = Get-ItemProperty -Path $taskmgrPath -Name Preferences -ErrorAction SilentlyContinue
}
$preferences.Preferences[28] = 0
New-ItemProperty -Path $taskmgrPath -Name Preferences -Type Binary -Value $preferences.Preferences -Force
If ($taskmgr)
{
$taskmgr | Stop-Process -Force
}
Start-Process -FilePath Taskmgr ## restart Task manager with modified properties
Alternatively, send Alt+F4 to the Task Manager. window i.e. '%{F4}' string in terms of both
.Sendkeys() method of COM object WScript.Shell, or
::SendWait() static method of .NET class [System.Windows.Forms.SendKeys]
$taskmgr = Get-Process Taskmgr -ErrorAction SilentlyContinue
If ($taskmgr)
{
$taskmgr.CloseMainWindow()
}
Start-Process -FilePath Taskmgr
Start-Sleep -Seconds 1
$taskmgr = Get-Process Taskmgr -ErrorAction SilentlyContinue
If ($taskmgr)
{
$taskmgr.CloseMainWindow()
}
Start-Sleep -Seconds 1
$preferences = Get-ItemProperty -Path HKCU:\Software\Microsoft\Windows\CurrentVersion\TaskManager -Name Preferences
$preferences.Preferences[28] = 0
New-ItemProperty -Path HKCU:\Software\Microsoft\Windows\CurrentVersion\TaskManager -Name Preferences -Type Binary -Value $preferences.Preferences -Force
Start-Process -FilePath Taskmgr

Deploying Registry Keys remotely

I have to code a script, which deploys two different registry keys remotely to all clients in our Active Directory. The script doesnt respond with any errors, but it still seems, that the keys aren't getting created. Can you maybe help me?
Im not used to programming so please keep that in mind :D
$pc = Get-ADComputer -filter {name -like "WS226"}
foreach ($object in $pc)
{
$object.name
#New Powershell-Remotesession with $PC
$session = New-PSSession -Computername $object.name
$Dir ="HKLM:\SOFTWARE\ORACLE\KEY_OraClient11g_home1"
If (Test-Path $Dir)
{
New-Item -Path HKLM:\SOFTWARE\ORACLE\KEY_OraClient11g_home1 -Name NewKey -Value "Default Value" -Force
New-ItemProperty -Path HKLM:\SOFTWARE\ORACLE\KEY_OraClient11g_home1\NewKey -Name "NLS_DATE_FORMAT" -PropertyType "String" -Value "DD.MM.RRRR"
}
else
{
ECHO "key"
}
$Dir ="HKLM:\SOFTWARE\ORACLE\KEY_OraClient11g_home1"
If (Test-Path $Dir)
{
New-Item -Path HKLM:\SOFTWARE\ORACLE\KEY_OraClient11g_home1 -Name NewKey2 -Value "Default Value" -Force
New-ItemProperty -Path HKLM:\SOFTWARE\ORACLE\KEY_OraClient11g_home1\NewKey2 -Name " NLS_NUMERIC_CHARACTERS" -PropertyType "String" -Value ".,"
}
else
{
ECHO "key"
}
}
You are creating the pssession but you don't use it.
Put your code in a invoke-command.
Example
Invoke-command -Session $Session -scriptblock{
$Dir ="HKLM:\SOFTWARE\ORACLE\KEY_OraClient11g_home1"
If (Test-Path $Dir)
{
New-Item -Path HKLM:\SOFTWARE\ORACLE\KEY_OraClient11g_home1 -Name NewKey -
Value "Default Value" -Force
#your other code goes here
}

Powershell export all detailed logging to csv file

I have inherited a script that is not working. I need to capture everything that would normally output to the console, including Success and Error entries from the script. This is only a small portion of the script, and it only captures errors. Any help would be appreciated on getting all output to the file instead of the console.
An example is the Write-Verbose "VERIFYING contact for $($User.WindowsEmailAddress)"
I know this is writing to the console, but I need it to write to the log that is defined at the very bottom of the script.
Catch
{Out-File -InputObject "$(Get-Date -Format MM.dd.yyyy-HH:mm:ss);$($WriteMode);ERROR;Target;$($targetUser.Split('#')[1]);$($User.WindowsEmailAddress);Update;;;Error updating user: $($Error[0])" -FilePath $LogFilePath -Append}
I hope this makes sense.
### UPDATES
ForEach ($User in $colUpdContact)
{
Write-Verbose "VERIFYING contact for $($User.WindowsEmailAddress)"
#Filter used to find the target contact object(s)
$strFilter = "WindowsEmailAddress -eq `"$($User.WindowsEmailAddress)`""
Try
{$colContacts2 = Invoke-Command -Session $targetSession -ScriptBlock {param ($strFilter) Get-Contact -Filter $strFilter} -ArgumentList $strFilter -ErrorAction Stop}
Catch
{Out-File -InputObject "$(Get-Date -Format MM.dd.yyyy-HH:mm:ss);$($WriteMode);ERROR;Target;$($targetUser.Split('#')[1]);$($User.WindowsEmailAddress);Find;;;Error getting contact: $($Error[0])" -FilePath $LogFilePath -Append}
ForEach ($Contact in $colContacts2)
{
#initialize update string and cmd string
$strUpdateContact = $null
$updateCmd = $null
$strWriteBack = $null
$writeBackCmd = $null
#Iterate through attributes and append to the strUpdateContact string if the attribute value has changed
ForEach ($Attrib in $arrAttribs)
{
If ($User.$Attrib -ne $Contact.$Attrib)
{
if($ReadOnly){
Add-Content -Path $readOnlyFilePath -Value " Changing $Attrib"
Add-Content -Path $readOnlyFilePath -Value " Before: $($Contact.$Attrib)"
Add-Content -Path $readOnlyFilePath -Value " After: $($User.$Attrib)"
}
$strUpdateContact += " -$($Attrib) `"$($User.$Attrib)`""
Out-File -InputObject "$(Get-Date -Format MM.dd.yyyy-HH:mm:ss);$($WriteMode);CHANGE;Target;$($targetUser.Split('#')[1]);$($User.WindowsEmailAddress);Update;$($Contact.$Attrib);$($User.$Attrib);" -FilePath $LogFilePath -Append
}
}
#Check if LegacyExchangeDN has been written back to User object
$mailContact = Invoke-Command -Session $targetSession -ScriptBlock {param ($contact) Get-MailContact $($contact.WindowsEmailAddress)} -ArgumentList $Contact -ErrorAction Stop
$x500 = "X500:$($mailContact.LegacyExchangeDN)"
$userRec = Invoke-Command -Session $sourceSession -ScriptBlock {param ($User) Get-Recipient $($User.WindowsEmailAddress)} -ArgumentList $User -ErrorAction Stop
if($UserRec.emailAddresses -notcontains $x500){
$userName = ($user.UserPrincipalName).Split('#')[0]
if($userName -eq "")
{
$userName = $user.SamAccountName
}
$strWriteBack = "Set-ADUser -Identity $userName -Add #{ProxyAddresses=`"$x500`"} -Server $sourceDC -Credential `$sourceDCCredential"
}
#If there is anything to update
If ($strUpdateContact.Length -gt 0)
{
Write-Verbose "Updating attributes for $($User.WindowsEmailAddress)"
#Prepend the command for the contact being modified
$strUpdateContact = "Set-Contact $($User.WindowsEmailAddress) " + $strUpdateContact
If ($ReadOnly)
{Add-Content -Path $readOnlyFilePath -Value $strUpdateContact}
Else
{
Try
{
#Create the complete command and invoke it
$updateCmd = "Invoke-Command -Session `$targetSession -ScriptBlock {$($strUpdateContact)}"
Invoke-Expression $updateCmd -ErrorAction Stop
}
Catch
{Out-File -InputObject "$(Get-Date -Format MM.dd.yyyy-HH:mm:ss);$($WriteMode);ERROR;Target;$($targetUser.Split('#')[1]);$($User.WindowsEmailAddress);Update;;;Error updating contact: $($Error[0])" -FilePath $LogFilePath -Append}
}
}
If ($strWriteBack){
Write-Verbose "Updating X500 for $($User.WindowsEmailAddress)"
Out-File -InputObject "$(Get-Date -Format MM.dd.yyyy-HH:mm:ss);$($WriteMode);CHANGE;Target;$($targetUser.Split('#')[1]);$($User.WindowsEmailAddress);Update;;$x500;" -FilePath $LogFilePath -Append
If($ReadOnly){
Add-Content -Path $readOnlyFilePath -Value $strWriteBack
}
else{
Try
{
Invoke-Expression $strWriteBack -ErrorAction Stop
}
Catch
{Out-File -InputObject "$(Get-Date -Format MM.dd.yyyy-HH:mm:ss);$($WriteMode);ERROR;Target;$($targetUser.Split('#')[1]);$($User.WindowsEmailAddress);Update;;;Error updating user: $($Error[0])" -FilePath $LogFilePath -Append}
}
}
}
}
Why you not use the Start-Transcript to output all the information into a log file, and then you can manually copy anything you want?
An example for the command:
Start-Transcript -Path $TranscriptOutputFile -Append -Force
#Your script; write-output 'something update';
Stop-Transcript
Everything output by write-output command will be appended into the log file.