WSUS Command to decline updates - powershell

I am currently trying to force my WSUS to decline updates through a command line because it is overloaded i cannot access the mangement snap in. This is a code that i found online but the only problem i am having is that my server has dashes in the name and is messing up the code. I tried placing qoutations around the name and it still spits back an invalid character after ":" from 3rd line
EDIT: New Error
Invalid URI: The hostname could not be parsed. at Microsoft.UpdateServices.Administration.AdminProxy.CreateUpdateServer(Object[] args) at Microsoft.UpdateServices.Administration.AdminProxy.GetupdateServer(String serverName, Boolean useSecureConnection, Int32 portnumber) at Callsite.Target(Closure , Callsite , Type , Object , Object , Object )
#Change server name and port number and $True if it is on SSL
$Computer = $env:common-n-sccm2012
$Domain = $env:airplane.black.low.com
$FQDN = "$Computer" + "." + "$Domain"
[String]$updateServer1 = $FQDN
[Boolean]$useSecureConnection = $False
[Int32]$portNumber = 8530
# Load .NET assembly
[void][reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration")
$count = 0
# Connect to WSUS Server
$updateServer = [Microsoft.UpdateServices.Administration.AdminProxy]::getUpdateServer($updateServer1,$useSecureConnection,$portNumber)
write-host "<<<Connected sucessfully >>>" -foregroundcolor "yellow"
$updatescope = New-Object Microsoft.UpdateServices.Administration.UpdateScope
$u=$updateServer.GetUpdates($updatescope )
foreach ($u1 in $u )
{
if ($u1.IsSuperseded -eq 'True')
{
write-host Decline Update : $u1.Title
$u1.Decline()
$count=$count + 1
}
}
write-host Total Declined Updates: $count
trap
{
write-host "Error Occurred"
write-host "Exception Message: "
write-host $_.Exception.Message
write-host $_.Exception.StackTrace
exit
}
# EOF

I think you mean the error pops up at this line:
$Computer = $env:common-n-sccm2012
because you are trying to get an environment variable with dashes in its name. You may try to format it as:
$Computer = ${env:common-n-sccm2012}
HOWEVER
I think you are 'Environmentifying' hardcoded values and that is the actual problem.
It is hard to believe that you do in fact have Environment variables with names like common-n-sccm2012 and airplane.black.low.com.
To get the actual Fully Qualified Domain Name from the computer you are on, you could do this:
$Computer = $env:COMPUTERNAME # --> "common-n-sccm2012"
$Domain = $env:USERDNSDOMAIN # --> "airplane.black.low.com"
$FQDN = "$Computer" + "." + "$Domain" # --> "common-n-sccm2012.airplane.black.low.com"
( or in one line: $FQDN = "$env:COMPUTERNAME.$env:USERDNSDOMAIN" )
You can also obtain this FQDN using WMI:
$FQDN = (Get-WmiObject win32_computersystem).DNSHostName + "." + (Get-WmiObject win32_computersystem).Domain
You can read about Environment Variables here

Related

Not getting remote user names with (Get-WmiObject -Class win32_computersystem -ComputerName $computer).UserName

First and foremost thanks for your time and help.
My issue here is that I am having an erratic behavior in getting the name of the remote logged users. I want to send a message to a certain list of computers or IPs, and if somebody is logged get who saw the message.
To do so I created a script which should read and get the computer names one by one, and see if is connected. If so, get the name of the user, send the message and then show in table and write on a txt file the results:
#We read the "Lista" file with IPs or computer names, works with both
$PCLIST = Get-Content 'C:\Users\XXXX\Desktop\Lista.txt'
#Add the date tot he txt files to be created
$Fecha = date
echo $Fecha > "C:\Users\XXXX\Desktop\ListOffline.txt"
echo $Fecha > "C:\Users\XXXX\Desktop\Done.txt"
#We check every computer and if ONLINE send the message
foreach ($computer in $PCLIST)
{
if ((Test-NetConnection -ComputerName $computer -WarningAction SilentlyContinue).PingSucceeded -eq $true) #If ping back is succesfull then write the message
{
$Usuario = (Get-WmiObject -Class win32_computersystem -ComputerName $computer).UserName
if ((Get-WmiObject -Class win32_computersystem -ComputerName $computer).UserName) {" "} else {$Usuario = "Usuario Remoto"}
$output = #{ 'Computer_Name / IP' = $computer }
$output.Usuario_Conectado = (Get-WmiObject -Class win32_computersystem -ComputerName $computer).UserName
msg * /server:$computer "Hey!!" $Usuario ", Something meaninfull :) " #Message1
msg * /server:$computer "And something more meaningfull even ;)" #Message2
echo "$computer, avisado y recibido por $Usuario" >> "C:\Users\XXXX\Desktop\Done.txt"
}
else
{
$output = #{'Computer_Name / IP' = $computer }
$output.Usuario_Conectado = "OFFLINE"
echo $Computer >> "C:\Users\XXXX\Desktop\ListOffline.txt"
}
[PSCustomObject]$output
}
The message part works as expected and the computers, when logged, can see the messages BUT:
I can't get the names of the remote logged users on the computer's list, I get mine and the rest as empty names. In some weird variation I could, but can't find what the problem is anymore. As pre-requisites I allowed Win RM and firewall config:
I went to the computers where this will be sent and modified the registry: "reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server" /v "AllowRemoteRPC" /t "REG_DWORD" /d "1" /f"
and opened the sshnet to allow remote access to get the names: "netsh firewall set service remoteadmin enable"
In the image in the orange circles should go the remote users logged but I get empty names
I want to send the message IF the user is logged, as remote user or on the console, now the txt file of "Done" gets written even when the user don't receive any message...
So, there should be an issue on this call: "(Get-WmiObject -Class win32_computersystem -ComputerName $computer).UserName" to get the names, this worked but I can't get the names on the remote logged users computers anymore... any idea or suggestion? I can accept get those in other form if is available and easier.
Thanks and best,
Juan
If you're using command line utilities anyway, why not use quser as its designed to get the information you need:
Function FindLogons {
Param (
[string]$Computer
)
Try {
$Query = quser /server:$Computer 2>&1
If ($LASTEXITCODE -ne 0) {
$ErrorDetail = $Query.Exception.Message
Switch -Wildcard ($Query) {
'*[1722]*' {
$Status = 'Remote RPC not enabled'
}
'*[5]*' {
$Status = 'Access denied'
}
'No User exists for*' {
$Status = 'No logged on users found'
}
default {
$Status = 'Error'
}
}
$result = [pscustomobject]#{ErrorStatus = $Status;ErrorMessage = $ErrorDetail}
$result.PSObject.Properties | ForEach-Object {
Write-Host $_.Name "`t" $_.Value
}
Return
}
Else {
Write-Host "Query complete"
}
$Query | Select-Object -Skip 1 -ErrorAction Stop | ForEach-Object {
$CurrentLine = $_.Trim() -Replace '\s+',' ' -Split '\s'
# If session is disconnected different fields will be selected
If ($CurrentLine[2] -eq 'Disc') {
$SearchResult = [pscustomobject]#{
UserName = $CurrentLine[0];
SessionName = $null;
Id = $CurrentLine[1];
State = $CurrentLine[2];
IdleTime = $CurrentLine[3];
LogonTime = $CurrentLine[4..($CurrentLine.GetUpperBound(0))] -join ' '
}
# LogonTime = $CurrentLine[4..6] -join ' ';
}
Else {
$SearchResult = [pscustomobject]#{
UserName = $CurrentLine[0];
SessionName = $CurrentLine[1];
Id = $CurrentLine[2];
State = $CurrentLine[3];
IdleTime = $CurrentLine[4];
LogonTime = $CurrentLine[5..($CurrentLine.GetUpperBound(0))] -join ' '
}
}
If ($SearchResult) {
$SearchResult
}
}
}
Catch {
Write-Host "Error: $_"
}
Write-Host "Done"
}

How to Load Component Services/DCOM Config SnapIn

I have a PS script to do some DCOM configuration. It works fine as long as I have the Component Services/DCOM Config snapin loaded. I want to load that programmatically so I can do all of this as part of an install package. Does anyone know how to do it? I don't know the name of the snapin to add/import.
To load the snapin I run comexp.msc -32 and click Component Services, Computers, My Computer, DCOM Configuration.
Thanks
I faced a similar problem. I couldn't find a way of loading Component services on the DCOM Config spapIn. But I found a workaround to add the user the Default DCOM Launch and Activation permissions using this powershell script:
https://www.peppercrew.nl/index.php/2012/03/set-dcom-remote-access-via-powershell/
That way, you don't need to assign the user to that particular DCOM App.
Hope this help
This is the powershell script:
PARAM(
[string]$Principal = $(throw "`nMissing -Principal DOMAIN\Group"),
$Computers = $(throw "`nMissing -Computers ('server01','server02')"))
# USAGE:
# .\Set-RemotePermission-DCOM.ps1 -Principal "DOMAIN\" -Computers ('', '',...)
#
# EXAMPLE:
# .\Set-RemotePermission-DCOM.ps1 -Principal "DOMAIN\LG-Citrix-Admins" -Computers ('CTX_DC001', 'CTX_DC002')
#
# Inspired by Karl Mitschke's post:
# http://unlockpowershell.wordpress.com/2009/11/20/script-remote-dcom-wmi-access-for-a-domain-user/
#
# And inspired Brad Turner's post:
# http://social.technet.microsoft.com/Forums/en-US/ilm2/thread/5db2707c-87c9-4bb2-a0eb-912363e2814a/
function get-sid
{
PARAM ($DSIdentity)
$ID = new-object System.Security.Principal.NTAccount($DSIdentity)
return $ID.Translate( [System.Security.Principal.SecurityIdentifier] ).toString()
}
$sid = get-sid $Principal
#DefaultLaunchPermission - Local Launch, Remote Launch, Local Activation, Remote Activation
$DCOMSDDLDefaultLaunchPermission = "A;;CCDCLCSWRP;;;$sid"
#DefaultAccessPermision - Local Access, Remote Access
$DCOMSDDLDefaultAccessPermision = "A;;CCDCLC;;;$sid"
#PartialMatch
$DCOMSDDLPartialMatch = "A;;\w+;;;$sid"
foreach ($strcomputer in $computers)
{
write-host "`nWorking on $strcomputer with principal $Principal ($sid):"
# Get the respective binary values of the DCOM registry entries
$Reg = [WMIClass]"\\$strcomputer\root\default:StdRegProv"
$DCOMDefaultLaunchPermission = $Reg.GetBinaryValue(2147483650,"software\microsoft\ole","DefaultLaunchPermission").uValue
$DCOMDefaultAccessPermission = $Reg.GetBinaryValue(2147483650,"software\microsoft\ole","DefaultAccessPermission").uValue
# Convert the current permissions to SDDL
write-host "`tConverting current permissions to SDDL format..."
$converter = new-object system.management.ManagementClass Win32_SecurityDescriptorHelper
$CurrentDCOMSDDLDefaultLaunchPermission = $converter.BinarySDToSDDL($DCOMDefaultLaunchPermission)
$CurrentDCOMSDDLDefaultAccessPermission = $converter.BinarySDToSDDL($DCOMDefaultAccessPermission)
# Build the new permissions
if (($CurrentDCOMSDDLDefaultLaunchPermission.SDDL -match $DCOMSDDLPartialMatch) -and ($CurrentDCOMSDDLDefaultLaunchPermission.SDDL -notmatch $DCOMSDDLDefaultLaunchPermission))
{
$NewDCOMSDDLDefaultLaunchPermission = $CurrentDCOMSDDLDefaultLaunchPermission.SDDL -replace $DCOMSDDLPartialMatch, $DCOMSDDLDefaultLaunchPermission
}
else
{
$NewDCOMSDDLDefaultLaunchPermission = $CurrentDCOMSDDLDefaultLaunchPermission.SDDL + "(" + $DCOMSDDLDefaultLaunchPermission + ")"
}
if (($CurrentDCOMSDDLDefaultAccessPermission.SDDL -match $DCOMSDDLPartialMatch) -and ($CurrentDCOMSDDLDefaultAccessPermission.SDDL -notmatch $DCOMSDDLDefaultAccessPermision))
{
$NewDCOMSDDLDefaultAccessPermission = $CurrentDCOMSDDLDefaultAccessPermission.SDDL -replace $DCOMSDDLPartialMatch, $DCOMSDDLDefaultAccessPermision
}
else
{
$NewDCOMSDDLDefaultAccessPermission = $CurrentDCOMSDDLDefaultAccessPermission.SDDL + "(" + $DCOMSDDLDefaultAccessPermision + ")"
}
# Convert SDDL back to Binary
write-host "`tConverting SDDL back into binary form..."
$DCOMbinarySDDefaultLaunchPermission = $converter.SDDLToBinarySD($NewDCOMSDDLDefaultLaunchPermission)
$DCOMconvertedPermissionDefaultLaunchPermission = ,$DCOMbinarySDDefaultLaunchPermission.BinarySD
$DCOMbinarySDDefaultAccessPermission = $converter.SDDLToBinarySD($NewDCOMSDDLDefaultAccessPermission)
$DCOMconvertedPermissionsDefaultAccessPermission = ,$DCOMbinarySDDefaultAccessPermission.BinarySD
# Apply the changes
write-host "`tApplying changes..."
if ($CurrentDCOMSDDLDefaultLaunchPermission.SDDL -match $DCOMSDDLDefaultLaunchPermission)
{
write-host "`t`tCurrent DefaultLaunchPermission matches desired value."
}
else
{
$result = $Reg.SetBinaryValue(2147483650,"software\microsoft\ole","DefaultLaunchPermission", $DCOMbinarySDDefaultLaunchPermission.binarySD)
if($result.ReturnValue='0'){write-host " Applied DefaultLaunchPermission complete."}
}
if ($CurrentDCOMSDDLDefaultAccessPermission.SDDL -match $DCOMSDDLDefaultAccessPermision)
{
write-host "`t`tCurrent DefaultAccessPermission matches desired value."
}
else
{
$result = $Reg.SetBinaryValue(2147483650,"software\microsoft\ole","DefaultAccessPermission", $DCOMbinarySDDefaultAccessPermission.binarySD)
if($result.ReturnValue='0'){write-host " Applied DefaultAccessPermission complete."}
}
}
#----------------------------------------------------------------------------------------------------------
trap
{
$exMessage = $_.Exception.Message
if($exMessage.StartsWith("L:"))
{write-host "`n" $exMessage.substring(2) "`n" -foregroundcolor white -backgroundcolor darkblue}
else {write-host "`nError: " $exMessage "`n" -foregroundcolor white -backgroundcolor darkred}
Exit
}
#----------------------------------------------------------------------------------------------------------
I faced the same issue and, I believe, it's because there's no equivalent 64-bit registry entry so PowerShell doesn't see it. Launching mmc compexp.msc /32 and expanding DCOM Config seems to create the entry in the background.
The work-around is to manually add the 64-bit AppID yourself which is simply done by the following code,
$appGUID = 'YOUR_APPNAME_OR_GUID'
New-PSDrive -PSProvider Registry -Name HKCR -Root HKEY_CLASSES_ROOT
New-Item -Path HKCR:\AppID\$appGUID -Value $appGUID
#New-Item -Path HKCR:\Wow6432Node\AppID\$appGUID -Value $appGUID
Remove-PSDrive HKCR
I've left the 32-bit location in the above code too although that should already exist. Once you run the above then PowerShell should be able to see the COM component,
Get-WMIObject -query ('SELECT * FROM Win32_DCOMApplicationSetting WHERE AppID = "' + $appGUID + '"') -EnableAllPrivileges
Hope this helps someone as it was driving me bananas for hours!

Writing DWORD value in HEX to Remote Registry in Powershell

I am trying to write a HEX value DWORD Key to the remote registry on a machine I target. The key lies under the HKEY_Users hive and targets the SID of the user, then the path I need. My issue lies with constantly receiving the following error:
Exception calling "SetValue" with "3" argument(s): "The type of the value object did not match the specified RegistryValueKind or the object could not be properly converted."
Here is my script; the connection to the remote registry works, as does determining the SID of the user. Can anyone see where I am going wrong?
$Value1 = "1f24db0a"
$Value2 = "062efc0a"
$remoteuser = Read-Host 'Enter Username of User'
$Comptername = Read-Host 'Enter Asset Number of User'
$userLogin = New-Object System.Security.Principal.NTAccount(“TestDomain“,$remoteuser)
$userSID = $userLogin.Translate([System.Security.Principal.SecurityIdentifier])
If (Test-Connection $Comptername -count 1) {
$subkey = $userSID.value+"\Software\SoftwareVendor\Application"
$type = [Microsoft.Win32.RegistryHive]::Users
$regkey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($type,$Computername)
$regkey.OpenSubKey($subkey, $true)
$regkey.SetValue('CommsServer1', $Value1, 'DWORD')
$regkey.SetValue('CommsServer2', $Value2, 'DWORD')
}
else
{
Write-Host "User's computer unreachable! Please try again!"
PAUSE
}
Hex-values uses 0x-prefix. Try:
$Value1 = 0x1f24db0a
$Value2 = 0x062efc0a

Powershell List Network Printers on Remote PC

I am trying to trouble shoot a GPO to deploy a printer and I need to see what network printers are on a remote machine for the current logged on user. When I do this
Get-WMIObject Win32_Printer -ComputerName PCNAME
I get a list of the local installed printers and when I try this
Get-WMIObject Win32_Printer -ComputerName PCNAME | where{$_.Name -like “*\\*”} | select sharename,name
I get nothing. Any help? I am using PowerShell 4.0 so the Get-Printer doesn't work.
I borrowed heavily from tukan's code in another thread (thanks... that code bridged a gap I had not yet been able to bridge) and modified it to my purposes.
The code below determines the logged-in user on the specified remote computer, then outputs the printers that user has listed in the Registry under HKU<SID>\Printers\Connections.
As a bonus I added a couple extra lines that output the user's currently-selected default printer.
#- BEGIN FUNCTION -------------------------------------------------------------------------------------------------
Function remote_registry_query($target, $key)
{
Try {
$registry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("Users", $target)
ForEach ($sub in $registry.OpenSubKey($key).GetSubKeyNames())
{
#This is really the list of printers
write-output $sub
}
} Catch [System.Security.SecurityException] {
"Registry - access denied $($key)"
} Catch {
$_.Exception.Message
}
}
#- END FUNCTION ---------------------------------------------------------------------------------------------------
##############################
# EXECUTION STARTS HERE
##############################
# Prep variables and get information for the function call
# set the computer name
$computer = "computer_name"
# get the logged-in user of the specified computer
$user = Get-WmiObject –ComputerName $computer –Class Win32_ComputerSystem | Select-Object UserName
$UserName = $user.UserName
write-output " "
write-output " "
write-output "Logged-in user is $UserName"
write-output " "
write-output " "
write-output "Printers are:"
write-output " "
# get that user's AD object
$AdObj = New-Object System.Security.Principal.NTAccount($user.UserName)
# get the SID for the user's AD Object
$strSID = $AdObj.Translate([System.Security.Principal.SecurityIdentifier])
#remote_registry_query -target $computer -key $root_key
$root_key = "$strSID\\Printers\\Connections"
remote_registry_query -target $computer -key $root_key
# get a handle to the "USERS" hive on the computer
$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("Users", $Computer)
$regKey = $reg.OpenSubKey("$strSID\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows")
# read and show the new value from the Registry for verification
$regValue = $regKey.GetValue("Device")
write-output " "
write-output " "
write-output "Default printer is $regValue"
write-output " "
write-output " "
[void](Read-Host 'Press Enter to continue…')

How to log into remote servers?

I currently have a VBScript that reads a list of servers, and attempts to verify the password of a specific userid. The userid is locally on that server. I am checking to see that the password is not set to the default (I want to make sure it was changed to something else).
The "list of servers" can be a mix of IP addresses, hostnames (like Rocky), or fully qualified DNS names (like rocky.bigcompany.com). The servers are a mixture of physical and virtual devices, and may or may not be on a domain.
The existing VBScript I wrote handles all this, and works fine. I'm trying to re-write this same program in Powershell, and It's not working.
Here's the function I have in VBScript that does what I want:
Function LoginToServer(Computer, username, password)
'this function will log into a server
On Error Resume next
Set locator = CreateObject("WbemScripting.SWbemLocator")
Set wmi = locator.ConnectServer(computer, "root\cimv2", username, password)
'check the error code and see if we logged in successfully
LoginRC = Err.Number
If LoginRC <> 0 Then
msg = "Could not log into server: " & CStr(computer) & " With ID: " & CStr(username)
lfo.lmsg "B", "WARN", msg
Else
msg = "Server: " & CStr(computer) & " Logged in successfully as: " & CStr(username)
lfo.lmsg "B", "INFO", msg
End If
wmi.Security_.ImpersonationLevel = 3
'return the code back to calleer
LoginToServer = LoginRC
End Function
… and here's what I've tried to do in PowerShell:
Param($ComputerName = "LocalHost")
$ErrorActionPreference = "Stop"
# Actual Code starts here
Write-Host "Attempting to ping server: $ComputerName"
$IPResult = Test-Connection -ComputerName $ComputerName -Quiet
if ($IPResult -eq "TRUE") {
Write-Host "Ping OK - now attempting to log in"
try {
$ID = "userid"
$PSW = "password"
$password = ConvertTo-SecureString $PSW -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential ($ID, $password)
$sesh = New-PSSession -ComputerName $ComputerName -Credential $cred
} catch {
Write-Host "Error caught"
$ErrorMessage = $_.Exception.Message
$FailedItem = $_.Exception.ItemName
} finally {
$Time = Get-Date
"$Time Computer: $ComputerName ERROR: $ErrorMessage ITEM: $FailedItem" |
Out-File c:\temp\TestCredScript.log -Append
}
} else {
Write-Host "Could not ping server"
}
How do I log into these remote computers with an ID and Password using PowerShell?
Your two code samples do different things. The VBScript code connects via WMI whereas the PowerShell code tries to establish a PowerShell session. For the latter you need PowerShell Remoting enabled, which you probably don't have.
While you probably may want to enable PSRemoting anyway, you can also use WMI from PowerShell. The Get-WmiObject cmdlet allows you to provide credentials and impersonation level, so you don't need to establish a connection first like you need to do with VBScript (if you want to use explicit credentials).
Example querying the Win32_Process class on a remote computer:
$computer = '...'
$username = 'userid'
$password = 'password'
$pw = ConvertTo-SecureString $password -AsPlainText -Force
$cred = New-Object Management.Automation.PSCredential ($username, $pw)
Get-WmiObject -Computer $computer -Namespace 'root\cimv2' -Class Win32_Process -Impersonation 3 -Credential $cred
See here for further information.