This is the script I have in Powershell;
$wmi = get-wmiobject -Namespace root\ccm -class sms_client -list | gm
The line above works exactly how I want.
The line below gives me completely different results because I removed the -list switch:
$wmi = get-wmiobject -Namespace root\ccm -class sms_client | gm
My question is how can I transpose the first command to VBScript. I want to be able to call the "TriggerSchedule" method.
Something like this should work (schedule ID taken from the documentation):
Set wmi = GetObject("winmgmts://./root/ccm")
scheduleID = "{00000000-0000-0000-0000-000000000001}"
For Each client In wmi.ExecQuery("SELECT * FROM SMS_Client")
client.TriggerSchedule(scheduleID)
Next
Untested, though, because I don't have SCCM at hand here.
Late to the party but try this in PowerShell:
GWMI CCM_Scheduler_ScheduledMessage -namespace root\ccm\policy\machine\actualconfig |
select-object ScheduledMessageID, TargetEndPoint |
where-object {$_.TargetEndPoint -ne "direct:execmgr"}
Returns the following, pay attention to the values which are similar to 00000000-0000-0000-0000-000000000116
ScheduledMessageID TargetEndPoint
------------------ --------------
{F83F662D-3DE6-4696-B064-701B2D86DADA} direct:UpdatesDeploymentAgent
{00000000-0000-0000-0000-000000000116} direct:StateMessageManager
Related
I'm trying to put together an expression that returns a bare value, without a table or name of the expression included.
For example, I have this line from another solution:
gwmi win32_logicaldisk -Filter "DeviceID = 'C:'" |
Format-Table #{n="Size";e={[math]::Round($_.Size/1GB,2)}}
This returns:
Size
----
475.33
How can I grab just the 475.33?
Use Select-Object instead of Format-Table:
$DiskC = gwmi win32_logicaldisk -Filter "DeviceID = 'C:'" | Select-Object #{n="Size";e={[math]::Round($_.Size/1GB,2)}}
$DiskC.Size
Format-* cmdlets return objects whose sole purpose is to provide formatting instructions to PowerShell's output-formatting system - see this answer. In short: only ever use Format-* cmdlets to format data for display, never for subsequent programmatic processing.
gwmi is the built-in alias for the Get-WmiObject cmdlet, which is obsolete.
The CIM cmdlets (e.g., Get-CimInstance) superseded the WMI cmdlets in PowerShell v3 (released in September 2012). Therefore, the WMI cmdlets should be avoided, not least because PowerShell [Core] v6+, where all future effort will go, doesn't even have them anymore. For more information, see this answer.
If you simply want to calculate a value derived from (each of) your input object(s), use the ForEach-Object cmdlet.
Therefore:
Get-CimInstance win32_logicaldisk -Filter "DeviceID = 'C:'" | ForEach-Object {
[math]::Round($_.Size / 1gb, 2)
}
Or, more simply, using an expression:
[math]::Round(
(Get-CimInstance win32_logicaldisk -Filter "DeviceID = 'C:'").Size / 1gb, 2
)
If you use your first line:
gwmi win32_logicaldisk -Filter "DeviceID = 'C:'" | Format-Table #{n="Size";e={[math]::Round($_.Size/1GB,2)}}
But Pipe it trough Select-object instead of format table then pipe it again to select object expanding the property (or just .size)
gwmi win32_logicaldisk -Filter "DeviceID = 'C:'" | Select #{n="Size";e={[math]::Round($_.Size/1GB,2)}} | select -ExpandProperty Size
Im currently running the following commands, but im running them through a foreachloop. I can currently output them both to a csv as just a list for each machine, but ideally id like to output them to a csv of all the machines in the loop, that has the apps listed along the top, then below the computername, version, etc. so that i can append the csv and just have one big csv that i can sort and filter when opened in excel.
Aditionally id also like to be able to alert if app versions are differnt than my baseline, not sure where to start on this though.
$InstalledApps = gwmi Win32Reg_AddRemovePrograms64 | Select DisplayName, Publisher, Version
$InstalledApps +=
C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe -Command {gwmi Win32Reg_AddRemovePrograms | Select DisplayName, Publisher, Version}
$InstalledApps = $InstalledApps | sort displayname | Get-Unique -AsString
$InstalledApps| Select DisplayName, Publisher, Version | ogv -PassThru | export-csv .\apps.csv
Thanks
Powershell v3. and above.
Try to use CimInstance instead of WMI where possible.
#Define a Table Array
$MasterTable = #()
#Define computer list
$Computerlist = "computer1", "computer22"
#Run Foreach to retrieve data from each computer remotely
Foreach ($computer in $Computerlist) {
$MasterTable += Get-CimInstance win32_product -Computername $Computer |
Select-Object #{Name = "Computername"; Expression = {$Computer}},
#{Name = "SoftwareName"; Expression = {$_.Caption}},
#{Name = "Publisher"; Expression = {$_.Vendor}},
#{Name = "Version"; Expression = {$_.Version}}
}
$MasterTable | export-csv .\apps.csv -NoTypeInformation -Append
This should give you one CSV export with four columns containing all the information you require (incl the computername Get-CimInstance was run against).
You can do some additional logic with the MasterTable Variable if you like before exporting to CSV
Yes, this is also possible using WMI, however I would suggest you look at migrating WMI calls to CimInstance instead using Powershell v.3 and above as WMI is legacy and is not really firewall friendly as CimInstance is for example.
But here is the code for using WMI.
#Define a Table Array
$MasterTable = #()
#Define computer list
$Computerlist = "computer1", "computer2"
#Run Foreach to retrieve data from each computer remotely
Foreach ($computer in $Computerlist) {
$MasterTable += Get-WmiObject -Class Win32_Product -Computername $Computer |
Select-Object #{Name = "Computername"; Expression = {$Computer}},
#{Name = "SoftwareName"; Expression = {$_.Caption}},
#{Name = "Publisher"; Expression = {$_.Vendor}},
#{Name = "Version"; Expression = {$_.Version}}
}
$MasterTable | export-csv c:\temp\apps.csv -NoTypeInformation -Append
I'm writing an PowerShell script to collect some data of a computer. I'm almost done, but I don't know how to get size of all the disks on the computer. I know how to do it with a couple of If statements, but I want it to automatically detect the drives, not that I have to write a new If statement if a new disk is attached. The output I want is as follows: "A:,250GB". The "A:," bit works, but not the disk size bit.
This is the code I used and tweaked, but to no avail:
$Drives = Get-WmiObject Win32_logicaldisk| ?{$_.DriveType -eq 3} | ForEach-Object {$_.name}
ForEach ($Drivename in $Drives) {
$Drivenames = Get-WMIObject -Query "Select * From win32_logicaldisk Where DriveType = '3'" -computer localhost | Select-Object DeviceID
$Drive = [Math]::Round($Drivenames.Size / 1GB)
"$Drivenames,", $Drive | Out-File "C:\HDS\HDS_DRIVES.csv" -Append
}
In addition, [Math]::Round($Drivenames.Size / 1GB) throws me an error:
Method invocation failed because [System.Object[]] does not contain a method named 'op_Division'"
You can use Calculated Property with Select-Object to make it much more simple:
Get-WmiObject Win32_logicaldisk| ? {$_.DriveType -eq 3} |
Select-Object #{N="DeviceId";E={$_.DeviceId}},`
#{N="VolumeName";E={$_.VolumeName}},`
#{N="Size";E={[Math]::Round($_.Size / 1GB)}} |
Out-File "C:\HDS\HDS_DRIVES.csv" -Append
Note that you don't need to Invoke Get-WmiObject Twice like in your example.
Why it doesn't work?
The issue is that $Drivenames contains only DeviceID (as you used Select-Object to get only that property). Therefore you're getting an error where trying to round it (as rounding nothing is not supposed to work).
How to fix it?
You have to add Size and then access it using .PropertyName:
$DriveInfo = Get-WMIObject -Query "Select * From win32_logicaldisk Where DriveType = '3'" -computer localhost | Select-Object DeviceID, Size
$DriveInfo | ForEach-Object {
$DriveSize = [Math]::Round($_.Size / 1GB)
"$($_.DeviceID),$DriveSize" | Out-File "C:\HDS\HDS_DRIVES.csv" -Append
}
How can I make it more elegant
Also, take a look at #Avshalom's answer which uses calculated property.
I have written a system maintenance script which executes basic functions that retrieve statistics from a host, writes the output to a new PSObject, then finally combines the results and converts it all to a HTML web page.
I do not seem to be able to write the output of Optimize-Volume to the pipeline, I have to use -verbose - why is this? I would like to check the results of the Optimize-Volume cmdlet by looking for the following text which is generated at the end of the -verbose output, depending on the result:-
'It is recommended that you defragment this volume.'
'You do not need to defragment this volume.'
Here is the function:-
function Get-DefragInfo {
$getwmi = Get-WmiObject -Class Win32_volume -Filter "DriveType = 3" | Where-Object {$_.DriveLetter -cne $null} -ErrorAction SilentlyContinue
$letter = $getwmi.DriveLetter -replace ':'
foreach ($drive in $getwmi)
{
$analysis = Optimize-Volume -DriveLetter $letter -Analyze
if ($analysis -like 'It is recommended that you defragment this volume.')
{
$props =[ordered]#{‘Drive Letter’=$letter
'Defrag Recommended?'='Yes'}
}
elseif ($analysis -like 'You do not need to defragment this volume.')
{
$props =#{‘Drive Letter’=$letter
'Defrag Recommended?'='No'}
}
$obj = New-Object -TypeName PSObject -Property $props
Write-Output $obj
}
}
How do I capture the output I need?
Thanks in advance.
In PowerShell 3.0 and onward, you can use the stream redirection operator > to capture the Verbose ouput to a variable:
# Merge stream 4 (Verbose) into standard Output stream
$analysis = &{Optimize-Volume -DriveLetter $letter -Analyze -Verbose} 4>&1
# Check the "Message" property of the very last VerboseRecord in the output
if($analysis[-1].Message -like "*It is recommended*")
{
# defrag
}
else
{
# don't defrag
}
If we Get-Help Optimize-Volume -full we'll see the cmdlet has no output.
Some searching lead me to this Microsoft Scripting Guys article that pointed out using the following to check if Defrag is needed.
(gwmi -Class win32_volume -Filter "DriveLetter = 'C:'").DefragAnalysis()
Knowing this, we can easily make an IF Statement.
$DefragCheck = (gwmi -Class win32_volume -Filter "DriveLetter = 'C:'").DefragAnalysis() |
Select DefragRecommended
IF($DefragCheck){"Defrag recommended"}ELSE{"Defrag is not needed."}
It's helpful to pipe cmdlets to Get-Member in order to see if there are any options available. In the above example, we can pipe gwmi -Class win32_volume -Filter "DriveLetter = 'C:'" to Get-Member and find the DefragAnalysis method, which we use dotted notation to access (wrap the Get-WmiObject in () then use a . and the method name followed by (), it looks confusing until you try it a couple times!)
Thanks, I went for the verbose redirection option and it seems to be working well. My method is not the cleanest way of doing it I understand, but it works for me.
I like the second option also, I'm going to look at using this once the script is complete and functionality is proofed.
Thanks for your help once again.
I am trying to find a way of retrieving the date/time of which the last windows update was either installed, or checked for.
So far I have found a function that allows to list recent Windows Updates, but it is far too much data and too bloated for such a simple function. Secondly I have tried to access the registry although I am having no luck in retriving the value I am after.
I am testing this on a Windows 10 Machine although the software will probably reside on Windows Server 2012 R2.
Here is an example of some of the code I have tried:
$key = “SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\Results\Install”
$keytype = [Microsoft.Win32.RegistryHive]::LocalMachine
$RemoteBase = [Microsoft.Win32.RegistryKey]::OpenBaseKey($keytype,"My Machine")
$regKey = $RemoteBase.OpenSubKey($key)
$KeyValue = $regkey.GetValue(”LastSuccessTime”)
$System = (Get-Date -Format "yyyy-MM-dd hh:mm:ss")
Also, just trying the Get-ChildItem
$hello = Get-ChildItem -Path “hkcu:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\”
foreach ($a in $hello) {
$a
}
I've checked in regedit and this key does not exist. Going to the "Windows Update" path shows only App Updates and not Windows updates.
EDIT
I seem to be closer to my goal with this line:
Get-HotFix | Where {$_.InstallDate -gt 30}
However how to I only retrive those of which have been installed in the last 30 days? And this doesnt show many results, even using Select $_.InstallDate
an option :
gwmi win32_quickfixengineering |sort installedon -desc
Another alternative, using the com object Microsoft.Update.Session can be find here : https://p0w3rsh3ll.wordpress.com/2012/10/25/getting-windows-updates-installation-history/
in short :
$Session = New-Object -ComObject Microsoft.Update.Session
$Searcher = $Session.CreateUpdateSearcher()
$HistoryCount = $Searcher.GetTotalHistoryCount()
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa386532%28v=vs.85%29.aspx
$Searcher.QueryHistory(0,$HistoryCount) | ForEach-Object {$_}
Here you have how to know the date and time of the last Windows update in a single line of Powershell:
(New-Object -com "Microsoft.Update.AutoUpdate"). Results | fl
You also have the following script to check it massively in Windows Server:
$ servers = Get-ADComputer -Filter {(OperatingSystem-like "* windows * server *") -and (Enabled -eq "True")} -Properties OperatingSystem | Sort Name | select -Unique Name
foreach ($ server in $ servers) {
write-host $ server.Name
Invoke-Command -ComputerName $ server.Name -ScriptBlock {
(New-Object -com "Microsoft.Update.AutoUpdate"). Results}
}
Extracted from: https://www.sysadmit.com/2019/03/windows-update-ver-fecha-powershell.html
Get-HotFix |?{$_.InstalledOn -gt ((Get-Date).AddDays(-30))}
Using PowerShell, you can get the date of the las Windows update like this:
$lastWindowsUpdate = (Get-Hotfix | Sort-Object -Property InstalledOn -Descending | Select-Object -First 1).InstalledOn