Powershell GPO start up script - Uninstall Microsoft Updates - windows

I can run this script manually/interactively with an account that has local admin rights and it works perfectly fine, but when I insert it into a GPO start up script or deploy it through SCCM, nothing happens. It just opens the console window and then closes it - the MS Updates remain installed.
$PatchList = #("KB2553154","KB2899519","KB2910902","KB3008923","KB3012176","KB3013126")
function Remove-Update
{
$HotFixes = Get-HotFix
foreach ($HotFix in $HotFixes)
{
foreach ($KBID in $PatchList)
{
if ($KBID -eq $HotFix.HotfixId)
{
"Inside first if"
$KBID = $HotFix.HotfixId.Replace("KB", "")
$RemovalCommand = "wusa.exe /uninstall /kb:$KBID /quiet /norestart"
Write-Host "Removing $KBID from the target."
Invoke-Expression $RemovalCommand
break
}
if ($KBID -match "All")
{
$KBNumber = $HotFix.HotfixId.Replace("KB", "")
$RemovalCommand = "wusa.exe /uninstall /kb:$KBNumber /quiet /norestart"
Write-Host "Removing update $KBNumber from the target."
Invoke-Expression $RemovalCommand
}
if ($KBID -match "Security")
{
if ($HotFix.Description -match "Security")
{
$KBSecurity = $HotFix.HotfixId.Replace("KB", "")
$RemovalCommand = "wusa.exe /uninstall /kb:$KBSecurity /quiet /norestart"
Write-Host "Removing Security Update $KBSecurity from the target."
Invoke-Expression $RemovalCommand
}
}
while (#(Get-Process wusa -ErrorAction SilentlyContinue).Count -ne 0)
{
Start-Sleep 3
Write-Host "Waiting for update removal to finish ..."
}
}
}
}
Remove-Update
I know that with a GPO start up script or SCCM deployment, the account being used to run the script is the system account. Not sure if that is causing the issue?
Any advice would be greatly appreciated.

Related

Run powershell Commands in Dockerfile

I have a PowerShell script and I tried running in the Docker using the RUN command in the windows base OS image but was unable due to permission issues. SO I thought to give the PS script in the Dockerfile itself but getting errors
I am using the "mcr.microsoft.com/windows:1909" image and I am attaching my PowerShell script below
param(
[Parameter()]
[ValidateNotNull()]
[string[]]$AdditionalExtensions = #(),
[switch]$LaunchWhenDone
)
if (!($IsLinux -or $IsOSX)) {
$codeCmdPath = "C:\Program Files (x86)\Microsoft VS Code\bin\code.cmd"
try {
$ProgressPreference = 'SilentlyContinue'
if (!(Test-Path $codeCmdPath)) {
Write-Host "`nDownloading latest stable Visual Studio Code..." -ForegroundColor Yellow
Remove-Item -Force $env:TEMP\vscode-stable.exe -ErrorAction SilentlyContinue
Invoke-WebRequest -Uri https://vscode-update.azurewebsites.net/latest/win32/stable -OutFile $env:TEMP\vscode-stable.exe
Write-Host "`nInstalling Visual Studio Code..." -ForegroundColor Yellow
Start-Process -Wait $env:TEMP\vscode-stable.exe -ArgumentList /silent, /mergetasks=!runcode
}
else {
Write-Host "`nVisual Studio Code is already installed." -ForegroundColor Yellow
}
$extensions = #("ms-vscode.PowerShell") + $AdditionalExtensions
foreach ($extension in $extensions) {
Write-Host "`nInstalling extension $extension..." -ForegroundColor Yellow
& $codeCmdPath --install-extension $extension
}
if ($LaunchWhenDone) {
Write-Host "`nInstallation complete, starting Visual Studio Code...`n`n" -ForegroundColor Green
& $codeCmdPath
}
else {
Write-Host "`nInstallation complete!`n`n" -ForegroundColor Green
}
}
finally {
$ProgressPreference = 'Continue'
}
}
else {
Write-Error "This script is currently only supported on the Windows operating system."
}```

Is it possible to launch a process under powershell and have visual studio immediately connect to it for debugging

I have a program foo.exe
foo.exe sometxtfile -arg0 10 -arg1 "cats" -arg3 666
It currently crashes with an exception. I have the project in visual studio and could put that command line into the visual studio debug startup but I'd like to be more flexible and be able to start this from powershell.
I am aware of Debug-Process but that only debugs a currently running process. Would it be possible to use this to launch and debug?
The trick is to wait for the process to start the job as a background process and then call Debug-Process on it.
start-job {
foo.exe sometxtfile -arg0 10 -arg1 "cats" -arg3 666
}
$process="foo"
Write-Host "Waiting for $process to start"
Do {
$status = Get-Process $process -ErrorAction SilentlyContinue
If (!($status)) {
Write-Host -NoNewline '.'
}
Else {
Write-Host ""
Write-Host "$process has started"
$started = $true
}
}
Until ( $started )
debug-process -Name ${process}
debug-process -Name ${process}

Powershell Script to deploy multiple software using variables

I am building a deployment script to install software on a new device using a ppkg file.
The script looks at which drive is the USB drive and copies the software over to the local temp folder and runs them according to a set of variables as shown below.
What I am struggling to do is simplify the script so I am not repeating code 7 times down the page, I want to just run a loop 7 times to pull in the needed software. I tried an array but I think I am not quite understanding it completely.
This is my script so far with the repeating code:
#SOE application Variables
#applcation1 CM_client
$app1name = "Config Manager Client 1706"
$app1skip = "no"
$app1path = "$env:SystemDrive\temp\soe\application_installs\app1\CM_client_inst_1706\"
$app1runcommand = "clientx64.bat"
$app1arguments = ""
#applcation2
$app2name = "Office 2016 Pro Plus"
$app2skip = "no"
$app2path = "$env:SystemDrive\temp\soe\application_installs\app2\O2016\"
$app2runcommand = "setup.exe"
$app2arguments = "/configure configuration.xml"
#log Folder
$datetime = Get-Date -format "yyyy.MM.dd-HH.mm.ss"
$logpath = "$env:ALLUSERSPROFILE\SOEInst_ppkg\$datetime"
New-Item -Path $logpath -ItemType Directory -ErrorAction SilentlyContinue
#Transcript Start
Start-Transcript -Path $logpath\SOE-app-installer-ppkg-$datetime.log
#Timer Function
$pkgremovetime = Get-Date -format "HH:mm:ss"
write-host "Script Start Time - $pkgremovetime"
#Find USB Drive
Write-host Discovering USB Drive
$drives = (GET-WMIOBJECT –query “SELECT * from win32_logicaldisk").DeviceID
foreach ($drive in $drives) {
$usbdrive = (dir $drive USBIMG.FILE | Select-Object -Unique "USBIMG.FILE")
if ($usbdrive -match "USBIMG.FILE*") {
$datadrive = $drive
}
}
Write-host Found $datadrive is the USB drive
#Copy Applications to Local Drive
Write-Host Creating Installer Folder
New-Item -Path $env:SystemDrive\temp\SOE -ItemType Directory
Copy-Item $datadrive\application_installs $env:SystemDrive\temp\soe -Recurse -Verbose
#Install Applications
#Application 1
if ($app1skip -eq "no") {
if ($app1arguments) { #Arguments Variable Populated
Write-Host Installing Applcation 1 `($app1name`)
$app1 = Start-Process -Wait -FilePath $app1path$app1runcommand -ErrorAction Continue -ArgumentList $app1arguments -WindowStyle Normal
if ($app1.ExitCode -eq "0") {
Write-Host $app1name Installed ok
} Else {
Write-host $app1name install exited with code $app1.ExitCode
}
}
}Else { #Argurments Variable Empty
Write-Host Installing Applcation 1 `($app1name`)
$app1 = Start-Process -Wait -FilePath $app1path$app1runcommand -ErrorAction Continue -WindowStyle Normal
if ($app1.ExitCode -eq "0") {
Write-Host $app1name Installed ok
} Else {
Write-host $app1name install exited with code $app1.ExitCode
}
}
#Application 2
if ($app2skip -eq "no") {
if ($app2arguments) { #Arguments Variable Populated
Write-Host Installing Applcation 2 `($app2name`)
$app2 = Start-Process -Wait -FilePath $app2path$app2runcommand -ErrorAction Continue -ArgumentList $app2arguments -WindowStyle Normal
if ($app2.ExitCode -eq "0") {
Write-Host $app2name Installed ok
} Else {
Write-host $app2name install exited with code $app2.ExitCode
}
}
}Else { #Argurments Variable Empty
Write-Host Installing Applcation 2 `($app2name`)
$app2 = Start-Process -Wait -FilePath $app2path$app2runcommand -ErrorAction Continue -WindowStyle Normal
if ($app2.ExitCode -eq "0") {
Write-Host $app2name Installed ok
} Else {
Write-host $app2name install exited with code $app2.ExitCode
}
}
#cleanup
Remove-Item $env:SystemDrive\temp\soe -Recurse -Force -Verbose
#get end time
$pkgremovetime_end = Get-Date -format "HH:mm:ss"
#calculate time difference
$timetaken = New-TimeSpan $pkgremovetime $pkgremovetime_end
if ($timetaken.Seconds -lt 0) {
$Hrs = ($timetaken.Hours) + 23
$Mins = ($timetaken.Minutes) + 59
$Secs = ($timetaken.Seconds) + 59 }
else {
$Hrs = $timetaken.Hours
$Mins = $timetaken.Minutes
$Secs = $timetaken.Seconds }
$Difference = '{0:00}:{1:00}:{2:00}' -f $Hrs,$Mins,$Secs
#log time difference
write-host "Script End Time - $pkgremovetime_end"
Write-Host "Total time taken $difference"
#Transcript End
Stop-Transcript
I suggest you make a function which takes in the variables. I did a quick comparison of your installation codes and something like this should work
function installApplication{
Param($skip, $arguments, $name, $path, $runcommand)
if ($skip -eq "no"){
if ($arguments){
write-host "Installing Application $appname"
$app = Start-Process -Wait -FilePath $path$runcommand -ErrorAction....
if($app.ExitCode -eq "0"){
....
....
}
and so on, You can then call the function using
installApplication $app1skip $app1arguments $app1name $app1path $app1runcommand
installApplication $app2skip $app2arguments $app2name $app2path $app1runcommand
Your input arguments will replace the function parameters in the order you pass them in, or you can use -skip $app1skip to assign the parameters.
If your repeating the same code too many times, I suggest throwing it into something like diffchecker, put the code into a function and replace all the differences with variables.
You can see your code here https://www.diffchecker.com/FxAIdD6g (1 Day only)

Check server before starting service

I need to edit a script. There is a service that is on two Windows Server 2008 R2. They are load balanced. I need it so when I run the script that starts the service on the primary server and the secondary, so before it even start the service on both servers, the goes out and checks to ensure the primary server is up and running, then continues on as normal to start the services on both servers.
# Start Appian
function StartAppian {
$APNSVC = Get-Service -Name $AppianService
if (!$APNSVC) {
Write-Host "Appian Service does not exist"
return
}
# Check to see if Appian's service is already started
if ($APNSVC.Status -eq "Running") {
if ($LB) {
if ($MULEAPNSVC.Status -eq "Running") {
Write-Host "Appian Service on the Load Balanced Server already is started!" -ForegroundColor Yellow
return
}
}
Write-Host "Appian Service already is started!" -ForegroundColor Yellow
Read-Host "Press any key to return"
return
}
# Check if DEV's Process Design has a writing_*.kdb file and delete it
if ($Server -eq "DEV") {
#gw1
if (Test-Path $APPIAN_HOME\server\process\design\gw1\writing_*.kdb) {
Write-Host "Removing writing_*.kdb from GW1" -ForegroundColor Yellow
Remove-Item $APPIAN_HOME\server\process\design\gw1\writing_*.kdb
}
#gw2
if (Test-Path $APPIAN_HOME\server\process\design\gw2\writing_*.kdb) {
Write-Host "Removing writing_*.kdb from GW2" -ForegroundColor Yellow
Remove-Item $APPIAN_HOME\server\process\design\gw2\writing_*.kdb
}
}
Write-Host "Starting Appian"
# Place the name of the service here to start for Appian
Start-Service $AppianService
Notify("StartAppian")
if ($LB) {
(Get-Service $MULEAPNSVC.Name -ComputerName $MULE).Start()
Write-Host "Starting Mule's Appian" -ForegroundColor Magenta
}
cmd.exe "/C $APPIAN_HOME\server\_scripts\diagnostic\checkengine.bat -s > $logdir\Startup.log"
# These lines check the Startup log for fatals and errors at the beginning
$fatals = Select-String FATAL $logdir\Startup.log
$errs = Select-String ERROR $logdir\Startup.log
# Check for errors and fatals again
$fatals = Select-String FATAL $logdir\Startup.log
$errs = Select-String ERROR $logdir\Startup.log
Write-Host "Still warnings or Errors in CE" -ForegroundColor Yellow
# Increment times
$times = $times + 1
# If times > threshold, email out error message
if ($times -gt $threshold) {
SendAlert("There is a problem with Appian - It won't start")
Write-Host "There was a problem with Appian..it took too long to start - emailing alert" -ForegroundColor Red
Read-Host "Press any key to exit"
}
}
Write-Host "Appian Started" -ForegroundColor Green
Read-Host "Press any key to return"
}
You can do that with a simple Test-Connection at the start of the script.
if ($ping = Test-Connection -ComputerName PrimaryServerName -Quiet) {
Write-Host "Host avalible"
}
else {
Write-Host "Host unavalible"
Exit
}
any other suggestions?
if (Test-Connection ServerName -Count 1 -Quiet) {
Write-Host "Host avalible"
}
else {
Write-Host "Host unavalible"
Exit
}
this doesnt work. I want it to test the if the server is up or not, if its not, exit the script, if it is, continue on with the script. When testing this on ISE it work, but when i launch the script it doesn't

Run powershell script with elevated command

I have an autologon Powershell script that I'd like to run as admin when I double click on it. I tried to use different scripts but I'm out of luck.
For example:
Start-Process PowerShell –Verb RunAs
Would open another Powershell screen as administrator but without the original script that I wanna run which is:
net accounts /minpwlen:0
net user TPUser /add
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' -Name AutoAdminLogon -Value 1
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' -Name DefaultUserName -Value "TPUser"
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' -Name DefaultPassword -Value ""
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' -Name DefautDomainName -Value ""
copy c:\temp\OP.rdp c:\Users\Public\Desktop
pause
Any idea how can I get this to work ?
You are in luck because I was fighting with this issue for some time, what you need to do is make it take note of where it is at and when it starts back up the shell as an admin it needs to execute the script.
Function Test-IsAdmin {
[cmdletbinding()]
Param()
Write-Verbose "Checking to see if current user context is Administrator"
If (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.NTAccount] "[WriteGroupHere]"))
{
Write-Warning "You are not currently running this under an Administrator account! `nThere is potential that this command could fail if not running under an Administrator account."
Write-Verbose "Presenting option for user to pick whether to continue as current user or use alternate credentials"
#Determine Values for Choice
$choice = [System.Management.Automation.Host.ChoiceDescription[]] #("Use &Alternate Credentials","&Continue with current Credentials")
#Determine Default Selection
[int]$default = 0
#Present choice option to user
$userchoice = $host.ui.PromptforChoice("Warning","Please select to use Alternate Credentials or current credentials to run command",$choice,$default)
#$workingDir = $PSCommandPath
#$PSCommandPath
Write-Debug "Selection: $userchoice"
#Determine action to take
Switch ($Userchoice)
{
0
{
#Prompt for alternate credentials
Write-Verbose "Prompting for Alternate Credentials"
$Credential = Get-Credential
#Write-Output $Credential
#We are not running "as Administrator" - so relaunch as administrator
Start-Process powershell.exe -ArgumentList "$PSCommandPath" -Credential $Credential
#-WorkingDirectory $workingDir
exit
}
1
{
#Continue using current credentials
Write-Verbose "Using current credentials"
Write-Output "CurrentUser"
}
}
}
Else
{
Write-Verbose "Passed Administrator check"
#$Host.UI.RawUI.WindowTitle = "Custom Powershell Environment" +
#$Host.UI.RawUI.BackgroundColor = "DarkBlue"
}
}
with this just put it in the top of your script and call the function, and you will need to change the group that it checks to know if you are an admin or not, I was using an AD group to check since it was a more functional way for me.
I have used the following before to re-launch as script as admin but there is not stopping the UAC prompt:
function IsAdministrator
{
$Identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$Principal = New-Object System.Security.Principal.WindowsPrincipal($Identity)
$Principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)
}
function IsUacEnabled
{
(Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System).EnableLua -ne 0
}
#
# Main script
#
if (!(IsAdministrator))
{
if (IsUacEnabled)
{
[string[]]$argList = #('-NoProfile', '-NoExit', '-File', $MyInvocation.MyCommand.Path)
$argList += $MyInvocation.BoundParameters.GetEnumerator() | Foreach {"-$($_.Key)", "$($_.Value)"}
$argList += $MyInvocation.UnboundArguments
Start-Process PowerShell.exe -Verb Runas -WorkingDirectory $pwd -ArgumentList $argList
return
}
else
{
throw "You must be administrator to run this script"
}
}
I actually used this script on top of mine and it worked perfectly.
# ##########################################
# Determine if we have Administrator rights
Write-Host 'Checking user permissions... '
$windowsID = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$windowsSecurityPrincipal = New-Object System.Security.Principal.WindowsPrincipal($windowsID)
$adminRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator
If (!($windowsSecurityPrincipal.IsInRole($adminRole))) {
Write-Warning 'Current user does not have Administrator rights'
Write-Host 'Attempting to copy files to temporary location and restarting script'
# Get random file name
Do {
$temp = [System.IO.Path]::GetTempPath() + [System.IO.Path]::GetRandomFileName()
} Until (!(Test-Path -LiteralPath "$temp"))
# Create directory
Write-Host 'Creating temp directory... ' -NoNewLine
New-Item -Path "$temp" -ItemType 'Directory' | Out-Null
Write-Host 'done.'
# Copy script to directory
Write-Host 'Copying script to temp directory... ' -NoNewLine
Copy-Item -LiteralPath "$($myInvocation.MyCommand.Path)" "$temp" | Out-Null
Write-Host 'done.'
$newScript = "$($temp)\$($myInvocation.MyCommand.Name)"
# Start new script elevated
Write-Host 'Starting script as administrator... ' -NoNewLine
$adminProcess = New-Object System.Diagnostics.ProcessStartInfo
$adminProcess.Filename = ([System.Diagnostics.Process]::GetCurrentProcess()).Path
$adminProcess.Arguments = " -File `"$newScript`""
$adminProcess.Verb = 'runas'
Try {
[System.Diagnostics.Process]::Start($adminProcess) | Out-Null
}
Catch {
Write-Error 'Could not start process'
Exit 1
}
Write-Host 'done.'
Exit 0
}

Resources