/
CMOperations.psm1
959 lines (914 loc) · 33.4 KB
/
CMOperations.psm1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
<#
.SYNOPSIS
Configuration Manager Module for basic configuration manager operations and commands. This includes commands that interact both with the SCCM client
and commands that interact directly with the SMS Provider on the configuration Manager server.
.DESCRIPTION
This module has been written over a year and a half and will continue to grow and expand as new pieces are added to it.
.EXAMPLE
import-module CMOperations
.NOTES
FileName: CMOperations.psm1
Author: Jordan Benzing
Contact: @JordanTheItGuy
Created: 2/23/2018
Updated: 8/22/2018
Version history:
1.0.0.6 - Updated to use new coding standard for myself
1.0.0.7 - Updated with additional helper functions from other areas
1.0.0.8 - Updated and added a new function that retrieves the CMCollection object path
#TODO: - Add support for CollectionID
#TODO: - Add support for no Site ServerName
#TODO: - Add support for using AD to determine SMS provider (Attempt to determine at least)
#TODO: - Add support to return the entire object instead of just the object path?
#>
############################################
#region TriggerClientActions
function Start-SoftwareUpdateScan
{
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[string]$ComputerName,
[Parameter(Mandatory = $False)]
[switch]$ConnectionTest
)
if ($ConnectionTest)
#If the connection test switch is set start the process to test the network connectivity and run the function.
{
if (Test-Connectivity -ComputerName $ComputerName)
#Validates that the machine can be conneted to. If it passes will enter the try statement to start a software update Scan Cycle.
{
try
{
Start-ActionSoftwareUpdateScan -ComputerName $ComputerName -ErrorAction Stop
}
Catch
{
throw "$ComputerName failed to start software update Scan"
#Catch and throw an error statement if anything goes wrong.
}
}
}
else
#In the event a connection test is NOT requested - attempt to perform the action without triggering a connection test. Not reccomended.
{
try
{
Start-ActionSoftwareUpdateScan -ComputerName $ComputerName
}
Catch
{
throw "$ComputerName failed to start software update Scan"
#Catch and throw an error statement if anything goes wrong.
}
}
}
function Start-HardwareInventoryScan
{
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[string]$ComputerName,
[Parameter(Mandatory = $False)]
[switch]$ConnectionTest,
[Parameter(Mandatory = $false)]
[switch]$ForceReset
)
if ($ConnectionTest)
#If the connection test switch is set start the process to test the network connectivity and run the function.
{
if (Test-Connectivity -ComputerName $ComputerName)
#Validates that the machine can be conneted to. If it passes will enter the try statement to start a Hardware Inventory Scan
{
if ($ForceReset)
{
try
{
Start-ActionResetHardwareInventory -ComputerName $ComputerName -ErrorAction Stop
Start-ActionHardwareInventoryScan -ComputerName $ComputerName -ErrorAction Stop
}
Catch
{
throw "$ComputerName failed to clear the hardware inventory and start hardware inventory cycle"
#Catch and throw an error statement if anything goes wrong.
}
}
else
{
try
{
Start-ActionHardwareInventoryScan -ComputerName $ComputerName -ErrorAction Stop
}
Catch
{
throw "$ComputerName failed to start a hardware inventory cycle"
#Catch and throw an error statement if anything goes wrong.
}
}
}
}
else
#In the event a connection test is NOT requested - attempt to perform the action without triggering a connection test. Not reccomended.
{
if ($ForceReset)
{
try
{
Start-ActionResetHardwareInventory -ComputerName $ComputerName -ErrorAction Stop
#Runs the action to reset the hardware inventory
Start-ActionHardwareInventoryScan -ComputerName $ComputerName -ErrorAction Stop
#Runs the action to start a hardware inventory scan.
}
Catch
{
throw "$ComputerName failed to clear the hardware inventory and start hardware inventory cycle"
#Catch and throw an error statement if anything goes wrong.
}
}
else
{
try
{
Start-ActionHardwareInventoryScan -ComputerName $ComputerName -ErrorAction Stop
#Runs the action to start collecting hardware inventory infomration.
}
Catch
{
throw "$ComputerName failed to start a hardware inventory cycle"
#Catch and throw an error statement if anything goes wrong.
}
}
}
}
#endregion TriggerClientActions
############################################
############################################
#region TriggerProviderInteraction
function Get-CMOCollectionPath{
[cmdletbinding()]
param(
[Parameter(Mandatory = $true)]
[string]$CollectionName,
[Parameter(Mandatory = $false)]
[string]$SiteServerName
)
if($SiteServerName){
try{
Write-Verbose -Message "Now starting WINRM test to remote server"
$WINRMTEST = Test-WinRM -ComputerName $SiteServerName -ErrorAction Stop
Write-Verbose -Message "WinRM Test passed"
Write-Verbose -Message "Now attempting to retrieve information"
$ObjectPath = Start-CMOCollectionPath -CollectionName $CollectionName -SiteServerName $SiteServerName -ErrorACtion Stop
Write-Verbose -Message "Succesfully retrieved information"
return $ObjectPath
}
catch{
Write-Error -Message "An error has occurred testing the WINRM provider to the specified site server $($_.Exception.Message)"
}
}
if(!($SiteServerName)){
try{
Start-CMOCollectionPath -CollectionName $CollectionName -SiteServerName $SiteServerName -ErrorACtion Stop
}
catch{
Write-Error -Message "An error occured using the local hosts WMI Provider $($_.Exception.Message)"
}
}
}
#endregion TriggerProviderInteraction
############################################
############################################
#region SoftwareUpdateActions
function Get-UpdatesInSoftwareCenter
{
[CmdletBinding()]
param
(
[parameter(Mandatory = $true)]
[string]$ComputerName,
[Parameter(Mandatory = $False)]
[switch]$ConnectionTest
)
If ($ConnectionTest)
#If the connection test switch is set start the process to test the network connectivity and run the function.
{
If (Test-Connectivity -ComputerName $ComputerName)
#If the computer passes the connection test enter the if statement to try to get the updates that are currently available to install in software center.
{
try
{
Write-Verbose "Connecting to $ComputerName to query for updates in Software Center"
Get-WmiObject -ComputerName $ComputerName -ErrorAction Stop -Query 'SELECT * FROM CCM_SoftwareUpdate' -Namespace ROOT\ccm\ClientSDK -Verbose:$false | ft ArticleID, Name -AutoSize
#Connects to the WMI Client SDK namespace and returns teh updates that are currently pending installation or are displayed in software center.
}
catch
{
Throw "Error with returning any data from $ComputerName"
#Catch Error in the event something goes wrong connecting to WMI.
}
}
}
else
#If a connection test switch was NOT set this will run the process without performing a connection test to device. This is not recccommended.
{
Write-Verbose "Connecting to $ComputerName to query for updates in Software Center"
Get-WmiObject -ComputerName $ComputerName -Query 'SELECT * FROM CCM_SoftwareUpdate' -Namespace ROOT\ccm\ClientSDK -Verbose:$false | ft ArticleID, Name -AutoSize
#Connects to the WMI Client SDK namespace and returns the updates that are currently pending installation or are displayed in software center.
}
}
function Install-UpdatesInSoftwareCenter
{
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[String]$ComputerName,
[Parameter(Mandatory = $false)]
[switch]$AllUpdates,
[Parameter(Mandatory = $false)]
[array]$ArticleID,
[Parameter(Mandatory = $False)]
[switch]$ConnectionTest
)
If ($ConnectionTest)
#If the connection test switch is set start the process to test the network connectivity and run the function.
{
If (Test-Connectivity -ComputerName $ComputerName)
#If the computer passes the connection test enter the if statement and based on parameters install the missing software updates.
{
if ($AllUpdates)
#The All Updates Switch is flagged then enter and try
{
try
{
Write-Verbose "All Updates was selected for $ComputerName attempting to install all available updates in software center"
$Updates = [System.Management.ManagementObject[]](Get-WmiObject -ComputerName $ComputerName -ErrorAction Stop -Query 'SELECT * FROM CCM_SoftwareUpdate' -Namespace ROOT\ccm\ClientSDK -Verbose:$false)
#build an array in the variable $Updates that contains all udpate articles
([wmiclass]"\\$ComputerName\ROOT\ccm\ClientSDK:CCM_SoftwareUpdatesManager").InstallUpdates([System.Management.ManagementObject[]]$Updates) | Out-Null
#Using the older WMI methodology run the install updates wmi method to install allupdates in the $updates object.
}
catch
#If an error occurs throw terminating catch event.
{
throw "Remote installation of updates failed."
}
}
ElseIf ($ArticleID)
#array of articleID's provided by the user.
{
try
{
Write-Verbose "$ArticleID were selected for $ComputerName attempting to install updates"
$Updates = [System.Management.ManagementObject[]](Get-WmiObject -ComputerName $ComputerName -ErrorAction Stop -Query 'SELECT * FROM CCM_SoftwareUpdate' -Namespace ROOT\ccm\ClientSDK -Verbose:$false) | Where-Object { $_.ArticleID -in $ArticleID }
#build an array in the variable $Updates that contains all udpate articles that are in the articleID list provided by the user.
([wmiclass]"\\$ComputerName\ROOT\ccm\ClientSDK:CCM_SoftwareUpdatesManager").InstallUpdates([System.Management.ManagementObject[]]$Updates) | Out-Null
#Using the older WMI methodology run the install updates wmi method to install allupdates in the $updates object.
}
catch
#In an error occurs throw a terminating catch event.
{
throw "Remote installation of updates failed"
}
}
}
}
else
#See Comments above - runs exactly the same but does not perform connection test.
{
if ($AllUpdates)
{
try
{
Write-Verbose "All Updates was selected for $ComputerName attempting to install all available updates in software center"
$Updates = [System.Management.ManagementObject[]](Get-WmiObject -ComputerName $ComputerName -Query 'SELECT * FROM CCM_SoftwareUpdate' -Namespace ROOT\ccm\ClientSDK -Verbose:$false)
#build an array in the variable $Updates that contains all udpate articles
([wmiclass]"\\$ComputerName\ROOT\ccm\ClientSDK:CCM_SoftwareUpdatesManager").InstallUpdates([System.Management.ManagementObject[]]$Updates) | Out-Null
#Using the older WMI methodology run the install updates wmi method to install allupdates in the $updates object.
}
catch
#If an error occurs throw terminating catch event.
{
throw "Remote installation of updates failed."
}
}
ElseIf ($ArticleID)
{
try
{
Write-Verbose "$ArticleID were selected for $ComputerName attempting to install updates"
$Updates = [System.Management.ManagementObject[]](Get-WmiObject -ComputerName $ComputerName -Query 'SELECT * FROM CCM_SoftwareUpdate' -Namespace ROOT\ccm\ClientSDK -Verbose:$false) | Where-Object { $_.ArticleID -in $ArticleID }
#build an array in the variable $Updates that contains all udpate articles that are in the articleID list provided by the user.
([wmiclass]"\\$ComputerName\ROOT\ccm\ClientSDK:CCM_SoftwareUpdatesManager").InstallUpdates([System.Management.ManagementObject[]]$Updates) | Out-Null
#Using the older WMI methodology run the install updates wmi method to install allupdates in the $updates object.
}
catch
#If an error occurs throw terminating catch event.
{
throw "Remote installation of updates failed"
}
}
}
}
#endregion SoftwareUpdateActions
############################################
############################################
#region GetClientInformation
function Get-NextAvailableMW
{
[CmdletBinding()]
param
(
[parameter(Mandatory = $true)]
[string]$ComputerName,
[Parameter(Mandatory = $False)]
[switch]$ConnectionTest,
[Parameter(Mandatory = $False)]
[switch]$SoftwareMW,
[Parameter(Mandatory = $False)]
[switch]$AllProgramsMW,
[Parameter(Mandatory = $False)]
[switch]$ProgramsMW
)
if ($ConnectionTest)
#If the connection test switch is set start the process to test the network connectivity and run the function.
{
if (Test-Connectivity -ComputerName $ComputerName)
#If the test connection is passed enter the step to look for maintenance window switches are detected.
{
if ($AllProgramsMW)
#If the All Programs MW is selected find the next available ALL PROGRAMS maintenance window.
{
try
{
Get-ActionNextAvailableMW -ComputerName $ComputerName -AllProgramsMW -ErrorAction Stop
}
catch
#catches and throws a terminating error if the remote WMI call fails.
{
throw "An Error has occured retriving window information"
}
}
if ($SoftwareMW)
#if the SoftwareMW is selected finds the next available software maintenance window for the device.
{
try
{
Get-ActionNextAvailableMW -ComputerName $ComputerName -SoftwareMW -ErrorAction Stop
}
catch
#catches and throws a terminating error if the remote WMI call fails.
{
throw "An Error has occured retriving window information"
}
}
if ($ProgramsMW)
#if the ProgramsMW is selected finds the next available Programs window for the device.
{
try
{
Get-ActionNextAvailableMW -ComputerName $ComputerName -ProgramsMW -ErrorAction Stop
}
catch
{
throw "An Error has occured retriving window information"
}
}
if($SoftwareMW -eq $false -and $AllProgramsMW -eq $false -and $ProgramsMW -eq $false)
{
try
{
Get-ActionNextAvailableMW -ComputerName $ComputerName -ErrorAction Stop
}
catch
#catches and throws a terminating error if the remote WMI call fails.
{
throw "An Error has occured retriving window information"
}
}
}
}
else
#See above comments. Performs same actions without running the connection test first. Might later be created as a sub function that is called.
{
if ($AllProgramsMW)
{
try
{
Get-ActionNextAvailableMW -ComputerName $ComputerName -AllProgramsMW -ErrorAction Stop
}
catch
{
throw "An Error has occured retriving window information"
}
}
if ($SoftwareMW)
{
try
{
Get-ActionNextAvailableMW -ComputerName $ComputerName -SoftwareMW -ErrorAction Stop
}
catch
{
throw "An Error has occured retriving window information"
}
}
if ($ProgramsMW)
{
try
{
Get-ActionNextAvailableMW -ComputerName $ComputerName -ProgramsMW -ErrorAction Stop
}
catch
{
throw "An Error has occured retriving window information"
}
}
if ($SoftwareMW -eq $false -and $AllProgramsMW -eq $false -and $ProgramsMW -eq $false)
{
try
{
Get-ActionNextAvailableMW -ComputerName $ComputerName -ErrorAction Stop
}
catch
{
throw "An Error has occured retriving window information"
}
}
}
}
function Get-LastSoftwareUpdateScan
{
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[string]$ComputerName,
[Parameter(Mandatory = $False)]
[switch]$ConnectionTest
)
if ($ConnectionTest)
#If the connection test switch is set start the process to test the network connectivity and run the function.
{
if (Test-Connectivity -ComputerName $ComputerName)
#If the connect test is passed enters the IF statement and trys to run the function to get the last software Update Scan Cycle run time and location run against.
{
try
{
#Calls the Action for getting the last software update scan.
Get-ActionLastSoftwareUpdateScan -ComputerName $ComputerName -ErrorAction Stop
}
catch
#Cathes terminating errors and throws an event.
{
throw "Something went wrong collecting the Software Update Scan Time"
}
}
}
else
#if the conection test was not called for runs the function without running the connection test. - Can be optimized later by creating wrapper/action function.
{
try
{
Get-ActionLastSoftwareUpdateScan -ComputerName $ComputerName -ErrorAction Stop
}
catch
{
throw "Something went wrong collecting the Software Update Scan Time"
}
}
}
function Get-LastHardwareScan
{
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[string]$ComputerName,
[Parameter(Mandatory = $False)]
[switch]$ConnectionTest
)
if ($ConnectionTest)
#If the connection test switch is set start the process to test the network connectivity and run the function.
{
if (Test-Connectivity -ComputerName $ComputerName)
#If the connection test function is passed enter the function to attempt to get the last time a hardware scan was run.
{
try
{
Get-ActionLastHardwareScan -ComputerName $ComputerName -ErrorAction Stop
}
Catch
#In the event of an error terminate and throw.
{
throw "Unable to get last hardware scan run time"
}
}
else
{
throw "Failed collection test to remote machine"
}
}
else
#Same function as above but tun without connection test - could be optimized at a later date using a 'rapper' function.
{
try
{
Get-ActionLastHardwareScan -ComputerName $ComputerName -ErrorAction Stop
}
Catch
{
throw "Unable to get last hardware scan run time"
}
}
}
#endregion GetClientInformation
############################################
############################################
#region ActionFunctions
#Fucntions within this region contain the actual action that is being performed. These functions are not listed in the manifest as they are hidden behind the wrapper call functions.
#Action Function for starting a Software Update Scan
function Start-ActionSoftwareUpdateScan{
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[string]$ComputerName
)
Write-Verbose -message "Attempting to start a Software Update Scan Cycle"
Invoke-WmiMethod -ComputerName $ComputerName -ErrorAction Stop -Namespace root\ccm -Class SMS_CLIENT -Name TriggerSchedule "{00000000-0000-0000-0000-000000000113}" | Out-Null
#Uses the invoke command to start scheduled action 113 - Software Update Scan Cycle
Write-Verbose -Message "The computer has started a software update scan cycle."
#When verbose flag is triggered notifies the user that the cycle has been started.
}
#Action function for starting a Hardware Inventory Scan Cycle
function Start-ActionHardwareInventoryScan{
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[string]$ComputerName
)
Write-Verbose -Message "Attempting to invoke a hardware inventory cycle"
Invoke-WMIMethod -ComputerName $ComputerName -ErrorAction Stop -Namespace root\ccm -Class SMS_CLIENT -Name TriggerSchedule "{00000000-0000-0000-0000-000000000001}" | Out-Null
#Uses the invoke command to start scheduled action 001 - Hardware Inventory Scan Cycle
Write-Verbose -Message "The computer has started a hardware inventory cycle."
#When verbose flag is triggered notifies the user that the cycle has been started.
}
#Action Function for getting the next available MW.
function Get-ActionNextAvailableMW{
[CmdletBinding()]
param
(
[parameter(Mandatory = $true)]
[string]$ComputerName,
[Parameter(Mandatory = $False)]
[switch]$ConnectionTest,
[Parameter(Mandatory = $False)]
[switch]$SoftwareMW,
[Parameter(Mandatory = $False)]
[switch]$AllProgramsMW,
[Parameter(Mandatory = $False)]
[switch]$ProgramsMW
)
if ($AllProgramsMW)
#If the All Programs MW is selected find the next available ALL PROGRAMS maintenance window.
{
try
{
Write-Verbose -Message "Attempting to connect to $ComputerName and retrieve next available ALL PROGRAMS MAINTENANCE WINDOW"
$Window = Get-WmiObject -ComputerName $ComputerName -ErrorACtion Stop -Namespace root\ccm\clientsdk -ClassName CCM_ServiceWindow | Where-Object{ $_.type -eq 1 } | ForEach-Object{ [Management.ManagementDateTimeConverter]::ToDateTime($_.StartTime) } | Sort $_.StartTime | Select-Object -First 1
#Gets the Maintenance Window time from WMI And converts it to a date time object.
$Message = "Next available ALL PROGRAMS window for $ComputerName is " + $Window
$Message
#Returns and displays the window information to the screen.
}
catch
#catches and throws a terminating error if the remote WMI call fails.
{
throw "An Error has occured retriving window information"
}
}
if ($SoftwareMW)
#if the SoftwareMW is selected finds the next available software maintenance window for the device.
{
try
{
Write-Verbose -Message "Attempting to connect to $ComputerName and retrieve next available SOFTWARE UPDATES MAINTENANCE WINDOW"
$Window = Get-WmiObject -ComputerName $ComputerName -Namespace root\ccm\clientsdk -ClassName CCM_ServiceWindow | Where-Object{ $_.type -eq 4 } | ForEach-Object{ [Management.ManagementDateTimeConverter]::ToDateTime($_.StartTime) } | Sort $_.StartTime | Select-Object -First 1
#Gets the next available maintenance window from WMI of type Software Update and converts it to a datetime object
$Message = "Next available SOFTWARE UPDATES MAINTENANCE window for $ComputerName is " + $Window
$Message
#Returns and displays the window information to the screen.
}
catch
#catches and throws a terminating error if the remote WMI call fails.
{
throw "An Error has occured retriving window information"
}
}
if ($ProgramsMW)
#if the ProgramsMW is selected finds the next available Programs window for the device.
{
try
{
Write-Verbose -Message "Attempting to connect to $ComputerName and retrieve next available PROGRAMS MAINTENANCE WINDOW"
$window = Get-WmiObject -ComputerName $ComputerName -Namespace root\ccm\clientsdk -ClassName CCM_ServiceWindow | Where-Object{ $_.type -eq 2 } | ForEach-Object{ [Management.ManagementDateTimeConverter]::ToDateTime($_.StartTime) } | Sort $_.StartTime | Select-Object -First 1
$Message = "Next available PROGRAMS MAINTENANCE window for $ComputerName is " + $Window
$Message
}
catch
{
throw "An Error has occured retriving window information"
}
}
if ($SoftwareMW -eq $false -and $AllProgramsMW -eq $false -and $ProgramsMW -eq $false)
{
try
{
Write-Verbose -Message "Attempting to connect to $ComputerName and retrieve next available MAINTENANCE WINDOW OF ANY TYPE"
$Window = Get-WmiObject -ComputerName $ComputerName -Namespace root\ccm\clientsdk -ClassName CCM_ServiceWindow | Where-Object{ $_.type -eq 2 -or $_.Type -eq 1 -or $_.Type -eq 4 } | ForEach-Object{ [Management.ManagementDateTimeConverter]::ToDateTime($_.StartTime) } | Sort $_.StartTime | Select-Object -First 1
#Gets the next available Programs Maintenance window from WMI and converts it to a datetime object
$Message = "Next available MAINTEANNCE window of any type for $ComputerName is " + $Window
$Message
#Returns and displays the window information to the screen.
}
catch
#catches and throws a terminating error if the remote WMI call fails.
{
throw "An Error has occured retriving window information"
}
}
}
#Action Function for getting the last time a software update Scan was performed and the server it was run against.
function Get-ActionLastSoftwareUpdateScan{
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[string]$ComputerName
)
Write-Verbose -Message "Attempting to gather Last Scanned WSUS Server name and time"
$LastScanTime = Get-WmiObject -ComputerName $ComputerName -ErrorAction Stop -Namespace "Root\ccm\SCanAgent" -ClassName CCM_ScanUpdateSourceHistory | ForEach-Object { [Management.ManagementDateTimeConverter]::ToDateTime($_.LastCompletionTime) }
#Connects to WMI And returns the date time object of the last time a WSUS scan was run.
$LastServerScanned = Get-WmiObject -computer $ComputerName -ErrorAction Stop -Namespace root\ccm\softwareupdates\wuahandler -Class CCM_updatesource | Select-Object ContentLocation
#Connects to WMI and returns the last server that a software update scan was run against.
$Message = "Your computer $Computername last scanned at " + $LastScanTime + " against the server " + $LastServerScanned.ContentLocation
$Message
#Returns the message to the screen with the information of last run time and server name.
}
#Action Function for getting the last time a hardware San was run.
function Get-ActionLastHardwareScan{
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[string]$ComputerName
)
Write-Verbose -Message "Attempting to connect and retrieve the instance for Hardware Inventory Information"
$obj = Get-WmiObject -computername $ComputerName -Namespace "root\ccm\invagt" -Class InventoryActionStatus -ErrorAction Stop | Where-Object { $_.InventoryActionID -eq "{00000000-0000-0000-0000-000000000001}" } | select PsComputerName, LastCycleStartedDate, LastReportDate
#Get the WMI Instance for the hardware scan information.
Write-Verbose -Message "Retrieved WMI Instance for Hardware Scan Information"
$LastHWRun = $ComputerName + " last attempted Hardware inventory on " + [Management.ManagementDateTimeConverter]::ToDateTime($obj.LastCycleStartedDate)
#Convert the instance information into a date time object and send the data back to the screen.
$LastHWRun
}
function Start-ActionResetHardwareInventory{
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[string]$ComputerName
)
Write-Verbose -Message "Attemptiong to remove the action status for Hardware Inventory this will force a FULL Hardware Inventory"
Get-WmiObject -computername $ComputerName -Namespace "root\ccm\invagt" -Class InventoryActionStatus -ErrorAction Stop | where { $_.InventoryActionID -eq "{00000000-0000-0000-0000-000000000001}" } | Remove-WmiObject
#Gets the WMI Instance for the last action status and then deletes it.
Write-Verbose -Message "Removed the last Action Status for Hardware Scan"
}
function Start-CMOCollectionPath{
[CmdletBinding(DefaultParameterSetName = "Non")]
param(
[Parameter(Mandatory = $true , ParameterSetName = "CollectionName",Position = 0)]
[string]$CollectionName,
[parameter(Mandatory = $true)]
[string]$SiteServerName,
[Parameter(HelpMessage = $true , ParameterSetName = "CollectionID",Position = 0)]
[string]$CollectionID
)
if($SiteServerName){
Write-Verbose -Message "Initializing command"
$CollectionObject = Invoke-Command -ComputerName $SiteServerName -ScriptBlock {$CollectionName = "$($CollectionName)";Get-WmiObject -Namespace "Root\sms\$((Get-WmiObject -namespace "root\sms" -class "__Namespace").Name)" -Query "select *from SMS_Collection where Name = '$($args[0])'"} -ArgumentList $CollectionName
return $CollectionObject.ObjectPath
}
}
#endregion ActionFunctions
############################################
############################################
#region HelperFunctions
function Test-Connectivity
#Test Connection function. All network tests should be added to this for a full connection test. Returns true or false.
{
[CmdletBinding()]
param
(
[parameter(Mandatory = $true)]
[string]$ComputerName
)
Try
#Try each connection test. If there is a connection test that you do not want to use remove it by commenting out the line.
{
Test-Ping -ComputerName $ComputerName -ErrorAction Stop
Test-AdminShare -ComputerName $ComputerName -ErrorAction Stop
Test-WinRM -ComputerName $ComputerName -ErrorAction Stop
Write-Verbose -Message "$ComputerName has passed all connection tests"
return $true
}
CATCH
{
$ConnectionStatus = $false
Write-Verbose "$ComputerName failed a connection test."
return $false
}
}
function Test-Ping
#Test ping for computer.
{
[CmdletBinding()]
param
(
[parameter(Mandatory = $true)]
[string]$ComputerName
)
$PingTest = Test-Connection -ComputerName $ComputerName -BufferSize 8 -Count 1 -Quiet
If ($PingTest)
{
Write-Verbose "The Ping test for $ComputerName has PASSED"
}
Else
{
Write-Verbose "$ComputerName failed ping test"
throw [System.Net.NetworkInformation.PingException] "$ComputerName failed ping test."
}
}
function Test-AdminShare
#Test Conection to admin C$ share.
{
[CmdletBinding()]
param
(
[parameter(Mandatory = $true)]
[string]$ComputerName
)
$AdminShare = "\\" + $ComputerName + "\C$"
$AdminAccess = Test-Path -Path $AdminShare -ErrorAction Stop
if ($AdminAccess)
{
Write-Verbose "The admin share connection test $ComputerName has PASSED"
$ConnectionStatus = $true
}
Else
{
Write-Verbose "$ComputerName admin share not found"
throw [System.IO.FileNotFoundException] "$ComputerName admin share not found"
}
}
function Test-WinRM
#Test WinRM.
{
[CmdletBinding()]
param
(
[parameter(Mandatory = $true)]
[string]$ComputerName
)
Try
{
Test-WSMan -computername $ComputerName -ErrorAction Stop
Write-Verbose "The WINRM check for $ComputerName has PASSED"
}
Catch
{
throw [System.IO.DriveNotFoundException] "$ComputerName cannot be connected to via WINRM"
}
}
function Get-CMModule
#This application gets the configMgr module
{
[CmdletBinding()]
param()
Try
{
Write-Verbose "Attempting to import SCCM Module"
#Retrieves the fcnction from ConfigMgr installation path.
Import-Module (Join-Path $(Split-Path $ENV:SMS_ADMIN_UI_PATH) ConfigurationManager.psd1) -Verbose:$false
Write-Verbose "Succesfully imported the SCCM Module"
}
Catch
{
Throw "Failure to import SCCM Cmdlets."
}
}
function Test-ConfigMgrAvailable
#Tests if ConfigMgr is availble so that the SMSProvider and configmgr cmdlets can help.
{
[CMdletbinding()]
Param
(
[Parameter(Mandatory = $false)]
[bool]$Remediate
)
try
{
if((Test-Module -ModuleName ConfigurationManager -Remediate:$true) -eq $false)
#Checks to see if the Configuration Manager module is loaded or not and then since the remediate flag is set automatically imports it.
{
throw "You have not loaded the configuration manager module please load the appropriate module and try again."
#Throws this error if even after the remediation or if the remediation fails.
}
write-Verbose "ConfigurationManager Module is loaded"
Write-Verbose "Checking if current drive is a CMDrive"
if((Get-location).Path -ne (Get-location -PSProvider 'CmSite').Path)
#Checks if the current location is the - PS provider for the CMSite server.
{
if($Remediate)
#If the remediation field is set then it attempts to set the current location of the path to the CMSite server path.
{
Set-Location -Path (((Get-PSDrive -PSProvider CMSite).Name) + ":")
#Sets the location properly to the PSDrive.
}
else
{
throw "You are not currently connected to a CMSite Provider Please Connect and try again"
}
}
write-Verbose "Succesfully validated connection to a CMProvider"
return $true
}
catch
{
$errorMessage = $_.Exception.Message
write-error -Exception CMPatching -Message $errorMessage
return $false
}
}
function Test-Module
#Function that is designed to test a module if it is loaded or not.
{
[CMdletbinding()]
Param
(
[Parameter(Mandatory = $true)]
[String]$ModuleName,
[Parameter(Mandatory = $false)]
[bool]$Remediate
)
If(Get-Module -Name $ModuleName)
#Checks if the module is currently loaded and if it is then return true.
{
Write-Verbose -Message "The module was already loaded return TRUE"
return $true
}
If((Get-Module -Name $ModuleName) -ne $true)
#Checks if the module is NOT loaded and if it's not loaded then check to see if remediation is requested.
{
Write-Verbose -Message "The Module was not already loaded evaluate if remediation flag was set"
if($Remediate -eq $true)
#If the remediation flag is selected then attempt to import the module.
{
try
{
if($ModuleName -eq "ConfigurationManager")
#If the module requested is the Configuration Manager module use the below method to try to import the ConfigMGr Module.
{
Write-Verbose -Message "Non-Standard module requested run pre-written function"
Get-CMModule
#Runs the command to get the COnfigMgr module if its needed.
Write-Verbose -Message "Succesfully loaded the module"
return $true
}
else
{
Write-Verbose -Message "Remediation flag WAS set now attempting to import module $($ModuleName)"
Import-Module -Name $ModuleName
#Import the other module as needed - if they have no custom requirements.
Write-Verbose -Message "Succesfully improted the module $ModuleName"
Return $true
}
}
catch
{
Write-Error -Message "Failed to import the module $($ModuleName)"
Set-Location $StartingLocation
break
}
}
else {
#Else return the fact that it's not applicable and return false from the execution.
{
Return $false
}
}
}
}
#endregion HelperFunctions
############################################