├── 27 WSUS Servers ├── Get-WSUSPortNumbers.ps1 ├── Set-LedbatWSUS.ps1 ├── Set-WSUSContentDir.ps1 ├── Set-WSUSContentDirAnonAuth.ps1 ├── Set-WSUSSSLCertificate.ps1 ├── Test-IsWSUSServer.ps1 ├── Test-WSUSIsSSL.ps1 ├── Test-WSUSPostInstallRan.ps1 └── readme.md ├── Add-FunctionToProfile.ps1 ├── Battery_Health.sql ├── CollectionSummary.sql ├── ConfigMgrSUP_LastScan_Summary.sql ├── Configure-DedupeForMe - SCCM ├── CB - SCCM Distribution Point.cab ├── Set-CMDPDedupConfiguration.ps1 └── readme.md ├── Content_NotOnAllDPGroup.sql ├── Convert-CoManagementWorkload.ps1 ├── Create-SecurityProviders-CI_CB.ps1 ├── DB-Size-Queries.sql ├── Get-30DayHinv_Summary.sql ├── Get-BuzzPhrase.ps1 ├── Get-CCMBaseline.ps1 ├── Get-CCMLogFile.ps1 ├── Get-CMClientMaintenanceWindow.ps1 ├── Get-CMCollectionSummary.ps1 ├── Get-CMHTTPSReadiness.ps1 ├── Get-CMSoftwareUpdatePointSummary.ps1 ├── Get-HinvFrequency.sql ├── Get-LenovoBIOSSettings.ps1 ├── Get-PendingSCCMPatches.ps1 ├── Get-SupersededUpdatesInSUGBySUG.ps1 ├── Get-WQLObject.ps1 ├── Get-WmiRegistryProperty.ps1 ├── Invoke-CCMBaseline.ps1 ├── Invoke-CCMClientAction.ps1 ├── Invoke-SCCMUpdates.ps1 ├── Invoke-SQLAGPatchPrep.ps1 ├── Invoke-UpdateScanAfterSSU ├── CI - Invoke-UpdateScanAfterSSU - Scheduled Task.cab ├── Invoke-UpdateScanAfterSSU.ps1 └── readme.md ├── MEMCM_PolicyPriority ├── README.md ├── Set-CMAntimalwarePolicyPriority.ps1 └── Set-CMClientSettingPriority.ps1 ├── MachineCertInv.ps1 ├── New-CMGlobalConditionRule.ps1 ├── New-CMScheduleStartTime.ps1 ├── New-ClientActionScheduledTask.ps1 ├── New-LoopAction.ps1 ├── New-PostTeamsMachineWideInstallScheduledTask.ps1 ├── Office365 ├── New-365DynamicApp.ps1 ├── O365-PrjOnline-VisPro-2016.xml ├── O365-PrjOnline-VisPro-2019.xml ├── O365-PrjOnline-VisStd-2016.xml ├── O365-PrjOnline-VisStd-2019.xml ├── O365-PrjOnline.xml ├── O365-PrjPro-2016.xml ├── O365-PrjPro-2019.xml ├── O365-PrjStd-2016.xml ├── O365-PrjStd-2019.xml ├── O365-VisOnline-PrjOnline.xml ├── O365-VisOnline-PrjPro-2016.xml ├── O365-VisOnline-PrjPro-2019.xml ├── O365-VisOnline-PrjStd-2016.xml ├── O365-VisOnline-PrjStd-2019.xml ├── O365-VisOnline.xml ├── O365-VisPro-2016-PrjOnline.xml ├── O365-VisPro-2016.xml ├── O365-VisPro-2019-PrjOnline.xml ├── O365-VisPro-2019.xml ├── O365-VisPro-PrjPro-2016.xml ├── O365-VisPro-PrjPro-2019.xml ├── O365-VisPro-PrjStd-2016.xml ├── O365-VisPro-PrjStd-2019.xml ├── O365-VisStd-2016-PrjOnline.xml ├── O365-VisStd-2016.xml ├── O365-VisStd-2019-PrjOnline.xml ├── O365-VisStd-2019.xml ├── O365-VisStd-PrjPro-2016.xml ├── O365-VisStd-PrjPro-2019.xml ├── O365-VisStd-PrjStd-2016.xml ├── O365-VisStd-PrjStd-2019.xml ├── O365.xml ├── PrjOnline.xml ├── PrjPro-2016.xml ├── PrjPro-2019.xml ├── PrjStd-2016.xml ├── PrjStd-2019.xml ├── VisOnline.xml ├── VisPro-2016.xml ├── VisPro-2019.xml ├── VisStd-2016.xml ├── VisStd-2019.xml └── readme.md ├── README.md ├── RemoveExpiredUpdates.sql ├── Repair-CMClientSettings ├── Reprovision Windows 10 Apps ├── Get-DeprovisionedAppX.ps1 ├── Inventory-DeprovisionedAppX.ps1 └── Reprovision-AppX.ps1 ├── Script-InvokeBaseline.ps1 ├── Set-CMDistributionPointMaintenanceMode.ps1 ├── Set-DefaultSecureProtocols.ps1 ├── Set-WSUSContentPath.ps1 ├── Set-WSUSContentPoolAuth.ps1 ├── Start-CMClientAction.ps1 ├── Start-IISLogCleanup.ps1 └── prompt.ps1 /27 WSUS Servers/Get-WSUSPortNumbers.ps1: -------------------------------------------------------------------------------- 1 | function Get-WSUSPortNumbers { 2 | [CmdletBinding()] 3 | <# 4 | .SYNOPSIS 5 | Return the port numbers in use by WSUS 6 | .DESCRIPTION 7 | This function will automatically determine the ports in use by WSUS, and return them as a PSCustomObject. 8 | 9 | If WSUS is set to use any custom port other than 80/443 it 10 | automatically determines the HTTP as noted in the link below 11 | https://docs.microsoft.com/en-us/windows-server/administration/windows-server-update-services/deploy/2-configure-wsus#configure-ssl-on-the-wsus-server 12 | ... if you use any port other than 443 for HTTPS traffic, 13 | WSUS will send clear HTTP traffic over the port that numerically 14 | comes before the port for HTTPS. For example, if you use port 8531 for HTTPS, 15 | WSUS will use port 8530 for HTTP. 16 | .EXAMPLE 17 | PS C:\> Get-WSUSPortNumbers -WSUSServer (Get-WSUSServer) 18 | .INPUTS 19 | [Microsoft.UpdateServices.Internal.BaseApi.UpdateServer] 20 | .OUTPUTS 21 | [PSCustomerObject] 22 | .NOTES 23 | FileName: Get-WSUSPortNumbers.ps1 24 | Author: Cody Mathis 25 | Contact: @CodyMathis123 26 | Created: 6/29/2020 27 | Updated: 6/29/2020 28 | #> 29 | param ( 30 | [Parameter(Mandatory = $true)] 31 | [object]$WSUSServer 32 | ) 33 | #region Determine WSUS Port Numbers 34 | $WSUS_Port1 = $WSUSServer.PortNumber 35 | $WSUS_IsSSL = $WSUSServer.UseSecureConnection 36 | 37 | switch ($WSUS_IsSSL) { 38 | $true { 39 | switch ($WSUS_Port1) { 40 | 443 { 41 | $WSUS_Port2 = 80 42 | } 43 | default { 44 | $WSUS_Port2 = $WSUS_Port1 - 1 45 | } 46 | } 47 | } 48 | $false { 49 | $Wsus_Port2 = $null 50 | } 51 | } 52 | #endregion Determine WSUS Port Numbers 53 | 54 | return [PSCustomObject]@{ 55 | WSUSIsSSL = $WSUS_IsSSL 56 | WSUSPort1 = $WSUS_Port1 57 | WSUSPort2 = $WSUS_Port2 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /27 WSUS Servers/Set-LedbatWSUS.ps1: -------------------------------------------------------------------------------- 1 | #region detection/remediation 2 | #region define variables 3 | $Remediate = $false 4 | #endregion define variables 5 | 6 | try { 7 | $WSUS_Server = Get-WsusServer -ErrorAction Stop 8 | } 9 | catch { 10 | # This is not a WSUS server, or it is in an error state. Return compliant. 11 | return $true 12 | } 13 | 14 | #region helper functions 15 | function Get-WSUSPortNumbers { 16 | [CmdletBinding()] 17 | <# 18 | .SYNOPSIS 19 | Return the port numbers in use by WSUS 20 | .DESCRIPTION 21 | This function will automatically determine the ports in use by WSUS, and return them as a PSCustomObject. 22 | 23 | If WSUS is set to use any custom port other than 80/443 it 24 | automatically determines the HTTP as noted in the link below 25 | https://docs.microsoft.com/en-us/windows-server/administration/windows-server-update-services/deploy/2-configure-wsus#configure-ssl-on-the-wsus-server 26 | ... if you use any port other than 443 for HTTPS traffic, 27 | WSUS will send clear HTTP traffic over the port that numerically 28 | comes before the port for HTTPS. For example, if you use port 8531 for HTTPS, 29 | WSUS will use port 8530 for HTTP. 30 | .EXAMPLE 31 | PS C:\> Get-WSUSPortNumbers -WSUSServer (Get-WSUSServer) 32 | .INPUTS 33 | [Microsoft.UpdateServices.Internal.BaseApi.UpdateServer] 34 | .OUTPUTS 35 | [PSCustomerObject] 36 | .NOTES 37 | FileName: Get-WSUSPortNumbers.ps1 38 | Author: Cody Mathis 39 | Contact: @CodyMathis123 40 | Created: 6/29/2020 41 | Updated: 6/29/2020 42 | #> 43 | param ( 44 | [Parameter(Mandatory = $true)] 45 | [object]$WSUSServer 46 | ) 47 | #region Determine WSUS Port Numbers 48 | $WSUS_Port1 = $WSUSServer.PortNumber 49 | $WSUS_IsSSL = $WSUSServer.UseSecureConnection 50 | 51 | switch ($WSUS_IsSSL) { 52 | $true { 53 | switch ($WSUS_Port1) { 54 | 443 { 55 | $WSUS_Port2 = 80 56 | } 57 | default { 58 | $WSUS_Port2 = $WSUS_Port1 - 1 59 | } 60 | } 61 | } 62 | $false { 63 | $Wsus_Port2 = $null 64 | } 65 | } 66 | #endregion Determine WSUS Port Numbers 67 | 68 | return [PSCustomObject]@{ 69 | WSUSIsSSL = $WSUS_IsSSL 70 | WSUSPort1 = $WSUS_Port1 71 | WSUSPort2 = $WSUS_Port2 72 | } 73 | } 74 | #endregion 75 | 76 | switch ($WSUS_Server -is [Microsoft.UpdateServices.Internal.BaseApi.UpdateServer]) { 77 | $true { 78 | $WSUSPorts = Get-WSUSPortNumbers -WSUSServer $WSUS_Server 79 | 80 | $WSUS_Port1 = $WSUSPorts.WSUSPort1 81 | $WSUS_Port2 = $WSUSPorts.WSUSPort2 82 | 83 | $LEDBAT_Enabled = [bool](Get-NetTCPSetting -SettingName InternetCustom -CongestionProvider LEDBAT -ErrorAction SilentlyContinue) 84 | $CustomPort1Set = [bool](Get-NetTransportFilter -LocalPortStart $WSUS_Port1 -LocalPortEnd $WSUS_Port1 -SettingName InternetCustom -RemotePortStart 0 -RemotePortEnd 65535 -ErrorAction SilentlyContinue) 85 | if ($null -ne $Wsus_Port2) { 86 | $CustomPort2Set = [bool](Get-NetTransportFilter -LocalPortStart $WSUS_Port2 -LocalPortEnd $WSUS_Port2 -SettingName InternetCustom -RemotePortStart 0 -RemotePortEnd 65535 -ErrorAction SilentlyContinue) 87 | } 88 | else { 89 | $CustomPort2Set = $true 90 | } 91 | switch ($LEDBAT_Enabled -and $CustomPort1Set -and $CustomPort2Set) { 92 | $true { 93 | return $true 94 | } 95 | $false { 96 | switch ($LEDBAT_Enabled) { 97 | $false { 98 | switch ($Remediate) { 99 | $true { 100 | try { 101 | Set-NetTCPSetting -SettingName InternetCustom -CongestionProvider LEDBAT -ErrorAction Stop 102 | } 103 | catch { 104 | return $false 105 | } 106 | } 107 | } 108 | } 109 | } 110 | switch ($CustomPort1Set) { 111 | $false { 112 | switch ($Remediate) { 113 | $true { 114 | try { 115 | New-NetTransportFilter -SettingName InternetCustom -LocalPortStart $WSUS_Port1 -LocalPortEnd $WSUS_Port1 -RemotePortStart 0 -RemotePortEnd 65535 -ErrorAction Stop 116 | } 117 | catch { 118 | return $false 119 | } 120 | } 121 | } 122 | } 123 | } 124 | switch ($CustomPort2Set) { 125 | $false { 126 | switch ($Remediate) { 127 | $true { 128 | try { 129 | New-NetTransportFilter -SettingName InternetCustom -LocalPortStart $WSUS_Port2 -LocalPortEnd $WSUS_Port2 -RemotePortStart 0 -RemotePortEnd 65535 -ErrorAction Stop 130 | } 131 | catch { 132 | return $false 133 | } 134 | } 135 | } 136 | } 137 | } 138 | return $Remediate 139 | } 140 | } 141 | } 142 | $false { 143 | return $true 144 | } 145 | } 146 | #endregion detection/remediation 147 | -------------------------------------------------------------------------------- /27 WSUS Servers/Set-WSUSContentDir.ps1: -------------------------------------------------------------------------------- 1 | $Remediate = $false 2 | 3 | $PathShouldBeROOT = Get-ItemProperty -Path "registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Update Services\Server\Setup" -Name ContentDir | Select-Object -ExpandProperty ContentDir 4 | $PathShouldBe = Join-Path -Path $PathShouldBeROOT -ChildPath 'WSUSContent' 5 | 6 | [Void][Reflection.Assembly]::LoadWithPartialName("Microsoft.Web.Administration") 7 | $serverManager = New-Object Microsoft.Web.Administration.ServerManager 8 | $site = $serverManager.Sites | Where-Object { $_.Name -eq "WSUS Administration" } 9 | $rootApp = $site.Applications | Where-Object { $_.Path -eq "/" } 10 | $rootVdir = $rootApp.VirtualDirectories | Where-Object { $_.Path -eq "/Content" } 11 | $CurrentPath = $rootVdir.PhysicalPath 12 | 13 | switch ($CurrentPath -eq $PathShouldBe) { 14 | $true { 15 | $true 16 | } 17 | $false { 18 | switch ($Remediate) { 19 | $true { 20 | $rootVdir.PhysicalPath = $PathShouldBe 21 | $null = $serverManager.CommitChanges() 22 | $true 23 | } 24 | $false { 25 | $false 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /27 WSUS Servers/Set-WSUSContentDirAnonAuth.ps1: -------------------------------------------------------------------------------- 1 | $Remediate = $false 2 | 3 | Import-Module WebAdministration 4 | $UseAppPoolIdentity = (Get-WebConfiguration "/system.applicationHost/sites/site[@name='WSUS Administration']/application[@path='/']/virtualdirectory[@path='/Content']").userName -eq [string]::Empty 5 | switch ($UseAppPoolIdentity) { 6 | $true { 7 | $true 8 | } 9 | $false { 10 | switch ($Remediate) { 11 | $true { 12 | Set-WebConfiguration "/system.applicationHost/sites/site[@name='WSUS Administration']/application[@path='/']/virtualdirectory[@path='/Content']" -Value @{userName = ''; password = '' } 13 | $true 14 | } 15 | $false { 16 | $false 17 | } 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /27 WSUS Servers/Set-WSUSSSLCertificate.ps1: -------------------------------------------------------------------------------- 1 | $Remediate = $false 2 | $IssuingCA = 'Auto Enrollment Issuing CA' 3 | $Website = 'WSUS Administration' 4 | 5 | try { 6 | $WSUS_Server = Get-WsusServer -ErrorAction Stop 7 | } 8 | catch { 9 | # This is not a WSUS server, or it is in an error state. Return compliant. 10 | return $true 11 | } 12 | #region helper functions 13 | function Get-WSUSPortNumbers { 14 | [CmdletBinding()] 15 | <# 16 | .SYNOPSIS 17 | Return the port numbers in use by WSUS 18 | .DESCRIPTION 19 | This function will automatically determine the ports in use by WSUS, and return them as a PSCustomObject. 20 | 21 | If WSUS is set to use any custom port other than 80/443 it 22 | automatically determines the HTTP as noted in the link below 23 | https://docs.microsoft.com/en-us/windows-server/administration/windows-server-update-services/deploy/2-configure-wsus#configure-ssl-on-the-wsus-server 24 | ... if you use any port other than 443 for HTTPS traffic, 25 | WSUS will send clear HTTP traffic over the port that numerically 26 | comes before the port for HTTPS. For example, if you use port 8531 for HTTPS, 27 | WSUS will use port 8530 for HTTP. 28 | .EXAMPLE 29 | PS C:\> Get-WSUSPortNumbers -WSUSServer (Get-WSUSServer) 30 | .INPUTS 31 | [Microsoft.UpdateServices.Internal.BaseApi.UpdateServer] 32 | .OUTPUTS 33 | [PSCustomerObject] 34 | .NOTES 35 | FileName: Get-WSUSPortNumbers.ps1 36 | Author: Cody Mathis 37 | Contact: @CodyMathis123 38 | Created: 6/29/2020 39 | Updated: 6/29/2020 40 | #> 41 | param ( 42 | [Parameter(Mandatory = $true)] 43 | [object]$WSUSServer 44 | ) 45 | #region Determine WSUS Port Numbers 46 | $WSUS_Port1 = $WSUSServer.PortNumber 47 | $WSUS_IsSSL = $WSUSServer.UseSecureConnection 48 | 49 | switch ($WSUS_IsSSL) { 50 | $true { 51 | switch ($WSUS_Port1) { 52 | 443 { 53 | $WSUS_Port2 = 80 54 | } 55 | default { 56 | $WSUS_Port2 = $WSUS_Port1 - 1 57 | } 58 | } 59 | } 60 | $false { 61 | $Wsus_Port2 = $null 62 | } 63 | } 64 | #endregion Determine WSUS Port Numbers 65 | 66 | return [PSCustomObject]@{ 67 | WSUSIsSSL = $WSUS_IsSSL 68 | WSUSPort1 = $WSUS_Port1 69 | WSUSPort2 = $WSUS_Port2 70 | } 71 | } 72 | #endregion 73 | 74 | $WSUSPorts = Get-WSUSPortNumbers -WSUSServer $WSUS_Server 75 | if ($WSUSPorts.WSUSIsSSL) { 76 | $PortNumber = $WSUSPorts.WSUSPort1 77 | } 78 | else { 79 | # WSUS is not configured to use SSL. Return compliant. 80 | return 'Compliant' 81 | } 82 | 83 | $AllCerts = Get-ChildItem -Path Cert:\LocalMachine\My\ 84 | $ServerAuth = $allCerts | Where-Object { $_.issuer -match $IssuingCA -and $_.Subject -match $env:COMPUTERNAME -and $_.EnhancedKeyUsageList.FriendlyName -contains 'Server Authentication' } | Sort-Object -Property NotAfter | Select-Object -ExpandProperty Thumbprint -Last 1 85 | $Binding = Get-WebBinding -Name $Website -Protocol https 86 | 87 | if ($null -eq $ServerAuth) { 88 | return 'No Cert Found' 89 | } 90 | 91 | switch ($null -ne $Binding) { 92 | $true { 93 | continue 94 | } 95 | $false { 96 | switch ($Remediate) { 97 | $true { 98 | $null = New-WebBinding -Name $Website -Protocol https -Port $PortNumber 99 | $Binding = Get-WebBinding -Name $Website -Protocol https 100 | } 101 | $false { 102 | return 'HTTPS Binding does not exist' 103 | } 104 | } 105 | 106 | } 107 | } 108 | 109 | switch ($Binding.certificateHash -eq $ServerAuth) { 110 | $true { 111 | return 'Compliant' 112 | } 113 | $false { 114 | switch ($Remediate) { 115 | $true { 116 | $Binding.AddSslCertificate($ServerAuth, 'MY') 117 | return 'Compliant' 118 | } 119 | $false { 120 | return 'Incorrect Cert Bound' 121 | } 122 | } 123 | } 124 | } -------------------------------------------------------------------------------- /27 WSUS Servers/Test-IsWSUSServer.ps1: -------------------------------------------------------------------------------- 1 | try { 2 | [Void][Reflection.Assembly]::LoadWithPartialName("Microsoft.Web.Administration") 3 | $serverManager = New-Object Microsoft.Web.Administration.ServerManager -ErrorAction SilentlyContinue 4 | } 5 | catch { 6 | # Deliberate empty return. If anything above throws an error, we assume we are not on a box with IIS 7 | exit 0 8 | } 9 | 10 | if ((Get-CimInstance -Query "SELECT Name FROM Win32_ServerFeature WHERE Name ='Windows Server Update Services'").Name -and $serverManager.ApplicationPools.Name -contains 'WsusPool') { 11 | Write-Host 'WSUS Is Installed' 12 | } -------------------------------------------------------------------------------- /27 WSUS Servers/Test-WSUSIsSSL.ps1: -------------------------------------------------------------------------------- 1 | try { 2 | [Void][Reflection.Assembly]::LoadWithPartialName("Microsoft.Web.Administration") 3 | $serverManager = New-Object Microsoft.Web.Administration.ServerManager -ErrorAction SilentlyContinue 4 | } 5 | catch { 6 | # Deliberate empty return. If anything above throws an error, we assume we are not on a box with IIS 7 | exit 0 8 | } 9 | 10 | $WSUS_ConfigKey = 'registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Update Services\Server\Setup' 11 | 12 | try { 13 | $ServerCertName = Get-ItemPropertyValue -Path $WSUS_ConfigKey -Name 'ServerCertificateName' -ErrorAction Stop 14 | $UsingSSL = Get-ItemPropertyValue -Path $WSUS_ConfigKey -Name 'UsingSSL' -ErrorAction Stop 15 | if ($serverManager.ApplicationPools.Name -contains 'WsusPool' -and $env:COMPUTERNAME -match $ServerCertName -and $UsingSSL) { 16 | Write-Host 'WSUS Server is SSL' 17 | } 18 | } 19 | catch { 20 | # Deliberate empty return. If anything above throws an error, we assume we are not on an SSL WSUS box 21 | exit 0 22 | } -------------------------------------------------------------------------------- /27 WSUS Servers/Test-WSUSPostInstallRan.ps1: -------------------------------------------------------------------------------- 1 | try { 2 | [Void][Reflection.Assembly]::LoadWithPartialName("Microsoft.Web.Administration") 3 | $serverManager = New-Object Microsoft.Web.Administration.ServerManager 4 | } 5 | catch { 6 | # Deliberate empty return. If anything above throws an error, we assume we are not on a box with IIS 7 | exit 0 8 | } 9 | 10 | try { 11 | $UpdateServices = (Get-ItemProperty -Path "registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Update Services\Server\Setup\Installed Role Services" -Name 'UpdateServices-Services' -ErrorAction SilentlyContinue).'UpdateServices-Services' 12 | if ($UpdateServices -eq 2 -and (Get-CimInstance -Query "SELECT Name FROM Win32_ServerFeature WHERE Name ='Windows Server Update Services'").Name -and $serverManager.ApplicationPools.Name -contains 'WsusPool') { 13 | Write-Host 'WSUS PostInstall Has Ran' 14 | } 15 | } 16 | catch { 17 | # Deliberate empty return. If anything above throws an error, we assume we are not on a WSUS box 18 | exit 0 19 | } -------------------------------------------------------------------------------- /27 WSUS Servers/readme.md: -------------------------------------------------------------------------------- 1 | https://sccmf12twice.com/?p=12621&preview=true 2 | -------------------------------------------------------------------------------- /Add-FunctionToProfile.ps1: -------------------------------------------------------------------------------- 1 | function Add-FunctionToProfile { 2 | <# 3 | .SYNOPSIS 4 | Add a function to your profile 5 | .DESCRIPTION 6 | This function is used to append a function to your PowerShell profile. You provide a function name, and if it has a script block 7 | then it will be appended to your PowerShell profile with the function name provided. 8 | .PARAMETER FunctionToAdd 9 | The name of the function(s) you wish to add to your profile. You can provide multiple. 10 | .EXAMPLE 11 | PS C:\> Add-FunctionToProfile -FunctionToAdd 'Get-CMClientMaintenanceWindow' 12 | .NOTES 13 | If a function doesn't have a script block, then it cannot be added to your profile 14 | #> 15 | param( 16 | [Parameter(Mandatory = $True)] 17 | [string[]]$FunctionToAdd 18 | ) 19 | foreach ($FunctionName in $FunctionToAdd) { 20 | try { 21 | $Function = Get-Command -Name $FunctionName -CommandType Function -ErrorAction Stop 22 | } 23 | catch { 24 | Write-Error "Failed to find the specified function [Name = '$FunctionName']" 25 | continue 26 | } 27 | $ScriptBlock = $Function.ScriptBlock 28 | if ($null -ne $ScriptBlock) { 29 | $FuncToAdd = [string]::Format("`r`nfunction {0} {{{1}}}", $FunctionName, $ScriptBlock) 30 | ($FuncToAdd -split "`n") | Add-Content -Path $PROFILE 31 | } 32 | else { 33 | Write-Error "Function $FunctionName does not have a Script Block and cannot be added to your profile." 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Battery_Health.sql: -------------------------------------------------------------------------------- 1 | SELECT DISTINCT s.Netbios_Name0 2 | , os.InstallDate0 3 | , csp.Version0 4 | , csp.Name0 5 | , cycle.CycleCount0 6 | , batcapac.FullChargedCapacity0 7 | , batstat.DesignedCapacity0 8 | , CASE 9 | WHEN batcapac.FullChargedCapacity0 <= batstat.DesignedCapacity0 THEN CAST((100 * batcapac.FullChargedCapacity0 / batstat.DesignedCapacity0) AS FLOAT) 10 | ELSE 100 11 | END AS [BatteryHealth] 12 | , cycle.InstanceName0 13 | , bat.Name0 AS [BatteryName] 14 | , bat.DesignVoltage0 15 | , bat.Status0 16 | , portbat.Location0 17 | , portbat.Manufacturer0 18 | FROM v_R_System_Valid s 19 | JOIN v_GS_OPERATING_SYSTEM os ON os.ResourceID = s.ResourceID 20 | JOIN v_GS_COMPUTER_SYSTEM_PRODUCT csp ON csp.ResourceID = s.ResourceID 21 | JOIN v_GS_BATTERYCYCLECOUNT cycle ON cycle.ResourceID = s.ResourceID 22 | JOIN v_GS_BATTERYSTATICDATA batstat ON batstat.ResourceID = s.ResourceID AND batstat.InstanceName0 = cycle.InstanceName0 23 | JOIN v_GS_BATTERYFULLCHARGEDCAPACI batcapac ON batcapac.ResourceID = s.ResourceID AND batcapac.InstanceName0 = cycle.InstanceName0 24 | LEFT JOIN v_GS_BATTERY bat ON bat.ResourceID = s.ResourceID AND bat.Name0 = batstat.DeviceName0 25 | LEFT JOIN v_GS_PORTABLE_BATTERY portbat ON portbat.ResourceID = s.ResourceID AND portbat.Name0 = batstat.DeviceName0 26 | ORDER BY 1 27 | -------------------------------------------------------------------------------- /ConfigMgrSUP_LastScan_Summary.sql: -------------------------------------------------------------------------------- 1 | SELECT s.Netbios_Name0 AS 'Name' 2 | , s.ResourceID 3 | , srn.Resource_Names0 AS 'FQDN' 4 | , s.Resource_Domain_OR_Workgr0 AS 'DOMAIN' 5 | , os.Caption0 AS 'Operating System' 6 | , sn.StateDescription AS 'LastScanState' 7 | , scan.LastScanTime 8 | , scan.LastErrorCode 9 | , scan.LastScanPackageLocation 10 | , wsStatus.LastHWScan AS 'Last Hardware Scan' 11 | FROM v_R_System s 12 | JOIN v_RA_System_ResourceNames srn ON s.ResourceID = srn.ResourceID 13 | JOIN v_UpdateScanStatus scan ON s.ResourceID = scan.ResourceID 14 | JOIN v_GS_WORKSTATION_STATUS wsStatus ON s.ResourceID = wsStatus.ResourceID 15 | JOIN v_GS_OPERATING_SYSTEM os ON s.ResourceID = os.ResourceID 16 | JOIN v_StateNames sn ON sn.StateID = scan.LastScanState AND sn.TopicType = 501 -------------------------------------------------------------------------------- /Configure-DedupeForMe - SCCM/CB - SCCM Distribution Point.cab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodyMathis123/CM-Ramblings/ed952ee8966a9914ba5bff976020b5f16c20758f/Configure-DedupeForMe - SCCM/CB - SCCM Distribution Point.cab -------------------------------------------------------------------------------- /Configure-DedupeForMe - SCCM/readme.md: -------------------------------------------------------------------------------- 1 | https://sccmf12twice.com/2019/07/configure-dedupeforme-sccm/ 2 | -------------------------------------------------------------------------------- /Content_NotOnAllDPGroup.sql: -------------------------------------------------------------------------------- 1 | DECLARE @allDPgroupID uniqueidentifier = (SELECT TOP 1 GroupID FROM v_SMS_DistributionPointGroup ORDER BY membercount DESC) 2 | DECLARE @allDPgroupMemberCount int = (SELECT TOP 1 MemberCount FROM v_SMS_DistributionPointGroup ORDER BY membercount DESC) 3 | 4 | SELECT DISTINCT dpgp.PkgID 5 | , p.packagetype 6 | , bycount.TargeteddDPCount 7 | FROM v_DPGroupPackages dpgp 8 | JOIN v_package p ON p.packageid = dpgp.PkgID 9 | JOIN ( 10 | SELECT cdss.pkgid 11 | , cdss.TargeteddDPCount 12 | FROM v_ContDistStatSummary cdss 13 | WHERE TargeteddDPCount not in (@allDPgroupMemberCount,0) 14 | ) bycount ON bycount.PkgID = dpgp.PkgID 15 | WHERE p.packageid not in ( 16 | SELECT DISTINCT PkgID 17 | FROM v_DPGroupPackages 18 | WHERE groupid = @allDPgroupID 19 | ) 20 | -------------------------------------------------------------------------------- /Convert-CoManagementWorkload.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Convert CoMgmt workloads from int to text and vice-versa 4 | .DESCRIPTION 5 | In log files, WMI and the database we are presented with an integer values for the currently enabled functions with Co-Management. 6 | This function has two ParameterSets. 7 | For the 'TranslateWorkload' ParameterSet you can input an integer (0-255) and return a string array of the workloads that are enabled. 8 | For the 'GenerateWorkload' ParameterSet you can input a string array of the workloads you'd like and it will return the translated integer equivalent. 9 | .PARAMETER DesiredWorkload 10 | Specify the workloads you'd like to generate a translated integer for. The options are are below. It will be translated using a bitwise and operator. 11 | 'Client Apps', 12 | 'Compliance policies', 13 | 'Device Configuration', 14 | 'Endpoint Protection', 15 | 'Office Click-To-Run apps', 16 | 'Resource access policies', 17 | 'Windows Updates policies' 18 | .PARAMETER Workload 19 | Translate an integer workload to the named workload values using bitwise or operator. 20 | .EXAMPLE 21 | PS C:\> .\Convert-CoManagementWorkload -DesiredWorkload 'Windows Updates Policies','Office Click-To-Run apps' 22 | 145 23 | 24 | This would return a value of 145, which could be used to update a configuration item. 25 | .EXAMPLE 26 | PS C:\> .\Convert-CoManagementWorkload -Workload 145 27 | Office Click-to-Run apps 28 | Windows Updates Policies 29 | 30 | This translates the workload integer value of 145 to the component that would be enabled, Office C2R and WUFB. 31 | .NOTES 32 | FileName: Convert-CoManagementWorkload.ps1 33 | Author: Cody Mathis 34 | Contact: @CodyMathis123 35 | Created: 1/28/2019 36 | Updated: 6/17/2020 37 | 38 | Version History: 39 | 1.0.0 - (1/28/2019) Initial script creation 40 | 1.0.1 - (1/29/2019) Added proper help and commenting and specified OutputType 41 | 1.0.2 - (8/21/2019) Update workloads for 1906 42 | 1.0.3 - (6/17/2020) Stop using arraylist 43 | #> 44 | [OutputType([int], ParameterSetName = 'GenerateWorkload')] 45 | [OutputType([string[]], ParameterSetName = 'TranslateWorkload')] 46 | param( 47 | [Parameter(Mandatory = $True, ParameterSetName = 'GenerateWorkload')] 48 | [ValidateSet('Client Apps', 49 | 'Compliance policies', 50 | 'Device Configuration', 51 | 'Endpoint Protection', 52 | 'Office Click-To-Run apps', 53 | 'Resource access policies', 54 | 'Windows Updates policies' 55 | )] 56 | [string[]] 57 | $DesiredWorkload, 58 | [Parameter(Mandatory = $True, ParameterSetName = 'TranslateWorkload')] 59 | [ValidateRange(0, 255)] 60 | [int] 61 | $Workload 62 | ) 63 | 64 | switch ($PSCmdlet.ParameterSetName) { 65 | 'GenerateWorkload' { 66 | $Workloads = @{ 67 | "Compliance Policies" = 3; 68 | "Resource access policies" = 5; 69 | "Device Configuration" = 9; 70 | "Windows Updates Policies" = 17; 71 | "Endpoint Protection" = 33; 72 | "Client Apps" = 65; 73 | "Office Click-to-Run apps" = 129; 74 | } 75 | # Creating an arraylist and adding all the integer values for our workloads to the arraylist. 76 | $ToCalc = foreach ($Option in $DesiredWorkload) { 77 | $Workloads[$Option] 78 | } 79 | 80 | <# 81 | In order to calculate our output value we join all of our converted workload integers with -bor giving us something like '17 -bor 129' 82 | This is then converted to a scriptblock so we can execute it to receive an integer output. This is a bitwise operation. 83 | #> 84 | $Calculation = $ToCalc -join ' -bor ' 85 | $Output = [scriptblock]::Create($Calculation) 86 | & $Output 87 | } 88 | 'TranslateWorkload' { 89 | $Workloads = @{ 90 | 3 = "Compliance policies"; 91 | 5 = "Resource access policies" 92 | 9 = "Device Configuration" 93 | 17 = "Windows Updates Policies"; 94 | 33 = "Endpoint Protection" 95 | 65 = "Client Apps"; 96 | 129 = "Office Click-to-Run apps" 97 | } 98 | 99 | # If our workload input is greater than 1, we perform a -band comparison against all possible workloads and return the matches 100 | if ($Workload -gt 1) { 101 | foreach ($Value in $Workloads.Keys) { 102 | if (($Value -band $Workload) -eq $Value) { 103 | $Workloads[$value] 104 | } 105 | } 106 | } 107 | else { 108 | Write-Output 'None' 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /Create-SecurityProviders-CI_CB.ps1: -------------------------------------------------------------------------------- 1 | # define all the protocols available 2 | $ProtocolList = @('SSL 2.0', 'SSL 3.0', 'TLS 1.0', 'TLS 1.1', 'TLS 1.2') 3 | # specify all the protocols that should remain enabled 4 | $EnableList = @('TLS 1.2') 5 | 6 | $SetKeyParams = @{ 7 | DataType = 'Integer' 8 | Hive = 'LocalMachine' 9 | ReportNoncompliance = $true 10 | ExpressionOperator = 'IsEquals' 11 | Remediate = $true 12 | ValueRule = $true 13 | NoncomplianceSeverity = 'Warning' 14 | RemediateDword = $true 15 | Is64Bit = $true 16 | } 17 | 18 | $GetKeyExistanceParams = @{ 19 | Existence = 'MustExist' 20 | Hive = 'LocalMachine' 21 | DataType = 'Integer' 22 | ExistentialRule = $true 23 | NoncomplianceSeverity = 'Warning' 24 | RemediateDword = $true 25 | Is64Bit = $true 26 | } 27 | 28 | $EnabledListString = $EnableList -join ', ' 29 | $BL = New-CMBaseline -Name "Enforce $EnabledListString" -Description "Configures the machine to only use $EnabledListString" 30 | foreach ($Protocol in $ProtocolList) { 31 | if ($Protocol -in $EnableList) { 32 | $Name = "Registry - Enable $Protocol" 33 | $Description = "Forces $Protocol to be enabled" 34 | } 35 | else { 36 | $Name = "Registry - Disable $Protocol" 37 | $Description = "Forces $Protocol to be disabled" 38 | } 39 | $CI = New-CMConfigurationItem -Name $Name -Description $Description -CreationType WindowsOS 40 | 41 | $SetKeyParams['InputObject'] = $CI 42 | $GetKeyExistanceParams['InputObject'] = $CI 43 | 44 | 45 | foreach ($key in @('Client', 'Server')) { 46 | $currentRegPath = [string]::Format('SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\{0}\{1}', $Protocol, $key) 47 | $SetKeyParams['KeyName'] = $currentRegPath 48 | $GetKeyExistanceParams['KeyName'] = $currentRegPath 49 | 50 | if ($Protocol -in $EnableList) { 51 | $SetKeyParams['Name'] = "Set $Protocol DisabledByDefault for $key to 0" 52 | $SetKeyParams['ExpectedValue'] = 0 53 | $SetKeyParams['RuleName'] = "Set $Protocol DisabledByDefault for $key to 0" 54 | $SetKeyParams['ValueName'] = 'DisabledByDefault' 55 | $SetKeyParams['Description'] = "Set $Protocol DisabledByDefault for $key to 0 to ensure $Protocol is enabled" 56 | $SetKeyParams['RuleDescription'] = "Set $Protocol DisabledByDefault for $key to 0 to ensure $Protocol is enabled" 57 | Add-CMComplianceSettingRegistryKeyValue @SetKeyParams 58 | 59 | $SetKeyParams['Name'] = "Set $Protocol Enabled for $key to 1" 60 | $SetKeyParams['ExpectedValue'] = 1 61 | $SetKeyParams['RuleName'] = "Set $Protocol Enabled for $key to 1" 62 | $SetKeyParams['ValueName'] = 'Enabled' 63 | $SetKeyParams['Description'] = "Set $Protocol Enabled for $key to 1 to ensure $Protocol is enabled" 64 | $SetKeyParams['RuleDescription'] = "Set $Protocol Enabled for $key to 1 to ensure $Protocol is enabled" 65 | Add-CMComplianceSettingRegistryKeyValue @SetKeyParams 66 | 67 | $GetKeyExistanceParams['Name'] = "Validate $Protocol $Key Enabled exists" 68 | $GetKeyExistanceParams['RuleName'] = "Validate $Protocol $Key Enabled exists" 69 | $GetKeyExistanceParams['ValueName'] = 'Enabled' 70 | Add-CMComplianceSettingRegistryKeyValue @GetKeyExistanceParams 71 | 72 | $GetKeyExistanceParams['Name'] = "Validate $Protocol $Key DisabledByDefault exists" 73 | $GetKeyExistanceParams['RuleName'] = "Validate $Protocol $Key DisabledByDefault exists" 74 | $GetKeyExistanceParams['ValueName'] = 'DisabledByDefault' 75 | Add-CMComplianceSettingRegistryKeyValue @GetKeyExistanceParams 76 | } 77 | else { 78 | $SetKeyParams['Name'] = "Set $Protocol DisabledByDefault for $key to 1" 79 | $SetKeyParams['ExpectedValue'] = 1 80 | $SetKeyParams['RuleName'] = "Set $Protocol DisabledByDefault for $key to 1" 81 | $SetKeyParams['ValueName'] = 'DisabledByDefault' 82 | $SetKeyParams['Description'] = "Set $Protocol DisabledByDefault for $key to 1 to ensure $Protocol is disabled" 83 | $SetKeyParams['RuleDescription'] = "Set $Protocol DisabledByDefault for $key to 1 to ensure $Protocol is disabled" 84 | Add-CMComplianceSettingRegistryKeyValue @SetKeyParams 85 | 86 | $SetKeyParams['Name'] = "Set $Protocol Enabled for $key to 0" 87 | $SetKeyParams['ExpectedValue'] = 0 88 | $SetKeyParams['RuleName'] = "Set $Protocol Enabled for $key to 0" 89 | $SetKeyParams['ValueName'] = 'Enabled' 90 | $SetKeyParams['Description'] = "Set $Protocol Enabled for $key to 0 to ensure $Protocol is disabled" 91 | $SetKeyParams['RuleDescription'] = "Set $Protocol Enabled for $key to 0 to ensure $Protocol is disabled" 92 | Add-CMComplianceSettingRegistryKeyValue @SetKeyParams 93 | 94 | $GetKeyExistanceParams['Name'] = "Validate $Protocol $Key Enabled exists" 95 | $GetKeyExistanceParams['RuleName'] = "Validate $Protocol $Key Enabled exists" 96 | $GetKeyExistanceParams['ValueName'] = 'Enabled' 97 | Add-CMComplianceSettingRegistryKeyValue @GetKeyExistanceParams 98 | 99 | $GetKeyExistanceParams['Name'] = "Validate $Protocol $Key DisabledByDefault exists" 100 | $GetKeyExistanceParams['RuleName'] = "Validate $Protocol $Key DisabledByDefault exists" 101 | $GetKeyExistanceParams['ValueName'] = 'DisabledByDefault' 102 | Add-CMComplianceSettingRegistryKeyValue @GetKeyExistanceParams 103 | } 104 | } 105 | Set-CMBaseline -Id $BL.CI_ID -AddOSConfigurationItem $CI.CI_ID 106 | } 107 | -------------------------------------------------------------------------------- /DB-Size-Queries.sql: -------------------------------------------------------------------------------- 1 | --Queries sourced from various places, just storing them here for easy access. 2 | 3 | -- Table size sorted by total space in MB 4 | SELECT 5 | t.name AS TableName, 6 | i.name as indexName, 7 | sum(p.rows) as RowCounts, 8 | (sum(a.total_pages) * 8) / 1024 as TotalSpaceMB, 9 | (sum(a.used_pages) * 8) / 1024 as UsedSpaceMB, 10 | (sum(a.data_pages) * 8) / 1024 as DataSpaceMB 11 | FROM 12 | sys.tables t 13 | INNER JOIN 14 | sys.indexes i ON t.object_id = i.object_id 15 | INNER JOIN 16 | sys.partitions p ON i.object_id = p.object_id AND i.index_id = p.index_id 17 | INNER JOIN 18 | sys.allocation_units a ON p.partition_id = a.container_id 19 | WHERE 20 | t.name NOT LIKE 'dt%' AND 21 | i.object_id > 255 AND 22 | i.index_id <= 1 23 | GROUP BY 24 | t.name, i.object_id, i.index_id, i.name 25 | ORDER BY 26 | 6 DESC 27 | 28 | -- Database file size 29 | SELECT 30 | [TYPE] = A.TYPE_DESC 31 | ,[FILE_Name] = A.name 32 | ,[FILEGROUP_NAME] = fg.name 33 | ,[File_Location] = A.PHYSICAL_NAME 34 | ,[FILESIZE_MB] = CONVERT(DECIMAL(10,2),A.SIZE/128.0) 35 | ,[USEDSPACE_MB] = CONVERT(DECIMAL(10,2),A.SIZE/128.0 - ((SIZE/128.0) - CAST(FILEPROPERTY(A.NAME, 'SPACEUSED') AS INT)/128.0)) 36 | ,[FREESPACE_MB] = CONVERT(DECIMAL(10,2),A.SIZE/128.0 - CAST(FILEPROPERTY(A.NAME, 'SPACEUSED') AS INT)/128.0) 37 | ,[FREESPACE_%] = CONVERT(DECIMAL(10,2),((A.SIZE/128.0 - CAST(FILEPROPERTY(A.NAME, 'SPACEUSED') AS INT)/128.0)/(A.SIZE/128.0))*100) 38 | ,[AutoGrow] = 'By ' + CASE is_percent_growth WHEN 0 THEN CAST(growth/128 AS VARCHAR(10)) + ' MB -' 39 | WHEN 1 THEN CAST(growth AS VARCHAR(10)) + '% -' ELSE '' END 40 | + CASE max_size WHEN 0 THEN 'DISABLED' WHEN -1 THEN ' Unrestricted' 41 | ELSE ' Restricted to ' + CAST(max_size/(128*1024) AS VARCHAR(10)) + ' GB' END 42 | + CASE is_percent_growth WHEN 1 THEN ' [autogrowth by percent, BAD setting!]' ELSE '' END 43 | FROM sys.database_files A LEFT JOIN sys.filegroups fg ON A.data_space_id = fg.data_space_id 44 | order by A.TYPE desc, A.NAME; 45 | 46 | -- Index size sorted by total space in kb 47 | SELECT 48 | OBJECT_NAME(i.OBJECT_ID) AS TableName, 49 | i.name AS IndexName, 50 | i.index_id AS IndexID, 51 | 8 * SUM(a.used_pages) AS 'Indexsize(KB)' 52 | FROM 53 | sys.indexes AS i JOIN 54 | sys.partitions AS p ON p.OBJECT_ID = i.OBJECT_ID AND p.index_id = i.index_id JOIN 55 | sys.allocation_units AS a ON a.container_id = p.partition_id 56 | where [i].[is_primary_key] = 0 -- fix for size discrepancy 57 | GROUP BY 58 | i.OBJECT_ID, 59 | i.index_id, 60 | i.name 61 | ORDER BY 62 | 4 DESC 63 | -------------------------------------------------------------------------------- /Get-30DayHinv_Summary.sql: -------------------------------------------------------------------------------- 1 | SELECT TOP 30 2 | month(lasthw) [Month], day(lasthw) [Day], year(lasthw) [Year], count(*) [Count] 3 | FROM v_r_system_valid sys JOIN 4 | v_ch_clientsummary cs ON sys.resourceid = cs.resourceid 5 | GROUP BY month(lasthw), day(lasthw), year(lasthw) 6 | ORDER BY year(lasthw) DESC, month(lasthw) DESC, day(lasthw) DESC 7 | -------------------------------------------------------------------------------- /Get-BuzzPhrase.ps1: -------------------------------------------------------------------------------- 1 | function Get-BuzzPhrase { 2 | $Page = invoke-webrequest -uri https://www.atrixnet.com/bs-generator.html 3 | $(foreach ($WordType in @('adverbs', 'verbs', 'adjectives', 'nouns')) { 4 | ([regex]::match(($Page.AllElements)[1].outerHTML, "var $WordType = new Array \(([^\)]+)\)") -replace "var $WordType = new Array \(|\)" -split ',' -replace '''' -replace "`n").Trim() | Get-Random 5 | }) -join ' ' 6 | } 7 | -------------------------------------------------------------------------------- /Get-CMSoftwareUpdatePointSummary.ps1: -------------------------------------------------------------------------------- 1 | function Get-CMSoftwareUpdatePointSummary { 2 | param ( 3 | [Parameter(Mandatory = $true)] 4 | [string]$SMSProvider 5 | ) 6 | $SiteCode = $(Get-WmiObject -ComputerName $SMSProvider -Namespace 'root/SMS' -Class SMS_ProviderLocation -ErrorAction SilentlyContinue).SiteCode 7 | $WMIQueryParameters = @{ 8 | ComputerName = $SMSProvider 9 | NameSpace = "root\sms\site_$SiteCode" 10 | } 11 | 12 | $SoftwareUpdatePoints = Get-WmiObject -Query "SELECT NetbiosName FROM SMS_R_System where SystemRoles = 'SMS Software Update Point'" @WMIQueryParameters | Select-Object -ExpandProperty NetbiosName 13 | $SoftwareUpdatePoints | ForEach-Object { 14 | $ServerName = $_ -replace "\\" 15 | $ServerName = [system.net.dns]::GetHostByName($ServerName).HostNAme 16 | if (Test-Connection -ComputerName $ServerName -Quiet -Count 3) { 17 | $WSUS = @{ } 18 | $WSUS.ComputerName = $ServerName 19 | $WSUS.Site = $SiteCode 20 | $HKLM = 2147483650 21 | $WSUSConfigKeyPath = "SOFTWARE\Microsoft\Update Services\Server\Setup" 22 | $ConnectionProperties = @('PortNumber', 'UsingSSL') 23 | $WMI_Connection = Get-WmiObject -List "StdRegProv" -namespace root\default -ComputerName $ServerName 24 | foreach ($Property in $ConnectionProperties) { 25 | $WSUS.$Property = ($WMI_Connection.GetDWORDValue($hklm, $WSUSConfigKeyPath, $Property)).uValue 26 | } 27 | $WSUS_Server = Get-WsusServer -Name $WSUS['ComputerName'] -UseSsl:$WSUS['UsingSSL'] -PortNumber $WSUS['PortNumber'] 28 | $ServerConfig = $WSUS_Server.GetConfiguration() 29 | $DBConfig = $WSUS_Server.GetDatabaseConfiguration() 30 | $WSUS.SyncFromMicrosoftUpdate = $ServerConfig.SyncFromMicrosoftUpdate 31 | if (-not $WSUS.SyncFromMicrosoftUpdate) { 32 | $WSUS.UpstreamWsusServerName = $ServerConfig.UpstreamWsusServerName 33 | $WSUS.UpstreamWsusServerPortNumber = $ServerConfig.UpstreamWsusServerPortNumber 34 | $WSUS.UpstreamWsusServerUseSsl = $ServerConfig.UpstreamWsusServerUseSsl 35 | $WSUS.IsReplicaServer = $ServerConfig.IsReplicaServer 36 | } 37 | $WSUS.PortNumber = $WSUS_Server.PortNumber 38 | $WSUS.SqlServerName = $DBConfig.ServerName 39 | $WSUS.SqlDatabaseName = $DBConfig.DatabaseName 40 | $WSUS.UsingWID = $DBConfig.IsUsingWindowsInternalDatabase 41 | $WSUS.LocalContentCachePath = $ServerConfig.LocalContentCachePath 42 | $WSUS.SyncFromMicrosoftUpdate = $ServerConfig.SyncFromMicrosoftUpdate 43 | [pscustomobject]$WSUS | Select-Object -Property ComputerName, Site, PortNumber, UsingSSL, SqlServerName, SqlDatabaseName, UsingWID, LocalContentCachePath, SyncFromMicrosoftUpdate, UpstreamWsusServerName, UpstreamWsusServerPortNumber, UpstreamWsusServerUseSsl, IsReplicaServer 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Get-HinvFrequency.sql: -------------------------------------------------------------------------------- 1 | /* 2 | This query will show you the count of records per Hardware Inventory class in your HinvChangeLog table. 3 | This can help you identify a class that is create a lot of hardware inventory 'traffic' and potentially bloating 4 | your database. 5 | */ 6 | SELECT map.DisplayName 7 | , map.InvClassName AS 'Inventory View' 8 | , COUNT(HINV.RecordID) 9 | FROM HinvChangeLog hinv 10 | LEFT JOIN v_GroupMap map ON map.GroupID = hinv.GroupKey 11 | GROUP BY map.DisplayName 12 | , map.InvClassName 13 | ORDER BY 3 DESC 14 | -------------------------------------------------------------------------------- /Get-PendingSCCMPatches.ps1: -------------------------------------------------------------------------------- 1 | function Get-PendingSCCMPatches { 2 | [cmdletbinding()] 3 | param( 4 | # Computername(s) to invoke updates on 5 | [parameter(Mandatory = $false, ValueFromPipelineByPropertyName)] 6 | [Alias('Computer', 'PSComputerName', 'IPAddress', 'ServerName', 'HostName')] 7 | [string[]]$ComputerName = $env:COMPUTERNAME, 8 | # switch to determine if Definition updates should be included 9 | [switch]$IncludeDefs, 10 | # Credential to use 11 | [parameter(Mandatory = $false)] 12 | [system.management.automation.pscredential]$Credential 13 | ) 14 | begin { 15 | $UpdateStatus = @{ 16 | "23" = "WaitForOrchestration"; 17 | "22" = "WaitPresModeOff"; 18 | "21" = "WaitingRetry"; 19 | "20" = "PendingUpdate"; 20 | "19" = "PendingUserLogoff"; 21 | "18" = "WaitUserReconnect"; 22 | "17" = "WaitJobUserLogon"; 23 | "16" = "WaitUserLogoff"; 24 | "15" = "WaitUserLogon"; 25 | "14" = "WaitServiceWindow"; 26 | "13" = "Error"; 27 | "12" = "InstallComplete"; 28 | "11" = "Verifying"; 29 | "10" = "WaitReboot"; 30 | "9" = "PendingHardReboot"; 31 | "8" = "PendingSoftReboot"; 32 | "7" = "Installing"; 33 | "6" = "WaitInstall"; 34 | "5" = "Downloading"; 35 | "4" = "PreDownload"; 36 | "3" = "Detecting"; 37 | "2" = "Submitted"; 38 | "1" = "Available"; 39 | "0" = "None"; 40 | } 41 | #$UpdateStatus.Get_Item("$EvaluationState") 42 | #endregion status type hashtable 43 | 44 | $Filter = switch ($IncludeDefs) { 45 | $true { 46 | "ComplianceState=0" 47 | } 48 | Default { 49 | "NOT Name LIKE '%Definition%' and ComplianceState=0" 50 | } 51 | } 52 | } 53 | process { 54 | foreach ($Computer in $ComputerName) { 55 | try { 56 | if (Test-Connection -ComputerName $Computer -Count 1 -Quiet) { 57 | $getWmiObjectSplat = @{ 58 | Filter = $Filter 59 | ComputerName = $Computer 60 | Namespace = 'root\CCM\ClientSDK' 61 | Class = 'CCM_SoftwareUpdate' 62 | } 63 | if ($PSBoundParameters.ContainsKey('Credential')) { 64 | $getWmiObjectSplat.Add('Credential', $Credential) 65 | } 66 | [System.Management.ManagementObject[]]$MissingUpdates = Get-WmiObject @getWmiObjectSplat 67 | if ($MissingUpdates -is [Object] -and $MissingUpdates.Count -gt 0) { 68 | $MissingUpdates 69 | } 70 | else { 71 | Write-Verbose "No updates found for $Computer" 72 | } 73 | } 74 | else { 75 | Write-Warning "$Computer is not online" 76 | } 77 | } 78 | catch { 79 | $ErrorMessage = $_.Exception.Message 80 | Write-Error $ErrorMessage 81 | } 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Get-SupersededUpdatesInSUGBySUG.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | [Parameter(Mandatory = $true)] 3 | [string]$SoftwareUpdateGroupName 4 | ) 5 | # return a list of updates which are superseded by another update in the same SUG 6 | $SUG = Get-CMSoftwareUpdateGroup -Name $SoftwareUpdateGroupName 7 | $allInSUG = Get-CMSoftwareUpdate -UpdateGroup $SUG 8 | $updatesSugSupersedes = foreach ($Updates in $allInSUG) { 9 | ([xml]$Updates.sdmpackagexml).GetElementsByTagName("SupersededUpdates").SoftwareUpdateReference.LogicalName 10 | } 11 | return $allInSUG.Where( { [string]::Format("SUM_{0}", $_.CI_UniqueID) -in $updatesSugSupersedes }) 12 | -------------------------------------------------------------------------------- /Get-WQLObject.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | # WQL formatted query to perform 3 | [Parameter(Mandatory = $true, ParameterSetName = 'CustomQuery')] 4 | [string] 5 | $Query, 6 | # SMS Provider to query against 7 | [Parameter(Mandatory = $true)] 8 | [string] 9 | $SMSProvider, 10 | # Optional PSCredential (unfortunately I can't figure out how to use this cred in the DynamicParam WMI queries without providing info outside the function) 11 | [Parameter(Mandatory = $false, ParameterSetName = 'CustomQuery')] 12 | [pscredential] 13 | $Credential 14 | ) 15 | DynamicParam { 16 | if (($SMSProvider = $PSBoundParameters['SMSProvider'])) { 17 | $ParameterName = 'SCCMQuery' 18 | $RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary 19 | $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] 20 | $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute 21 | $ParameterAttribute.Mandatory = $true 22 | $ParameterAttribute.ParameterSetName = 'ExistingQuery' 23 | $ParameterAttribute.HelpMessage = 'Specify the name of a query that already exists in your ConfigMgr environment' 24 | $AttributeCollection.Add($ParameterAttribute) 25 | $SiteCode = (Get-WmiObject -Namespace "root\sms" -ClassName "__Namespace" -ComputerName $SMSProvider).Name.Substring(5, 3) 26 | $Namespace = [string]::Format("root\sms\site_{0}", $SiteCode) 27 | $arrSet = Get-WmiObject -ComputerName $SMSProvider -Namespace $Namespace -Query "SELECT Name FROM SMS_Query WHERE Expression not like '%##PRM:%'" | Select-Object -ExpandProperty Name 28 | $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet) 29 | $AttributeCollection.Add($ValidateSetAttribute) 30 | $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ParameterName, [string], $AttributeCollection) 31 | $RuntimeParameterDictionary.Add($ParameterName, $RuntimeParameter) 32 | return $RuntimeParameterDictionary 33 | } 34 | } 35 | Begin { 36 | $SCCMQuery = $PsBoundParameters[$ParameterName] 37 | if ($PSBoundParameters.ContainsKey('Credential') -and -not $PSDefaultParameterValues.ContainsKey("Get-WmiObject:Credential")) { 38 | $AddedDefaultParam = $true 39 | $PSDefaultParameterValues.Add("Get-WmiObject:Credential", $Credential) 40 | } 41 | $SiteCode = (Get-WmiObject -Namespace "root\sms" -ClassName "__Namespace" -ComputerName $SMSProvider).Name.Substring(5, 3) 42 | $Namespace = [string]::Format("root\sms\site_{0}", $SiteCode) 43 | if ($PSCmdlet.ParameterSetName -eq 'ExistingQuery') { 44 | $Query = Get-WmiObject -ComputerName $SMSProvider -Namespace $Namespace -Query "SELECT Expression FROM SMS_Query WHERE Name ='$SCCMQuery'" | Select-Object -ExpandProperty Expression 45 | } 46 | } 47 | Process { 48 | $RawResults = Get-WmiObject -ComputerName $SMSProvider -Namespace $Namespace -Query $Query 49 | $PropertySelectors = $RawResults | Get-Member -MemberType Property | Where-Object { -not $_.Name.StartsWith('__') } | Select-Object -ExpandProperty name | ForEach-Object { 50 | $Class = $_ 51 | $Properties = $RawResults.$Class | Get-Member -MemberType Property | Where-Object { -not $_.Name.StartsWith('__') } | Select-Object -ExpandProperty name 52 | foreach ($Property in $Properties) { 53 | [string]::Format("@{{Label='{1}.{0}';Expression = {{`$_.{1}.{0}}}}}", $Property, $Class) 54 | } 55 | } 56 | } 57 | end { 58 | if ($AddedDefaultParam) { 59 | $PSDefaultParameterValues.Remove("Get-WmiObject:Credential") 60 | } 61 | $PropertySelector = [scriptblock]::Create($($PropertySelectors -join ',')) 62 | $RawResults | Select-Object -Property $(. $PropertySelector) 63 | } 64 | -------------------------------------------------------------------------------- /Invoke-SCCMUpdates.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-SCCMUpdates { 2 | param( 3 | # Computername(s) to invoke updates on 4 | [parameter(Mandatory = $false, ValueFromPipelineByPropertyName)] 5 | [Alias('Computer', 'PSComputerName', 'IPAddress')] 6 | [string[]]$ComputerName = $env:COMPUTERNAME, 7 | # switch to determine if Definition updates should be included 8 | [parameter(Mandatory = $false)] 9 | [switch]$IncludeDefs, 10 | # invoke a set of updates 11 | [parameter(Mandatory = $false, ParameterSetName = 'PassUpdates', ValueFromPipeline)] 12 | [System.Management.ManagementObject[]]$Updates, 13 | # Credential to use 14 | [parameter(Mandatory = $false)] 15 | [pscredential]$Credential 16 | ) 17 | begin { 18 | $UpdateStatus = @{ 19 | "23" = "WaitForOrchestration"; 20 | "22" = "WaitPresModeOff"; 21 | "21" = "WaitingRetry"; 22 | "20" = "PendingUpdate"; 23 | "19" = "PendingUserLogoff"; 24 | "18" = "WaitUserReconnect"; 25 | "17" = "WaitJobUserLogon"; 26 | "16" = "WaitUserLogoff"; 27 | "15" = "WaitUserLogon"; 28 | "14" = "WaitServiceWindow"; 29 | "13" = "Error"; 30 | "12" = "InstallComplete"; 31 | "11" = "Verifying"; 32 | "10" = "WaitReboot"; 33 | "9" = "PendingHardReboot"; 34 | "8" = "PendingSoftReboot"; 35 | "7" = "Installing"; 36 | "6" = "WaitInstall"; 37 | "5" = "Downloading"; 38 | "4" = "PreDownload"; 39 | "3" = "Detecting"; 40 | "2" = "Submitted"; 41 | "1" = "Available"; 42 | "0" = "None"; 43 | } 44 | #$UpdateStatus.Get_Item("$EvaluationState") 45 | #endregion status type hashtable 46 | 47 | $Filter = switch ($IncludeDefs) { 48 | $true { 49 | "ComplianceState=0" 50 | } 51 | Default { 52 | "NOT Name LIKE '%Definition%' and ComplianceState=0" 53 | } 54 | } 55 | } 56 | process { 57 | foreach ($Computer in $ComputerName) { 58 | try { 59 | if (Test-Connection -ComputerName $Computer -Count 1 -Quiet) { 60 | if ($PSCmdlet.ParameterSetName -eq 'PassUpdates') { 61 | $invokeWmiMethodSplat = @{ 62 | Namespace = 'root\ccm\clientsdk' 63 | ArgumentList = ( , $Updates) 64 | ComputerName = $Computer 65 | Name = 'InstallUpdates' 66 | Class = 'CCM_SoftwareUpdatesManager' 67 | } 68 | if ($PSBoundParameters.ContainsKey('Credential')) { 69 | $invokeWmiMethodSplat.Add('Credential', $Credential) 70 | } 71 | Invoke-WmiMethod @invokeWmiMethodSplat 72 | } 73 | else { 74 | $getWmiObjectSplat = @{ 75 | Filter = $Filter 76 | ComputerName = $Computer 77 | Namespace = 'root\CCM\ClientSDK' 78 | Class = 'CCM_SoftwareUpdate' 79 | } 80 | if ($PSBoundParameters.ContainsKey('Credential')) { 81 | $getWmiObjectSplat.Add('Credential', $Credential) 82 | } 83 | [System.Management.ManagementObject[]]$MissingUpdates = Get-WmiObject @getWmiObjectSplat 84 | if ($MissingUpdates -is [Object]) { 85 | $invokeWmiMethodSplat = @{ 86 | Namespace = 'root\ccm\clientsdk' 87 | ArgumentList = ( , $MissingUpdates) 88 | ComputerName = $Computer 89 | Name = 'InstallUpdates' 90 | Class = 'CCM_SoftwareUpdatesManager' 91 | } 92 | if ($PSBoundParameters.ContainsKey('Credential')) { 93 | $invokeWmiMethodSplat.Add('Credential', $Credential) 94 | } 95 | Invoke-WmiMethod @invokeWmiMethodSplat 96 | } 97 | else { 98 | Write-Output "$Computer has no updates available to invoke" 99 | } 100 | } 101 | } 102 | else { 103 | Write-Warning "$Computer is not online" 104 | } 105 | } 106 | catch { 107 | $ErrorMessage = $_.Exception.Message 108 | Write-Error $ErrorMessage 109 | } 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /Invoke-SQLAGPatchPrep.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | [Parameter(Mandatory = $false)] 3 | [ValidateSet('Drain', 'Resume')] 4 | [string]$Purpose = 'Resume' 5 | ) 6 | $AvailabilityGroups = Get-DbaAvailabilityGroup -SqlInstance $env:COMPUTERNAME 7 | foreach ($AG in $AvailabilityGroups) { 8 | $DatabaseName = $AG.AvailabilityDatabases.Name 9 | $AGName = $AG.AvailabilityGroup 10 | $AGListener = $AG.AvailabilityGroupListeners.Name 11 | $PrimaryNode = $AG.PrimaryReplica 12 | 13 | $CheckAG = @" 14 | SELECT name AS [AGname] 15 | , replica_server_name AS [ServerName] 16 | , CASE 17 | WHEN replica_server_name=ag.primary_replica THEN 'PRIMARY' 18 | ELSE 'SECONDARY' 19 | END AS [Status] 20 | , synchronization_health_desc AS [SynchronizationHealth] 21 | , failover_mode_desc AS [FailoverMode] 22 | , availability_mode AS [Synchronous] 23 | , secondary_role_allow_connections_desc [ReadableSecondary] 24 | FROM sys.availability_replicas r 25 | INNER JOIN sys.availability_groups g ON r.group_id = g.group_id 26 | LEFT JOIN master.sys.dm_hadr_availability_group_states ag ON r.group_id = ag.group_id 27 | "@ 28 | 29 | 30 | switch ($Purpose) { 31 | 'Drain' { 32 | try { 33 | $StartStateAG = Invoke-DbaQuery -SqlInstance $AGListener -Query $CheckAG -ErrorAction Stop 34 | $SecondaryNode = $StartStateAG | Where-Object { $_.Status -eq 'SECONDARY' } | Select-Object -ExpandProperty ServerName 35 | } 36 | catch { 37 | Write-Error 'Failed to query for Availability Group status' 38 | exit 1 39 | } 40 | 41 | switch ($StartStateAG.SynchronizationHealth) { 42 | 'HEALTHY' { 43 | continue; 44 | } 45 | default { 46 | Write-Error 'At least one node has unhealthy SynchronizationHealth' 47 | exit 1 48 | } 49 | } 50 | 51 | switch ($StartStateAG) { 52 | { $_.FailoverMode -ne 'MANUAL' } { 53 | Set-DbaAgReplica -SqlInstance $PrimaryNode -AvailabilityGroup $_.AGname -Replica $_.ServerName -FailoverMode Manual 54 | } 55 | } 56 | 57 | switch ($PrimaryNode -eq $env:COMPUTERNAME) { 58 | $true { 59 | Invoke-DbaAgFailover -SqlInstance $SecondaryNode -AvailabilityGroup $AGName -Force 60 | } 61 | } 62 | 63 | Suspend-DbaAgDbDataMovement -SqlInstance $env:COMPUTERNAME -AvailabilityGroup $AGName -Database $DatabaseName -Confirm:$false 64 | 65 | $ClusterStatus = Get-ClusterNode -Name $env:COMPUTERNAME 66 | switch ($ClusterStatus.State) { 67 | 'Up' { 68 | Suspend-ClusterNode -Wait -Drain 69 | } 70 | } 71 | } 72 | 'Resume' { 73 | $PrimaryNode = $AG.PrimaryReplica 74 | $ClusterStatus = Get-ClusterNode -Name $env:COMPUTERNAME 75 | switch ($ClusterStatus.State) { 76 | 'Up' { 77 | continue 78 | } 79 | default { 80 | Resume-ClusterNode -Name $env:COMPUTERNAME 81 | } 82 | } 83 | foreach ($Node in $AG.AvailabilityReplicas.Name) { 84 | Set-DbaAgReplica -SqlInstance $PrimaryNode -AvailabilityGroup $AG.AvailabilityGroup -Replica $Node -FailoverMode Automatic 85 | } 86 | 87 | Resume-DbaAgDbDataMovement -SqlInstance $env:COMPUTERNAME -AvailabilityGroup $AG.AvailabilityGroup -Database $AG.AvailabilityDatabases.Name -Confirm:$false 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /Invoke-UpdateScanAfterSSU/CI - Invoke-UpdateScanAfterSSU - Scheduled Task.cab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodyMathis123/CM-Ramblings/ed952ee8966a9914ba5bff976020b5f16c20758f/Invoke-UpdateScanAfterSSU/CI - Invoke-UpdateScanAfterSSU - Scheduled Task.cab -------------------------------------------------------------------------------- /Invoke-UpdateScanAfterSSU/Invoke-UpdateScanAfterSSU.ps1: -------------------------------------------------------------------------------- 1 | #region detection 2 | $TaskName = 'Invoke-UpdateScanAfterSSU' 3 | 4 | $Task = Get-ScheduledTask -TaskName $TaskName -ErrorAction SilentlyContinue 5 | 6 | $null -ne $Task 7 | #endregion detection 8 | 9 | #region remediation 10 | $TaskName = 'Invoke-UpdateScanAfterSSU' 11 | 12 | $TaskXML = @" 13 | 14 | 16 | 17 | 2019-05-22T23:53:35.7629665 18 | SCCM 19 | \$TaskName 20 | 21 | 22 | 23 | 24 | PT5M 25 | PT15M 26 | true 27 | 28 | PT30M 29 | true 30 | <QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name='Microsoft-Windows-WindowsUpdateClient'] and EventID=19]]</Select></Query></QueryList> 31 | PT1M 32 | 33 | 34 | 35 | 36 | S-1-5-18 37 | LeastPrivilege 38 | 39 | 40 | 41 | IgnoreNew 42 | true 43 | true 44 | true 45 | false 46 | false 47 | 48 | true 49 | false 50 | 51 | true 52 | true 53 | false 54 | false 55 | false 56 | PT72H 57 | 7 58 | 59 | 60 | 61 | C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe 62 | -command "&{`$TimeFrame=(Get-Date).AddMinutes(-17);`$Filter=@{LogName='System';StartTime=`$TimeFrame;Id='19';ProviderName='Microsoft-Windows-WindowsUpdateClient';};`$Events=Get-WinEvent -FilterHashtable `$Filter;foreach(`$Event in `$Events){switch -Regex (`$Event.Message){'Servicing Stack Update'{foreach(`$Schedule in @('108','113')){`$ScheduleString = [string]::Format('{{00000000-0000-0000-0000-000000000{0}}}',`$Schedule);`$invokeWmiMethodSplat=@{Name='TriggerSchedule';Namespace='root\ccm';Class='sms_client';ArgumentList=`$ScheduleString;ErrorAction='Stop';};Invoke-WmiMethod @invokeWmiMethodSplat;}}}}}" 63 | 64 | 65 | 66 | "@ 67 | 68 | Register-ScheduledTask -Xml $TaskXML -TaskName $TaskName 69 | #endregion remediation 70 | -------------------------------------------------------------------------------- /Invoke-UpdateScanAfterSSU/readme.md: -------------------------------------------------------------------------------- 1 | https://sccmf12twice.com/2019/05/the-cure-for-your-ssu-fever/ 2 | -------------------------------------------------------------------------------- /MEMCM_PolicyPriority/README.md: -------------------------------------------------------------------------------- 1 | #### MEMCM Policy Priority 2 | 3 | If you have ever been in an environment with even a handful of AV Policies, or Client Settings policies in MEMCM then you have felt the pain of clicking increase, or decrease over and over to appropriately set policy priority. 4 | 5 | In this folder you'll find a couple quick functions to simplify setting the priority of a MEMCM Antimalware Policy, or Client Settings policy. Each functions supports -Verbose, and -WhatIf. An example of using each of them is below. 6 | 7 | ```ps 8 | Set-CMAntimalwarePolicyPriority -Name AVPolicy1 -Priority 3 9 | Set-CMClientSettingPriority -Name ClientSettings1 -Priority 1 10 | ``` 11 | 12 | Whether the priority needs to increase, or decrease is handled automatically. You simply provide the desired priority and the let the function do the rest! 13 | 14 | They could be improved by adding pipeline support from the builtin Get-CM* cmdlets for the respective types.... feel free to add a pull request :) 15 | -------------------------------------------------------------------------------- /MEMCM_PolicyPriority/Set-CMAntimalwarePolicyPriority.ps1: -------------------------------------------------------------------------------- 1 | function Set-CMAntimalwarePolicyPriority { 2 | <# 3 | .SYNOPSIS 4 | Move a MEMCM Antimalware policy to the specified priority 5 | .DESCRIPTION 6 | Becuase the GUI does not allow you to move more than one step at a time, 7 | this PowerShell function can be used to set an Antimalware policy to a specific 8 | priority without having to click increase, or decrease over and over 9 | .PARAMETER Name 10 | The name of the Antimalware Policy to increase or decrease the priority of 11 | .PARAMETER Priority 12 | The desired priority to set the Antimalware Policy to 13 | .EXAMPLE 14 | PS C:\> Set-CMAntimalwarePolicyPriority -Name 'AVPolicy1' -Priority 3 15 | Will move the priority of the AVPolicy1 up, or down, until it reaches priority 3 16 | .NOTES 17 | FileName: Set-CMAntimalwarePolicyPriority.ps1 18 | Author: Cody Mathis 19 | Contact: @CodyMathis123 20 | Created: 2020-07-07 21 | Updated: 2020-07-07 22 | #> 23 | [CmdletBinding(SupportsShouldProcess = $true)] 24 | param ( 25 | [Parameter(Mandatory = $true)] 26 | [string]$Name, 27 | [Parameter(Mandatory = $true)] 28 | [int]$Priority 29 | ) 30 | $Policy = Get-CMAntimalwarePolicy -Name $Name 31 | if ($null -eq $Policy) { 32 | Write-Warning "No AntiMalware policy found with [Name: $Name]" 33 | return $false 34 | } 35 | $BeginningPriority = $Policy.Priority 36 | 37 | if ($BeginningPriority -eq $Priority) { 38 | Write-Verbose "Policy [Name: $Name] found with [Priority: $BeginningPriority] already set" 39 | return $true 40 | } 41 | else { 42 | Write-Verbose "Policy [Name: $Name] found with [Priority: $BeginningPriority] - will increase or decrease as needed" 43 | 44 | switch ($Priority -gt $BeginningPriority) { 45 | $true { 46 | Write-Verbose "Will Decrease policy priority until it reaches $Priority" 47 | if ($PSCmdlet.ShouldProcess("[PolicyName: $Name] [DesiredPriority: $Priority] [Action: Decrease]", "Set-CMAntimalwarePolicyPriority")) { 48 | 49 | Do { 50 | $Policy | Set-CMAntimalwarePolicy -Priority Decrease 51 | } 52 | until ((Get-CMAntimalwarePolicy -Name $Name).Priority -eq $Priority) 53 | } 54 | } 55 | $false { 56 | Write-Verbose "Will Increase policy priority until it reaches $Priority" 57 | if ($PSCmdlet.ShouldProcess("[PolicyName: $Name] [DesiredPriority: $Priority] [Action: Increase]", "Set-CMAntimalwarePolicyPriority")) { 58 | 59 | Do { 60 | $Policy | Set-CMAntimalwarePolicy -Priority Increase 61 | } 62 | until ((Get-CMAntimalwarePolicy -Name $Name).Priority -eq $Priority) 63 | } 64 | } 65 | } 66 | 67 | $EndPriority = (Get-CMAntimalwarePolicy -Name $Name).Priority 68 | if ($EndPriority -eq $Priority) { 69 | return $true 70 | } 71 | else { 72 | return $false 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /MEMCM_PolicyPriority/Set-CMClientSettingPriority.ps1: -------------------------------------------------------------------------------- 1 | function Set-CMClientSettingPriority { 2 | <# 3 | .SYNOPSIS 4 | Move a MEMCM Client policy to the specified priority 5 | .DESCRIPTION 6 | Becuase the GUI does not allow you to move more than one step at a time, 7 | this PowerShell function can be used to set an Client Settings policy to a specific 8 | priority without having to click increase, or decrease over and over 9 | .PARAMETER Name 10 | The name of the Client Policy to increase or decrease the priority of 11 | .PARAMETER Priority 12 | The desired priority to set the Client Policy to 13 | .EXAMPLE 14 | PS C:\> Set-CMClientSettingPriority -Name 'ClientSetting1' -Priority 3 15 | Will move the priority of the ClientSetting1 up, or down, until it reaches priority 3 16 | .NOTES 17 | FileName: Set-CMClientSettingPriority.ps1 18 | Author: Cody Mathis 19 | Contact: @CodyMathis123 20 | Created: 2020-07-07 21 | Updated: 2020-07-07 22 | #> 23 | [CmdletBinding(SupportsShouldProcess = $true)] 24 | param ( 25 | [Parameter(Mandatory = $true)] 26 | [string]$Name, 27 | [Parameter(Mandatory = $true)] 28 | [int]$Priority 29 | ) 30 | $Policy = Get-CMClientSetting -Name $Name 31 | if ($null -eq $Policy) { 32 | Write-Warning "No Client policy found with [Name: $Name]" 33 | return $false 34 | } 35 | $BeginningPriority = $Policy.Priority 36 | 37 | if ($BeginningPriority -eq $Priority) { 38 | Write-Verbose "Policy [Name: $Name] found with [Priority: $BeginningPriority] already set" 39 | return $true 40 | } 41 | else { 42 | Write-Verbose "Policy [Name: $Name] found with [Priority: $BeginningPriority] - will increase or decrease as needed" 43 | 44 | switch ($Priority -gt $BeginningPriority) { 45 | $true { 46 | Write-Verbose "Will Decrease policy priority until it reaches $Priority" 47 | if ($PSCmdlet.ShouldProcess("[PolicyName: $Name] [DesiredPriority: $Priority] [Action: Decrease]", "Set-CMClientSettingPriority")) { 48 | 49 | Do { 50 | $Policy | Set-CMClientSettingGeneral -Priority Decrease 51 | } 52 | until ((Get-CMClientSetting -Name $Name).Priority -eq $Priority) 53 | } 54 | } 55 | $false { 56 | Write-Verbose "Will Increase policy priority until it reaches $Priority" 57 | if ($PSCmdlet.ShouldProcess("[PolicyName: $Name] [DesiredPriority: $Priority] [Action: Increase]", "Set-CMClientSettingPriority")) { 58 | 59 | Do { 60 | $Policy | Set-CMClientSettingGeneral -Priority Increase 61 | } 62 | until ((Get-CMClientSetting -Name $Name).Priority -eq $Priority) 63 | } 64 | } 65 | } 66 | 67 | $EndPriority = (Get-CMClientSetting -Name $Name).Priority 68 | if ($EndPriority -eq $Priority) { 69 | return $true 70 | } 71 | else { 72 | return $false 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /MachineCertInv.ps1: -------------------------------------------------------------------------------- 1 | ## Define new class name and date 2 | $NewClassName = 'Win32_MachineCerts' 3 | 4 | ## Remove class if exists 5 | Remove-WmiObject -Class $NewClassName -ErrorAction SilentlyContinue 6 | 7 | # Create new WMI class 8 | $newClass = New-Object System.Management.ManagementClass ("root\cimv2", [String]::Empty, $null) 9 | $newClass["__CLASS"] = $NewClassName 10 | 11 | ## Create properties you want inventoried 12 | $newClass.Qualifiers.Add("Static", $true) 13 | $newClass.Properties.Add("PSPath", [System.Management.CimType]::String, $false) 14 | $newClass.Properties.Add("PSParentPath", [System.Management.CimType]::String, $false) 15 | $newClass.Properties.Add("Issuer", [System.Management.CimType]::String, $false) 16 | $newClass.Properties.Add("Subject", [System.Management.CimType]::String, $false) 17 | $newClass.Properties.Add("FriendlyName", [System.Management.CimType]::String, $false) 18 | $newClass.Properties.Add("Version", [System.Management.CimType]::String, $false) 19 | $newClass.Properties.Add("Thumbprint", [System.Management.CimType]::String, $false) 20 | $newClass.Properties.Add("NotAfter", [System.Management.CimType]::String, $false) 21 | $newClass.Properties.Add("NotBefore", [System.Management.CimType]::String, $false) 22 | $newClass.Properties.Add("DNSNameList", [System.Management.CimType]::String, $false) 23 | $newClass.Properties["PSPath"].Qualifiers.Add("Key", $true) 24 | $newClass.Put() | Out-Null 25 | 26 | ## Gather current cert information 27 | Get-ChildItem Cert:\LocalMachine -Recurse | Where-Object { $_.PSisContainer -eq $false } | Select-Object PSPath, PSparentpath, Issuer, Subject, FriendlyName, Version, Thumbprint, NotAfter, NotBefore, DNSNamelist | 28 | ForEach-Object { 29 | 30 | ## Set cert information in new class 31 | Set-WmiInstance -Namespace root\cimv2 -class $NewClassName -ErrorAction SilentlyContinue -Arguments @{ 32 | PSParentPath = $_.PSParentPath 33 | Issuer = $_.Issuer 34 | Subject = $_.Subject 35 | FriendlyName = $_.FriendlyName 36 | Version = $_.Version 37 | Thumbprint = $_.Thumbprint 38 | NotAfter = $_.NotAfter 39 | NotBefore = $_.NotBefore 40 | DNSNamelist = $_.DNSNamelist 41 | PSPath = $_.PSPath 42 | } | Out-Null 43 | } 44 | 45 | Write-Output "Complete" 46 | -------------------------------------------------------------------------------- /New-CMScheduleStartTime.ps1: -------------------------------------------------------------------------------- 1 | Function New-CMScheduleStartTime { 2 | [CmdletBinding()] 3 | <# 4 | .SYNOPSIS 5 | Recreate a CMSchedule object with a new start time 6 | .DESCRIPTION 7 | Natively, the CMSchedule objects do not allow you to write to the StartTime property. This makes it 8 | difficult to adjust the start time of an existing maintenance window. This function can be used to 9 | 'recreate' a CMSchedule based on the input schedule, with a new start time. 10 | .PARAMETER CMSchedule 11 | An array of CMSchedule objects 12 | .PARAMETER StartTime 13 | The desired new start time for the schedule 14 | .EXAMPLE 15 | CCM:\> $Sched = Get-CMMaintenanceWindow -CollectionName 'test' 16 | $Schedobject = Convert-CMSchedule -ScheduleString $Sched.ServiceWindowSchedules 17 | New-CMScheduleStartTime -CMSchedule $Schedobject -StartTime $Schedobject.StartTime.AddDays(5) 18 | .NOTES 19 | FileName: New-CMScheduleStartTime.ps1 20 | Author: Cody Mathis 21 | Contact: @CodyMathis123 22 | Created: 2020-02-26 23 | Updated: 2020-02-26 24 | #> 25 | Param( 26 | [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] 27 | [Alias('Schedules')] 28 | [Microsoft.ConfigurationManagement.ManagementProvider.WqlQueryEngine.WqlResultObjectBase[]]$CMSchedule, 29 | [Parameter(Mandatory = $true)] 30 | [datetime]$StartTime 31 | ) 32 | begin { 33 | #region create our splat for the new schedule, start time is the same for all 34 | $NewSchedSplat = @{ 35 | Start = $StartTime 36 | } 37 | #endregion create our splat for the new schedule, start time is the same for all 38 | } 39 | process { 40 | foreach ($Schedule in $CMSchedule) { 41 | #region determine new end time based off new start time, and existing durations 42 | $NewEndTime = $StartTime.AddDays($Schedule.DayDuration).AddHours($Schedule.HourDuration).AddMinutes($Schedule.MinuteDuration) 43 | #endregion determine new end time based off new start time, and existing durations 44 | 45 | #region define the paramters that are the same for all 'new' schedules 46 | $NewSchedSplat['End'] = $NewEndTime 47 | $NewSchedSplat['IsUTC'] = $Schedule.IsGMT 48 | #endregion define the paramters that are the same for all 'new' schedules 49 | 50 | try { 51 | #region based on recur type, we will add parameters to our $NewSchedSplat 52 | Switch ($Schedule.SmsProviderObjectPath) { 53 | SMS_ST_NonRecurring { 54 | $NewSchedSplat['Nonrecurring'] = $true 55 | } 56 | SMS_ST_RecurInterval { 57 | if ($Schedule.MinuteSpan -ne 0) { 58 | $Span = 'Minutes' 59 | $Interval = $Schedule.MinuteSpan 60 | } 61 | elseif ($Schedule.HourSpan -ne 0) { 62 | $Span = 'Hours' 63 | $Interval = $Schedule.HourSpan 64 | } 65 | elseif ($Schedule.DaySpan -ne 0) { 66 | $Span = 'Days' 67 | $Interval = $Schedule.DaySpan 68 | } 69 | $NewSchedSplat['RecurInterval'] = $Span 70 | $NewSchedSplat['RecurCount'] = $Interval 71 | } 72 | SMS_ST_RecurWeekly { 73 | $NewSchedSplat['DayOfWeek'] = [DayOfWeek]($Day - $Schedule.Day) 74 | $NewSchedSplat['RecurCount'] = $Schedule.ForNumberOfWeeks 75 | } 76 | SMS_ST_RecurMonthlyByWeekday { 77 | $NewSchedSplat['DayOfWeek'] = [DayOfWeek]($Day - $Schedule.Day) 78 | $NewSchedSplat['WeekOrder'] = $Schedule.WeekOrder 79 | $NewSchedSplat['RecurCount'] = $Schedule.ForNumberOfMonths 80 | } 81 | SMS_ST_RecurMonthlyByDate { 82 | $NewSchedSplat['DayOfMonth'] = $Schedule.MonthDay 83 | $NewSchedSplat['RecurCount'] = $Schedule.ForNumberOfMonths 84 | } 85 | Default { 86 | Write-Error "Parsing Schedule String resulted in invalid type of $RecurType" -ErrorAction Stop 87 | } 88 | } 89 | #endregion based on recur type, we will add parameters to our $NewSchedSplat 90 | 91 | #region return our new CMSchedule 92 | New-CMSchedule @NewSchedSplat 93 | #endregion return our new CMSchedule 94 | } 95 | Catch { 96 | $_.Exception.Message 97 | } 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /New-PostTeamsMachineWideInstallScheduledTask.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Launch the Teams install as the logged in user via a scheduled task 4 | .DESCRIPTION 5 | This script will create a scheduled task if one is not found which is used to 6 | install Microsoft Teams as the logged in user. This scheduled task executes 7 | the Teams.exe found in %ProgramFiles% so that a user can start using Teams 8 | shortly after the Machine Wide installer completes instead of having to 9 | log off and back on. 10 | .NOTES 11 | Generally used as a script that runs after a Teams Machine Wide Installer completes 12 | #> 13 | if (!(Get-ScheduledTask -TaskName 'Teams User Install - Post Machine Wide Install' -ErrorAction SilentlyContinue)) { 14 | switch ([System.Environment]::Is64BitOperatingSystem) { 15 | $true { 16 | [string]$TeamsMachineInstaller = Get-ItemPropertyValue -Path registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Run\ -Name TeamsMachineInstaller -ErrorAction Stop 17 | [string]$Exe = $TeamsMachineInstaller.Substring(0, $TeamsMachineInstaller.IndexOf('.exe') + 4).Trim() -Replace "C:\\Program Files\\", "${env:ProgramFiles(x86)}\" 18 | 19 | } 20 | $false { 21 | [string]$TeamsMachineInstaller = Get-ItemPropertyValue -Path registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run\ -Name TeamsMachineInstaller -ErrorAction Stop 22 | [string]$Exe = $TeamsMachineInstaller.Substring(0, $TeamsMachineInstaller.IndexOf('.exe') + 4).Trim() 23 | } 24 | } 25 | [string]$InstallerArgs = $TeamsMachineInstaller.Substring($Exe.Length, $TeamsMachineInstaller.Length - $exe.Length).Trim() 26 | $newScheduledTaskSplat = @{ 27 | Action = New-ScheduledTaskAction -Execute $Exe -Argument $InstallerArgs 28 | Description = 'Start the Teams installer for the currently logged on user after a Teams Machine Wide install' 29 | Settings = New-ScheduledTaskSettingsSet -Compatibility Vista -AllowStartIfOnBatteries -MultipleInstances IgnoreNew -ExecutionTimeLimit (New-TimeSpan -Hours 1) 30 | Trigger = New-ScheduledTaskTrigger -At ($Start = (Get-Date).AddSeconds(5)) -Once 31 | Principal = New-ScheduledTaskPrincipal -GroupId 'S-1-5-32-545' -RunLevel Limited 32 | } 33 | 34 | $ScheduledTask = New-ScheduledTask @newScheduledTaskSplat 35 | $ScheduledTask.Settings.DeleteExpiredTaskAfter = "PT0S" 36 | $ScheduledTask.Triggers[0].StartBoundary = $Start.ToString("yyyy-MM-dd'T'HH:mm:ss") 37 | $ScheduledTask.Triggers[0].EndBoundary = $Start.AddMinutes(10).ToString('s') 38 | 39 | Register-ScheduledTask -InputObject $ScheduledTask -TaskName 'Teams User Install - Post Machine Wide Install' 40 | } -------------------------------------------------------------------------------- /Office365/O365-PrjOnline-VisPro-2016.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/O365-PrjOnline-VisPro-2019.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/O365-PrjOnline-VisStd-2016.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/O365-PrjOnline-VisStd-2019.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/O365-PrjOnline.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/O365-PrjPro-2016.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/O365-PrjPro-2019.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/O365-PrjStd-2016.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/O365-PrjStd-2019.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/O365-VisOnline-PrjOnline.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/O365-VisOnline-PrjPro-2016.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/O365-VisOnline-PrjPro-2019.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/O365-VisOnline-PrjStd-2016.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/O365-VisOnline-PrjStd-2019.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/O365-VisOnline.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/O365-VisPro-2016-PrjOnline.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/O365-VisPro-2016.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/O365-VisPro-2019-PrjOnline.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/O365-VisPro-2019.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/O365-VisPro-PrjPro-2016.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/O365-VisPro-PrjPro-2019.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/O365-VisPro-PrjStd-2016.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/O365-VisPro-PrjStd-2019.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/O365-VisStd-2016-PrjOnline.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/O365-VisStd-2016.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/O365-VisStd-2019-PrjOnline.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/O365-VisStd-2019.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/O365-VisStd-PrjPro-2016.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/O365-VisStd-PrjPro-2019.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/O365-VisStd-PrjStd-2016.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/O365-VisStd-PrjStd-2019.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/O365.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Office365/PrjOnline.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/PrjPro-2016.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/PrjPro-2019.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/PrjStd-2016.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/PrjStd-2019.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/VisOnline.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/VisPro-2016.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/VisPro-2019.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/VisStd-2016.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/VisStd-2019.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Office365/readme.md: -------------------------------------------------------------------------------- 1 | https://sccmf12twice.com/2019/05/getting-from-msi-to-c2r/ 2 | 3 | End Product - One application with all possible deployment types that will dynamically install the 'new' version of Office / Project / Visio as needed based on your licensing selection. 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | You can place all the XML, and Office 365 c2r setup.exe in a directory. 12 | 13 | You'll want to run: 14 | 15 | setup.exe /download Office365.xml 16 | 17 | Once to get the ball rolling. The binaries are the same for every single app combination. 18 | 19 | The script has 11 parameters that you can see below in the help info from the script. 20 | 21 | .PARAMETER SMSProvider
22 |     Provides the name for the SMSProvider for the environment you want to create the application in.
23 | .PARAMETER ApplicationName
24 |     Provides the name which you want assigned to the application that is created by this script.
25 | .PARAMETER Company
26 |     Provides the company name you want specified in all of the XML files.
27 | .PARAMETER AppRoot
28 |     Provides the root directory for the application to be created. This should be pre-populated with the provided XML files,
29 |     and it will be used as the source directory for all the deployment types.
30 | .PARAMETER Bitness
31 |     Provides the desired architecture for the deployment types. All of the XML will be updated with this value.
32 |     'x86', 'x64'
33 | .PARAMETER VisioLicense
34 |     Allows you to select the license type which you are licensed for. This will be either 'Online' or 'Volume'
35 |     Note that if 'Volume' is selected, you will see that Visio 2016 as well as 2019 deployment types are created, and have requirements
36 |     for Windows 7 or 8/8.1 attached to them. Visio 2019 deployment types are targeted at Windows 10.
37 | .PARAMETER ProjectLicense
38 |     Allows you to select the license type which you are licensed for. This will be either 'Online' or 'Volume'
39 |     Note that if 'Volume' is selected, you will see that Project 2016 as well as 2019 deployment types are created, and have requirements
40 |     for Windows 7 or 8/8.1 attached to them. Project 2019 deployment types are targeted at Windows 10.
41 | .PARAMETER UpdateChannel
42 |     Provides the desired Update Channel for the deployment types. All of the XML will be updated with this value.
43 |     'Semi-Annual', 'Semi-AnnualTargeted', 'Monthly', 'MonthlyTargeted'
44 | .PARAMETER AllowCdnFallback
45 |     A boolean value that will be set in the XML files. This will allow your clients to fallback to the Content Delivery Network (CDN)
46 |     aka 'the cloud.'
47 | .PARAMETER DisplayLevel
48 |     Provides the desired display level for the Office 365 installer. This can be either 'Full' or 'None. All of the XML will be updated with this value.
49 | 50 | $appName = 'Office 365 - Visio Volume and Project Volume'
51 | $New365DynamicAppSplat = @{
52 |     AppRoot = '\\contoso.com\DFS\CM\Applications\Office365\O365-DynamicInstall'
53 |     ProjectLicense = 'Volume'
54 |     ApplicationName = $AppName
55 |     SMSProvider = 'SCCM'
56 |     VisioLicense = 'Volume'
57 |     Company = 'Contoso'
58 |     Bitness = 'x64'
59 |     UpdateChannel = 'Semi-Annual'
60 | }
61 | .\New-365DynamicApp.ps1 @New365DynamicAppSplat
62 | 63 | This will use the info and XML to generate an application with 17 deployment types for you. It is every combination of O365, Visio/Project Professional/Standard 2016/2019 volume licensed. 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CM-Rambings 2 | Place to put things that might make sense and have some use for CM admins. 3 | -------------------------------------------------------------------------------- /RemoveExpiredUpdates.sql: -------------------------------------------------------------------------------- 1 | -- Delete expired updates that have beem marked as expired for more than @Days 2 | DECLARE @Days as int = 0 3 | exec spDeleteExpiredUpdates @Days 4 | -------------------------------------------------------------------------------- /Repair-CMClientSettings: -------------------------------------------------------------------------------- 1 | <# 2 | Rebuilds all CM Client settings with a ' - Rebuild' note at the end. 3 | This simply loops all settings, recreates them, copies the agent configurations and redeploys them. 4 | NOTE: THIS DELETES THE OLD ONES!!!!!!! 5 | #> 6 | $all = Get-CMClientSetting 7 | foreach ($clientSetting in $all) { 8 | if ($clientSetting.Type -ne 0) { 9 | $newClientSetting = New-CMClientSetting -Name "$($clientSetting.Name) - Rebuild" -Type $clientSetting.Type 10 | $oldAgentConfigurations = $clientSetting.AgentConfigurations 11 | $oldDeployments = Get-CMClientSettingDeployment -InputObject $clientSetting 12 | $newClientSetting.SetArrayItems('AgentConfigurations', $oldAgentConfigurations) 13 | $newClientSetting.Put() 14 | foreach ($deployment in $oldDeployments) { 15 | New-CMClientSettingDeployment -InputObject $newClientSetting -CollectionId $deployment.CollectionID 16 | Remove-CMClientSettingDeployment -InputObject $deployment -Force 17 | } 18 | Remove-CMClientSetting -InputObject $clientSetting -Force 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Reprovision Windows 10 Apps/Get-DeprovisionedAppX.ps1: -------------------------------------------------------------------------------- 1 | function Get-DeprovisionedAppX { 2 | <# 3 | .SYNOPSIS 4 | Returns an array of apps that are deprovisioned 5 | .DESCRIPTION 6 | This function returns an array of all the apps that are deprovisioned on the local computer. 7 | Deprovisioned apps will show up in the registry if they were removed while Windows was offline, or 8 | with the PowerShell cmdlets for removing AppX Packages. 9 | .PARAMETER Filter 10 | Option filter that will be ran through as a '-match' so that regex can be used 11 | Accepts an array of strings, which can be a regex string if you wish 12 | .EXAMPLE 13 | PS C:\> Get-DeprovisionedAppX 14 | Return all deprovisioned apps on the local computers 15 | .EXAMPLE 16 | PS C:\> Get-DeprovisionedAppX -Filter Store 17 | Return all deprovisioned apps on the local computers that match the filter 'Store' 18 | #> 19 | param ( 20 | [parameter(Mandatory = $false)] 21 | [string[]]$Filter 22 | ) 23 | begin { 24 | $DeprovisionRoot = "registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Appx\AppxAllUserStore\Deprovisioned" 25 | $AllDeprovisionedApps = Get-ChildItem -Path $DeprovisionRoot | Select-Object -Property @{ Name = 'DeprovisionedApp'; Expression = { $_.PSChildName } } 26 | if ($null -eq $AllDeprovisionedApps) { 27 | Write-Warning "There are no deprovisioned apps" 28 | } 29 | } 30 | process { 31 | switch ($PSBoundParameters.ContainsKey('Filter')) { 32 | $true { 33 | foreach ($SearchString in $Filter) { 34 | switch -regex ($AllDeprovisionedApps.DeprovisionedApp) { 35 | $SearchString { 36 | [PSCustomObject]@{ 37 | 'DeprovisionedApp' = $PSItem 38 | } 39 | } 40 | default { 41 | Write-Verbose "$PSItem does not match the filter `'$SearchString`"" 42 | } 43 | } 44 | } 45 | } 46 | $false { 47 | Write-Output $AllDeprovisionedApps 48 | } 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /Reprovision Windows 10 Apps/Inventory-DeprovisionedAppX.ps1: -------------------------------------------------------------------------------- 1 | #region define your Hardware Inventory Class Name, and the namespace in WMI to store it 2 | $HinvClassName = 'DeprovisionedAppX' 3 | $HinvNamespace = 'root\CustomHinv' 4 | #endregion define your Hardware Inventory Class Name, and the namespace in WMI to store it 5 | 6 | #region test if the namespace exists, and create it if it does not 7 | try { 8 | $null = [wmiclass]"$HinvNamespace`:__NameSpace" 9 | } 10 | catch { 11 | $HinvNamespaceSplit = $HinvNamespace -split '\\' 12 | $PathToTest = $HinvNamespaceSplit[0] 13 | for (${i} = 1; ${i} -lt $($HinvNamespaceSplit.Count); ${i}++) { 14 | $PathToTest = [string]::Join('\', @($PathToTest, $HinvNamespaceSplit[$i])) 15 | try { 16 | $null = [wmiclass]"$PathToTest`:__NameSpace" 17 | } 18 | catch { 19 | $PathToTestParent = Split-Path -Path $PathToTest -Parent 20 | $PathToTestName = Split-Path -Path $PathToTest -Leaf 21 | $NewNamespace = [wmiclass]"$PathToTestParent`:__NameSpace" 22 | $NamespaceCreation = $NewNamespace.CreateInstance() 23 | $NamespaceCreation.Name = $PathToTestName 24 | $null = $NamespaceCreation.Put() 25 | } 26 | } 27 | } 28 | #endregion test if the namespace exists, and create it if it does not 29 | 30 | #region clear our class from the namespace if it exists 31 | Remove-WmiObject -Class $HinvClassName -Namespace $HinvNamespace -ErrorAction SilentlyContinue 32 | #endregion clear our class from the namespace if it exists 33 | 34 | #region create the WMI class, and set the properties to be inventoried 35 | $HinvClass = [System.Management.ManagementClass]::new($HinvNamespace, [string]::Empty, $null) 36 | $HinvClass["__CLASS"] = $HinvClassName 37 | $HinvClass.Qualifiers.Add("Static", $true) 38 | $HinvClass.Properties.Add("DeprovisionedApp", [System.Management.CimType]::String, $false) 39 | $HinvClass.Properties["DeprovisionedApp"].Qualifiers.Add("Key", $true) 40 | $null = $HinvClass.Put() 41 | #endregion create the WMI class, and set the properties to be inventoried 42 | 43 | $DeprovisionRoot = "registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Appx\AppxAllUserStore\Deprovisioned" 44 | $AllDeprovisionedApps = Get-ChildItem -Path $DeprovisionRoot | Select-Object -ExpandProperty PSChildName 45 | if ($null -ne $AllDeprovisionedApps) { 46 | foreach ($App in $AllDeprovisionedApps) { 47 | $null = Set-WmiInstance -Namespace $HinvNamespace -Class $HinvClassName -ErrorAction SilentlyContinue -Arguments @{ 48 | DeprovisionedApp = $App 49 | } 50 | } 51 | } 52 | 53 | Write-Output 'Inventory Complete' -------------------------------------------------------------------------------- /Reprovision Windows 10 Apps/Reprovision-AppX.ps1: -------------------------------------------------------------------------------- 1 | function Reprovision-AppX { 2 | <# 3 | .SYNOPSIS 4 | 'Reprovision' apps by removing the registry key that prevents app reinstall 5 | .DESCRIPTION 6 | Starting in Windows 10 1803, a registry key is set for every deprovisioned app. As long as this registry key 7 | is in place, a deprovisioned application will not be reinstalled during a feature update. By removing these 8 | registry keys, we can ensure that deprovisioned apps, such as the windows store are able to be reinstalled. 9 | .PARAMETER DeprovisionedApp 10 | The full name of the app to reprovision, as it appears in the registry. You can easily get this name using 11 | the Get-DeprovisionedApp function. 12 | .EXAMPLE 13 | PS C:\> Reprovision-AppX -DeprovisionedApp 'Microsoft.WindowsAlarms_8wekyb3d8bbwe' 14 | Removes the registry key for the deprovisioned WindowsAlarms app. The app will return after the next 15 | feature update. 16 | .INPUTS 17 | [string[]] 18 | .NOTES 19 | You must provide the exact name of the app as it appears in the registry. This is the full app 'name' - It is 20 | recommended to first use the Get-DeprovisionApp function to find apps that can be reprovisioned. 21 | #> 22 | [CmdletBinding(SupportsShouldProcess)] 23 | param( 24 | [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] 25 | [string[]]$DeprovisionedApp 26 | ) 27 | begin { 28 | $DeprovisionRoot = "registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Appx\AppxAllUserStore\Deprovisioned" 29 | $AllDeprovisionedApps = Get-ChildItem -Path $DeprovisionRoot 30 | if ($null -eq $AllDeprovisionedApps) { 31 | Write-Warning "There are no deprovisioned apps" 32 | } 33 | } 34 | process { 35 | foreach ($App in $DeprovisionedApp) { 36 | $DeprovisionedAppPath = Join-Path -Path $DeprovisionRoot -ChildPath $App 37 | if ($PSCmdlet.ShouldProcess($App, "Reprovision-App")) { 38 | $AppPath = Resolve-Path -Path $DeprovisionedAppPath -ErrorAction Ignore 39 | if ($null -ne $AppPath) { 40 | Remove-Item -Path $AppPath.Path -Force 41 | } 42 | else { 43 | Write-Warning "App $App was not found to be deprovisioned" 44 | } 45 | } 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /Script-InvokeBaseline.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | [string]$BLName 3 | ) 4 | 5 | $BLQuery = [string]::Format("SELECT * FROM SMS_DesiredConfiguration WHERE DisplayName = '{0}'", $BLName) 6 | 7 | 8 | $getBaselineSplat = @{ 9 | Namespace = 'root\ccm\dcm' 10 | ErrorAction = 'Stop' 11 | Query = $BLQuery 12 | } 13 | 14 | $invokeBaselineEvalSplat = @{ 15 | Namespace = 'root\ccm\dcm' 16 | ClassName = 'SMS_DesiredConfiguration' 17 | ErrorAction = 'Stop' 18 | Name = 'TriggerEvaluation' 19 | } 20 | 21 | $PropertyOptions = 'IsEnforced', 'IsMachineTarget', 'Name', 'PolicyType', 'Version' 22 | 23 | $BL = Get-CimInstance @getBaselineSplat 24 | 25 | 26 | $ArgumentList = @{ } 27 | foreach ($Property in $PropertyOptions) { 28 | $PropExist = Get-Member -InputObject $BL -MemberType Properties -Name $Property -ErrorAction SilentlyContinue 29 | switch ($PropExist) { 30 | $null { 31 | continue 32 | } 33 | default { 34 | $TypeString = ($PropExist.Definition.Split(' '))[0] 35 | $Type = [scriptblock]::Create("[$TypeString]") 36 | $ArgumentList[$Property] = $BL.$Property -as (. $Type) 37 | } 38 | } 39 | } 40 | $invokeBaselineEvalSplat['Arguments'] = $ArgumentList 41 | 42 | Invoke-CimMethod @invokeBaselineEvalSplat -------------------------------------------------------------------------------- /Set-CMDistributionPointMaintenanceMode.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Allows you to toggle maintenance mode on a distribution point 4 | .DESCRIPTION 5 | This function allows you to select a distribution point and set whether the 'Maintenance Mode' feature introduced in ConfigMgr 1902 6 | is toggled on or off. 7 | .PARAMETER SMSProvider 8 | Define the SMS Provider which the WMI queries will execute against. 9 | .PARAMETER DistributionPoint 10 | Provides the Distribution Point which you want to change the maintenance mode of. This should be provided as a FQDN, 11 | but if you provide the shortname we will also attempt a search with a wildcard. 12 | .PARAMETER MaintenanceMode 13 | The desired state of of Maintenance Mode for the distribution point. This is either 'On' or 'Off' 14 | .PARAMETER Force 15 | Bypass confirmation prompts. 16 | .EXAMPLE 17 | C:\PS> Set-CMDistributionPointMaintenanceMode -SMSProvider SCCM.CONTOSO.COM -DistributionPoint DP.CONTOSO.COM -MaintenanceMode On 18 | .NOTES 19 | FileName: Set-CMDistributionPointMaintenanceMode.ps1 20 | Author: Cody Mathis 21 | Contact: @CodyMathis123 22 | Created: 2019-06-11 23 | Updated: 2019-06-11 24 | 25 | The account you run this as must have the proper permissions to perform the maintenance mode action 26 | #> 27 | function Set-CMDistributionPointMaintenanceMode { 28 | [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] 29 | param( 30 | [parameter(Mandatory = $true)] 31 | [string]$SMSProvider, 32 | [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] 33 | [Alias('ComputerName', 'Name', 'NetworkOSPath')] 34 | [string]$DistributionPoint, 35 | [parameter(Mandatory = $true)] 36 | [ValidateSet('On', 'Off')] 37 | [string]$MaintenanceMode, 38 | [parameter(Mandatory = $false)] 39 | [switch]$Force 40 | ) 41 | begin { 42 | $SiteCode = $(((Get-CimInstance -Namespace "root\sms" -ClassName "__Namespace" -ComputerName $SMSProvider).Name).substring(8 - 3)) 43 | $Namespace = [string]::Format('root\sms\site_{0}', $SiteCode) 44 | $null = $PSBoundParameters.Remove('Force') 45 | $PSBoundParameters.Confirm = $false 46 | } 47 | process { 48 | foreach ($Computer in $DistributionPoint) { 49 | try { 50 | #We remove \ if we find them, such as when NetworkOSPath is pipe from Get-CMDistributionPoint 51 | $Computer = $Computer -replace "\\" 52 | 53 | #region Query SMS Provider for DP instance 54 | $Filter = [string]::Format("Name = '{0}'", $Computer) 55 | $getCimInstanceSplat = @{ 56 | Filter = $Filter 57 | ComputerName = $SMSProvider 58 | Namespace = $Namespace 59 | ClassName = 'SMS_DistributionPointInfo' 60 | } 61 | $DP = Get-CimInstance @getCimInstanceSplat 62 | if ($null -eq $DP) { 63 | $Filter = [string]::Format("Name LIKE '{0}%'", $Computer) 64 | Write-Warning "Falling back to a wildcard filter [Filter = `"$Filter`"]" 65 | $getCimInstanceSplat['Filter'] = $Filter 66 | $DP = Get-WmiObject @getCimInstanceSplat 67 | if ($null -eq $DP) { 68 | Write-Error "WMI query for a distribution point succeded, but no object was returned. [Filter = `"$Filter`"] against [SMSProvider=$SMSProvider]" -ErrorAction Stop 69 | } 70 | } 71 | Write-Verbose "Identified Distribution point with [NALPath=$($DP.NALPath)]" 72 | #endregion Query SMS Provider for DP instance 73 | } 74 | catch { 75 | Write-Error "Failed to query for a distribution point with [Filter = `"$Filter`"] against [SMSProvider=$SMSProvider]" -ErrorAction Stop 76 | } 77 | 78 | #region convert from On or Off to a simple binary number to pass to the method 79 | $Mode = switch ($MaintenanceMode) { 80 | 'On' { 81 | 1 82 | } 83 | 'Off' { 84 | 0 85 | } 86 | } 87 | #endregion convert from On or Off to a simple binary number to pass to the method 88 | 89 | try { 90 | #region Invoke maintenance mode according to input parameters, note that Mode is cast as [uint32] 91 | $invokeCimMethodSplat = @{ 92 | ClassName = 'SMS_DistributionPointInfo' 93 | ComputerName = $SMSProvider 94 | Namespace = $Namespace 95 | MethodName = 'SetDPMaintenanceMode' 96 | Arguments = @{ 97 | NALPath = $DP.NALPath 98 | Mode = [uint32]$Mode 99 | } 100 | } 101 | if ($Force -or $PSCmdlet.ShouldProcess($Computer, "MaintenanceMode $MaintenanceMode")) { 102 | $Return = Invoke-CimMethod @invokeCimMethodSplat 103 | if ($Return.ReturnValue -ne 0) { 104 | Write-Error "Failed to set [DistributionPoint=$Computer] [MaintenanceMode=$MaintenanceMode] against [SMSProvider=$SMSProvider]" -ErrorAction Stop 105 | } 106 | elseif ($Return.ReturnValue -eq 0) { 107 | Write-Verbose "Set [DistributionPoint=$Computer] [MaintenanceMode=$MaintenanceMode]" 108 | } 109 | } 110 | #endregion Invoke maintenance mode according to input parameters, note that Mode is cast as [uint32] 111 | } 112 | catch { 113 | Write-Error "Failed to invoke maintenance mode change for [DistributionPoint=$Computer] [MaintenanceMode=$MaintenanceMode] against [SMSProvider=$SMSProvider]" -ErrorAction Stop 114 | } 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /Set-WSUSContentPath.ps1: -------------------------------------------------------------------------------- 1 | $Remediate = $false 2 | 3 | $PathShouldBeROOT = Get-ItemProperty -Path "registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Update Services\Server\Setup" -Name ContentDir | Select-Object -ExpandProperty ContentDir 4 | $PathShouldBe = Join-Path -Path $PathShouldBeROOT -ChildPath 'WSUSContent' 5 | 6 | [Void][Reflection.Assembly]::LoadWithPartialName("Microsoft.Web.Administration") 7 | $serverManager = New-Object Microsoft.Web.Administration.ServerManager 8 | $site = $serverManager.Sites | Where-Object { $_.Name -eq "WSUS Administration" } 9 | $rootApp = $site.Applications | Where-Object { $_.Path -eq "/" } 10 | $rootVdir = $rootApp.VirtualDirectories | Where-Object { $_.Path -eq "/Content" } 11 | $CurrentPath = $rootVdir.PhysicalPath 12 | 13 | switch ($CurrentPath -eq $PathShouldBe) { 14 | $true { 15 | $true 16 | } 17 | $false { 18 | switch ($Remediate) { 19 | $true { 20 | $rootVdir.PhysicalPath = $PathShouldBe 21 | $null = $serverManager.CommitChanges() 22 | $true 23 | } 24 | $false { 25 | $false 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Set-WSUSContentPoolAuth.ps1: -------------------------------------------------------------------------------- 1 | $Remediate = $false 2 | Import-Module WebAdministration 3 | $UseAppPoolIdentity = (Get-WebConfigurationProperty -Filter 'system.WebServer/security/authentication/AnonymousAuthentication' -Name username -Location 'WSUS Administration/Content') -eq '' 4 | switch ($UseAppPoolIdentity) { 5 | $true { 6 | $true 7 | } 8 | $false { 9 | switch ($Remediate) { 10 | $true { 11 | Set-WebConfigurationProperty -Filter 'system.WebServer/security/authentication/AnonymousAuthentication' -name username -value '' -location 'WSUS Administration/Content' 12 | $true 13 | } 14 | $false { 15 | $false 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Start-IISLogCleanup.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | IIS Log Cleanup 4 | .DESCRIPTION 5 | This script will cleanup all IIS logs according to the LogCleanupDays parameter, and the Remediate parameter. 6 | This was written for use in a MEMCM Configuration Item. You can paste this script into both detection, and 7 | remediation, adjusting the $Remediate paramater appropraitely. Ensure that the $LogCleanupDays parameter 8 | matches in both scripts. 9 | .PARAMETER Remediate 10 | A boolean that determines if the logs will be cleaned up, or if we simply return compliance 11 | .PARAMETER LogCleanupDays 12 | A positive integer value of days which you want to retain logs for, defaulting to 7. Anything older than the 13 | specified number of days will be removed, or used to return compliance if remediation is set to false. 14 | .EXAMPLE 15 | C:\PS> Start-IISLogCleanup -Remediate $False -LogCleanupDays 7 16 | Return a boolean based on whether there are log files older than 7 days 17 | .EXAMPLE 18 | C:\PS> Start-IISLogCleanup -Remediate $False -LogCleanupDays 7 19 | Remove files older than 7 days, and return a boolean of $true if it was succesful 20 | .OUTPUTS 21 | [bool] 22 | .NOTES 23 | This is just the function declaration. You should call the function, as you see at the end of the file 24 | based on the part of your CI this script is in. You would call it with -Remediate $false for detection 25 | and then call it with -Remediate $true for remediation. 26 | 27 | FileName: Start-IISLogCleanup.ps1 28 | Author: Cody Mathis 29 | Contact: @CodyMathis123 30 | Contributor: Vex 31 | Created: 2020-04-09 32 | Version: 1.0.2 33 | Updated: 34 | Version 1.0.0 2020-04-09 - Initial Release 35 | Version 1.0.1 2020-04-09 - Cleaned up 36 | Version 1.0.2 2020-04-09 - Added detection for available module; added function 37 | 38 | #> 39 | Function Start-IISLogCleanup { 40 | param( 41 | [Parameter(Mandatory = $false)] 42 | [bool]$Remediate = $false, 43 | [Parameter(Mandatory = $false)] 44 | [ValidateRange(1, [int]::MaxValue)] 45 | [int]$LogCleanupDays = 7 46 | ) 47 | 48 | If (Get-Module -ListAvailable | Where-Object { $_.Name -eq 'WebAdministration' } ) { 49 | Import-Module WebAdministration 50 | } 51 | Else { 52 | Write-Verbose "No WebAdministration. Compliant." 53 | return $true 54 | } 55 | 56 | $AllWebsites = Get-Website 57 | 58 | #region Loop through all websites, identify log file path, and check for old files. Removing according to remediation preference 59 | foreach ($WebSite in $AllWebsites) { 60 | $LogFilePath = [string]::Format("{0}\w3svc{1}", $WebSite.LogFile.Directory, $WebSite.ID).Replace('%SystemDrive%', $env:SystemDrive) 61 | if (Test-Path -Path $LogFilePath) { 62 | $AllLogFiles = Get-ChildItem -Path $LogFilePath -Filter "*.log" -Recurse 63 | if ($OldLogs = $AllLogFiles.Where( { $_.LastWriteTime -lt (Get-Date).AddDays(-$LogCleanupDays) })) { 64 | switch ($Remediate) { 65 | $true { 66 | try { 67 | $OldLogs | Remove-Item -Force -ErrorAction Stop 68 | } 69 | catch { 70 | return $false 71 | } 72 | } 73 | $false { 74 | return $false 75 | } 76 | } 77 | } 78 | } 79 | } 80 | #endregion Loop through all websites, identify log file path, and check for old files. Removing according to remediation preference 81 | 82 | #region If we make it through the loop with no $false returns, then we are compliant. Return $True 83 | Write-Verbose "Compliant." 84 | return $true 85 | #endregion If we make it through the loop with no $false returns, then we are compliant. Return $True 86 | } 87 | 88 | Start-IISLogCleanup -Remediate $false -LogCleanupDays 30 89 | -------------------------------------------------------------------------------- /prompt.ps1: -------------------------------------------------------------------------------- 1 | function global:Prompt { 2 | $Success = $? 3 | 4 | ## Time calculation 5 | $LastExecutionTimeSpan = if (@(Get-History).Count -gt 0) { 6 | Get-History | Select-Object -Last 1 | ForEach-Object { 7 | New-TimeSpan -Start $_.StartExecutionTime -End $_.EndExecutionTime 8 | } 9 | } 10 | else { 11 | New-TimeSpan 12 | } 13 | 14 | $LastExecutionShortTime = if ($LastExecutionTimeSpan.Days -gt 0) { 15 | "$($LastExecutionTimeSpan.Days + [Math]::Round($LastExecutionTimeSpan.Hours / 24, 2)) d" 16 | } 17 | elseif ($LastExecutionTimeSpan.Hours -gt 0) { 18 | "$($LastExecutionTimeSpan.Hours + [Math]::Round($LastExecutionTimeSpan.Minutes / 60, 2)) h" 19 | } 20 | elseif ($LastExecutionTimeSpan.Minutes -gt 0) { 21 | "$($LastExecutionTimeSpan.Minutes + [Math]::Round($LastExecutionTimeSpan.Seconds / 60, 2)) m" 22 | } 23 | elseif ($LastExecutionTimeSpan.Seconds -gt 0) { 24 | "$($LastExecutionTimeSpan.Seconds + [Math]::Round($LastExecutionTimeSpan.Milliseconds / 1000, 2)) s" 25 | } 26 | elseif ($LastExecutionTimeSpan.Milliseconds -gt 0) { 27 | "$([Math]::Round($LastExecutionTimeSpan.TotalMilliseconds, 2)) ms" 28 | } 29 | else { 30 | "0 s" 31 | } 32 | 33 | if ($Success) { 34 | Write-Host -Object "[$LastExecutionShortTime] " -NoNewline -BackgroundColor DarkGreen -ForegroundColor White 35 | } 36 | else { 37 | Write-Host -Object "! [$LastExecutionShortTime] " -NoNewline -BackgroundColor Red -ForegroundColor White 38 | } 39 | 40 | ## History ID 41 | $HistoryId = $MyInvocation.HistoryId - 1 42 | # Uncomment below for leading zeros 43 | # $HistoryId = '{0:d4}' -f $MyInvocation.HistoryId 44 | Write-Host -Object "$HistoryId`: " -NoNewline -BackgroundColor DarkCyan -ForegroundColor White 45 | 46 | ## User 47 | $IsAdmin = (New-Object Security.Principal.WindowsPrincipal ([Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator) 48 | Write-Host -Object "$($env:USERNAME) ($(if ($IsAdmin){ 'A' } else { 'U' })) " -NoNewline -BackgroundColor Red -ForegroundColor White 49 | 50 | ## Path FF 51 | $Drive = $pwd.Drive.Name 52 | $Pwds = $pwd -split "\\" | Where-Object { -Not [String]::IsNullOrEmpty($_) } 53 | $PwdPath = if ($Pwds.Count -gt 3) { 54 | $ParentFolder = Split-Path -Path (Split-Path -Path $pwd -Parent) -Leaf 55 | $CurrentFolder = Split-Path -Path $pwd -Leaf 56 | "..\$ParentFolder\$CurrentFolder" 57 | } 58 | elseif ($Pwds.Count -eq 3) { 59 | $ParentFolder = Split-Path -Path (Split-Path -Path $pwd -Parent) -Leaf 60 | $CurrentFolder = Split-Path -Path $pwd -Leaf 61 | "$ParentFolder\$CurrentFolder" 62 | } 63 | elseif ($Pwds.Count -eq 2) { 64 | Split-Path -Path $pwd -Leaf 65 | } 66 | else { 67 | "" 68 | } 69 | 70 | Write-Host -Object "$Drive`:\$PwdPath" -NoNewline -BackgroundColor Magenta -ForegroundColor White 71 | 72 | return ">`n" 73 | } --------------------------------------------------------------------------------