├── CVE-2023-36884-Check&Mitigation.ps1
├── DHCP Report
├── DHCPReport-du-09-10-2020.html
└── DHCP_Report-v1.1.ps1
├── Differents Ways to Display Information to user
├── Images
│ ├── BurntToast-Notification-Schedule.png
│ ├── BurntToast-Notification.png
│ ├── PowerBGInfo.png
│ ├── WTF-Window.png
│ └── WindowsNotification.png
└── Projet Display Information to User - how to do this.md
├── End Of Life
├── EOL-Report-at-07-11-2024.html
└── Get-EOL-V3.ps1
├── Export-EventLog.ps1
├── Function - Get AD OrganizationalUnit Permissions.ps1
├── Function ConvertHashtableTo-Object.ps1
├── GPO-Report
├── Exemple GPOReport-du-20_01_2021.html
├── GPOReport-du-12_10_2023.html
├── GPOReport-du-26_03_2024.html
├── GPOReport-v1.2.ps1
├── GPOReport-v1.3.ps1
└── GPOReport-v1.4.ps1
├── Get-PublicHoliday.ps1
├── HTML Certificates Report
├── CertReport.html
└── HtmlCertReport.ps1
├── Images
├── DialogInput.jpg
└── EventsAsHTMLReport.jpg
├── Microsoft.PowerShellISE_profile.ps1
├── Plongée dans Foreach -Parallel.md
├── Printers Report
├── PrinterReport-du-27-07-2020.html
└── Printer_Report-v1.1.ps1
├── README.md
├── Remove Old Profiles.ps1
├── Script - Bulk reboot of Windows Computers.ps1
├── Script - Get Organizational Unit Acls.ps1
├── Script - Get WIndows 10-11 version.ps1
├── Script- Troubleshoot GroupPolicy on a Workstation.ps1
├── TIP - Différentes manières d'exécuter un script avec une entrée utisateur.md
├── TIP - Extract medata from MP3 files and export in a file
├── Get-Mp3FilesMetadatas.ps1
└── TIP - Extract medata from MP3 files and export in a file.md
├── TIP - How to set up a time counter for your scripts.md
├── TIP - a sample script to configure DNS on servers in an AD domain.md
├── TIP - performance test package.ps1
├── TIP How to get the installed Font Families.md
├── TIPS- How to do something using a NT AUTHORITY Account.md
├── Tip - A elegant way to convert a String to a [TimeSpan].md
├── Tip - Play with Month and Day names depending of Culture.md
├── Tip - A small fonction to create a Calendar DialogBox in script.md
├── Tip - Convert a Image File to a Base64 string and reverse.md
├── Tip - Copy-Item (then Remove-Item) or Move-Item.md
├── Tip - Differents ways to create one or more dummy files.md
├── Tip - Differents ways to get the AD groups a user is member of.md
├── Tip - How to check the Active Directory related ports with Powershell.md
├── Tip - How to find and eliminate performance bottlenecks with PowerShell.md
├── Tip - How to get a Report about the Printed Documents.md
├── Tip - How to get the Process Owner from a TCP Connection established.md
├── Tip - How to know if the current user is on the the Admin role or not.md
├── Tip - New feeds and notify.ps1
├── Tip - Quickly Open an .exe file maximized windows.md
├── Tip - Quickly set up Windows logs.md
├── Tips - How to create an open file and folder dialog box with PowerShell.md
├── Tips - 4 ways to format numeric output with 2 decimals.md
├── Tips - Differents Ways to view loggued-on Users.md
├── Tips - Some useful accelerators, .NET Class and their methods in daily powershell Task.md
├── Tips - Some words about Compare-Object.md
├── Tips - 2 ways to rename file in mass.ps1
├── Tips - 2 ways to split a root UNC Path and get the Parent and the children without any caracter.md
├── Tips - 3 methodes pour créer des entres dans le registre.ps1
├── Tips - 3 ways to proceed a domain-wide update of Group Policy with Powershell.md
├── Tips - 3 ways to unzip compressed Files.ps1
├── Tips - 4 ways to do the samething (sending mail).md
├── Tips - About [DateTime].md
├── Tips - Appropriate way to have an output.md
├── Tips - Beginning parallel processing in PowerShell.ps1
├── Tips - Best ways to disable IPV6 in Windows Computers.md
├── Tips - Build a custom error info function.md
├── Tips - Build shortcuts with powershell.md
├── Tips - Check of DNS for specific old records.ps1
├── Tips - CmdLets Useful for network.ps1
├── Tips - Comment s'assurer qu'un booléen ne retourne que True ou False.ps1
├── Tips - Convert a string from a .csv file to another format.md
├── Tips - Count number of the week in a month based on each week start on a specific day.md
├── Tips - Create a .csv file from a PSCustomObject with Multivalues parameters.ps1
├── Tips - Create a Scheduled Task.md
├── Tips - Create a bunch of dummy files.md
├── Tips - Create registry values.ps1
├── Tips - DeleteFilesOlderThanXDays.ps1
├── Tips - Examples of ValidateScript.md
├── Tips - Explanations about different outputs.md
├── Tips - Find Duplicate files and delete.md
├── Tips - Find the total number of servers in each Organizational Unit.ps1
├── Tips - Get a list of the available special folders with the [Enum] Accelerator.md
├── Tips - Get the week of the month or the quarter from a Date.md
├── Tips - Grab Value from Text File.md
├── Tips - HardDiskUsage.ps1
├── Tips - Here-String use to send automatic and personalized mails.ps1
├── Tips - How to Rename a bunch of files.md
├── Tips - How to Send a Beautiful Email Report (html) with result of differents queries.md
├── Tips - How to Set Up Powershell to run in a secure Environment.ps1
├── Tips - How to Update-Help in a secure Environment.ps1
├── Tips - How to check if there are new or modified files in a path with a lot of files.md
├── Tips - How to check, securing or disabling Remote Desktop on remote computers.md
├── Tips - How to create Virtual Machines from a spreadsheet and a Posh Script.md
├── Tips - How to cut a big file (.txt or .csv) to many small files of defined size.ps1
├── Tips - How to display a pop-up message box windows.md
├── Tips - How to get your public address.md
├── Tips - How to install software from the Internet.md
├── Tips - How to launch a script as RunAsAdmin.md
├── Tips - How to make a Menu in a script.ps1
├── Tips - How to monitor files on a folder and do something when something appears.md
├── Tips - How to pass Robocopy parameters in a PS script.ps1
├── Tips - How to pass credentials as a parameter in a script.md
├── Tips - How to resolve PS Error Message Warning - unable to resolve package source.md
├── Tips - How to sort files in a directory basing on a réference .csv file.md
├── Tips - How to sort properties diffently for each parameter.ps1
├── Tips - How to use $MyInvocation var in a script.md
├── Tips - How to use get-WinEvent efficiently.md
├── Tips - How to work with IP addresses (IPv4).md
├── Tips - Identify Apps Packages and remove them.ps1
├── Tips - Increase Privacy By disabling Telemetry for Windows 10.ps1
├── Tips - Install-PowerShellCorePortable.ps1
├── Tips - Looking for the first Windows 10 installation.md
├── Tips - Most Critical Windows Security Events IDs to monitor.md
├── Tips - Obtenir les privilèges de l'utilisateur courant.md
├── Tips - Optimize comparizons with Compare-Object.md
├── Tips - Optional Arguments in a Scheduled Task.ps1
├── Tips - Ping and resolve DNSName at the same time.ps1
├── Tips - Play With PSReadLineOption.md
├── Tips - Play with SID.md
├── Tips - Play with dates.md
├── Tips - Powershell Remoting.ps1
├── Tips - Powershell copy to clipboard.ps1
├── Tips - Quickly searching files in IsReadOnly mode and uncheck them.ps1
├── Tips - Remember your work and documenting your script with the easy way using Start-Transcript.md
├── Tips - Resolving Get-NetTCPConnection owning service.md
├── Tips - Récupérer Logon-Logoff des utilisateurs & Machine.md
├── Tips - Sample Self-Auto elevated script.pS1
├── Tips - Separate different data in block of data.md
├── Tips - Setup WSManagement.md
├── Tips - Some Useful informations to put in a Powershell Profile.ps1
├── Tips - Some different ways to display script execution time with a beautiful display.md
├── Tips - Some ways to check if a .ps1 file is running in RunAsAdmin Mode.md
├── Tips - Sort PS results in both ascending and descending order.ps1
├── Tips - Speed up your Powershell Scripts.md
├── Tips - Split a DistinguishedName.ps1
├── Tips - Stratégie de Mot de Passe Fine.md
├── Tips - Technics to pass a password securely.md
├── Tips - Track changes to Active Directory group membership.ps1
├── Tips - Use a Posh script using PSWindowsUpdate module to update computers.md
├── Tips - Use-RunAsAdministrator.ps1
├── Tips - Usefull Well-known Ports.md
├── Tips - Using différent menus in script.md
├── Tips - Utiliser le Module EZLog efficacement.ps1
├── Tips - Working with collections-System.collections.stack.ps1
├── Tips - [System.Convert] et [System.Text.Encoding].ps1
├── Tips - how a better presentation can make it easier to read and debug a code.md
├── Tips - how to look for all installed software or a specific one.ps1
├── Tips - how to move a bunch of files based on the name.md
├── Tips - how to use Invoke-Expression cmdlet.ps1
├── Tips - infinite ping in powershell.md
├── Tips -Contruire un script pas à pas.md
├── Tips -Detecting applications, services and systems using LDAP instead of LDAPS.md
├── Tips -Get all of the administrators for a collection of remote computers.md
├── Tips -How to play with quser with powershel.md
├── Tips -How to replace a line break char with a space char.md
├── Tips -recreate-Shares-US.ps1
├── Tips- Restrieve Last Day of Month and First Day Next Month.md
├── Tips- Useful Events to Monitor for Security Reasons.ps1
├── Tips- how to work with network settings.md
├── Tips-Complete the file name on condition.md
├── Tips-GPOReport.ps1
├── Tips-Manipulate a .csv for more information.md
├── TroubleShoot Replication AD
└── TroubleShoot AD Replication.ps1
├── `Tips - Tests de performances pour la lecture d'un gros fichier.md
└── function Test-ServerRolePortGroup.ps1
/Differents Ways to Display Information to user/Images/BurntToast-Notification-Schedule.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusefulrepo/Tips/8da8779d41efb663ecb1ef39734a57eea1afe699/Differents Ways to Display Information to user/Images/BurntToast-Notification-Schedule.png
--------------------------------------------------------------------------------
/Differents Ways to Display Information to user/Images/BurntToast-Notification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusefulrepo/Tips/8da8779d41efb663ecb1ef39734a57eea1afe699/Differents Ways to Display Information to user/Images/BurntToast-Notification.png
--------------------------------------------------------------------------------
/Differents Ways to Display Information to user/Images/PowerBGInfo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusefulrepo/Tips/8da8779d41efb663ecb1ef39734a57eea1afe699/Differents Ways to Display Information to user/Images/PowerBGInfo.png
--------------------------------------------------------------------------------
/Differents Ways to Display Information to user/Images/WTF-Window.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusefulrepo/Tips/8da8779d41efb663ecb1ef39734a57eea1afe699/Differents Ways to Display Information to user/Images/WTF-Window.png
--------------------------------------------------------------------------------
/Differents Ways to Display Information to user/Images/WindowsNotification.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusefulrepo/Tips/8da8779d41efb663ecb1ef39734a57eea1afe699/Differents Ways to Display Information to user/Images/WindowsNotification.png
--------------------------------------------------------------------------------
/Export-EventLog.ps1:
--------------------------------------------------------------------------------
1 | function Export-EventLog
2 | {
3 | <#
4 | .SYNOPSIS
5 | Export an Event Log file
6 |
7 | .DESCRIPTION
8 | Export an Event Log file
9 |
10 | .PARAMETER LogFileName
11 | Name of the Log File to export
12 | eg. : Application, System, ...
13 | The Name must be already exists
14 |
15 | .PARAMETER Path
16 | Path of the folder to Export the log File.
17 | The Path must be already existing and be is a folder path.
18 |
19 | .EXAMPLE
20 | Export-EventLog -LogFileName 'Application' -Path "C:\temp"
21 | Export the Event Log FIle to the Path passed in parameter
22 |
23 | .EXAMPLE
24 | Get-Help Export-EventLog -ShowWindow
25 | Complet help about this function in a separate window
26 |
27 | .NOTES
28 | Author : O. FERRIERE (inspired by Jeffery Hick (https://4sysops.com/archives/managing-the-event-log-with-powershell-part-2-backup/) and Luke Murray (https://github.com/lukemurraynz/PowerOfTheShell/blob/master/Other/Export_EventLogs.ps1)
29 | Date : 30/10/2023
30 | Version : 1.0
31 | Change : V1.0 - 30/10/2023 - Initial Version
32 | #>
33 |
34 | [CmdletBinding()]
35 | [Alias()]
36 | [OutputType([int])]
37 | Param
38 | (
39 | # Event Log Name
40 | [Parameter(Mandatory = $true,
41 | ValueFromPipelineByPropertyName = $true,
42 | Position = 0)]
43 | [ValidateScript({
44 | if ([System.Diagnostics.EventLog]::SourceExists($_))
45 | {
46 | $true
47 | }
48 | else
49 | {
50 | throw "Le nom de l'EventLog '$_' n'existe pas."
51 | }
52 | })]
53 | [string]$LogFileName,
54 |
55 | # Folder Path to Export the Event Log File
56 | [Parameter(Mandatory = $true,
57 | ValueFromPipelineByPropertyName = $true,
58 | Position = 1)]
59 | [ValidateScript({
60 | if (-Not ($_ | Test-Path) )
61 | {
62 | throw "The path doesn't exist"
63 | }
64 | if (-Not ($_ | Test-Path -PathType Container ) )
65 | {
66 | throw 'The Path argument must be a folder. File paths are not allowed.'
67 | }
68 | return $true
69 | })]
70 | [System.IO.FileInfo]
71 | $Path
72 | )
73 |
74 | Begin
75 | {
76 | Write-Verbose -Message 'Determining the Name of the Exported Log File'
77 | $ExportFileName = $logFileName + '_' + (Get-Date -f yyyyMMdd) + '.evt'
78 | Write-Verbose -Message "The Name of the log File Exported will be : [$ExportFileName]"
79 | Write-Verbose -Message 'Parameters and values will be used'
80 | $PSBoundParameters
81 |
82 | }
83 | Process
84 | {
85 | Write-Verbose -Message "We'll use Get-WMIObject cause Get-CIMInstance hasn't the BackupEventlog Method"
86 | $logFile = Get-WmiObject -Class Win32_NTEventlogFile | Where-Object { $_.Logfilename -eq $logFileName }
87 | $FullNameExportPath = Join-Path -Path $Path -ChildPath $ExportFileName
88 | $logFile.BackupEventlog($FullNameExportPath)
89 | }
90 | End
91 | {
92 | Write-Output "The Export File is located here : [$FullNameExportPath]"
93 | }
94 | }
95 |
96 | # Usage example : Export-EventLog -LogFileName 'Application' -Path C:\temp -Verbose
97 |
--------------------------------------------------------------------------------
/Function ConvertHashtableTo-Object.ps1:
--------------------------------------------------------------------------------
1 | # source : https://gordon.byers.me/powershell/convert-a-powershell-hashtable-to-object/
2 | function ConvertHashtableTo-Object
3 | {
4 | [CmdletBinding()]
5 | Param([Parameter(Mandatory = $True, ValueFromPipeline = $True, ValueFromPipelinebyPropertyName = $True)]
6 | [hashtable]$ht
7 | )
8 | PROCESS
9 | {
10 | $results = @()
11 |
12 | $ht | ForEach-Object {
13 | $result = New-Object psobject
14 | foreach ($key in $_.keys)
15 | {
16 | $result | Add-Member -MemberType NoteProperty -Name $key -Value $_[$key]
17 | }
18 | $results += $result
19 | }
20 | return $results
21 | }
22 | }
--------------------------------------------------------------------------------
/Get-PublicHoliday.ps1:
--------------------------------------------------------------------------------
1 | function Get-PublicHoliday
2 | {
3 | <#
4 | .SYNOPSIS
5 | Get the Public Holiday dates for a country
6 |
7 | .DESCRIPTION
8 | Get the Public Holiday dates for a country
9 |
10 | .PARAMETER Date
11 | [DateTime]
12 | To avoid any trouble with an incorrect format of the Date if your current culture is not EN, use the Get-Date cmdlet, like this :
13 | $(Get-Date) or $(Get-Date -Year 2025)
14 |
15 | .PARAMETER CountryCode
16 | [String]
17 | Define your Country Code on 2 characters.
18 | ex. NZ, FR, ...
19 |
20 | .EXAMPLE
21 | Get-PublicHoliday -Date $(Get-Date) -CountryCode 'FR'
22 | return Holiday dates for this country
23 |
24 | .EXAMPLE
25 | Get-Help Get-PublicHoliday -ShowWindow
26 | Complete help about this fonction
27 |
28 | .NOTES
29 | Author : O. FERRIERE (inspired by Luke Murray Code : https://github.com/lukemurraynz/PowerOfTheShell/blob/master/Other/Get-PublicHoliday.ps1)
30 | Version 1.0
31 | Date 30/10/2023
32 | Change : v1.0 - 30/10/2023 - Initial Version
33 | #>
34 | [CmdletBinding()]
35 | [OutputType([PSObject])]
36 | Param
37 | (
38 | # Define a Date
39 | [Parameter(Mandatory=$true,
40 | ValueFromPipelineByPropertyName=$true,
41 | Position=0)]
42 | [DateTime]
43 | $Date,
44 |
45 | # define your country Code with 2 char
46 | [Parameter(Mandatory=$true,
47 | ValueFromPipelineByPropertyName=$true,
48 | Position=1)]
49 | [String]
50 | $CountryCode
51 | )
52 |
53 | Begin
54 | {
55 | Write-Verbose "Determining The Year of the date variable"
56 | $Year = $Date.Year
57 | }
58 | Process
59 | {
60 | Write-Verbose "Read the content from nager.date"
61 | $url = "https://date.nager.at/api/v2/publicholidays/$Year/$CountryCode"
62 | Write-Verbose "Fix TLS Protocol to avoid any problem"
63 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
64 | Write-Verbose "Querying for Holiday dates"
65 | $Holidays = Invoke-RestMethod -Method Get -UseBasicParsing -Uri $url
66 | }
67 | End
68 | {
69 | $Holidays
70 | }
71 | }
72 | $Holidays | Format-Table
--------------------------------------------------------------------------------
/Images/DialogInput.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusefulrepo/Tips/8da8779d41efb663ecb1ef39734a57eea1afe699/Images/DialogInput.jpg
--------------------------------------------------------------------------------
/Images/EventsAsHTMLReport.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusefulrepo/Tips/8da8779d41efb663ecb1ef39734a57eea1afe699/Images/EventsAsHTMLReport.jpg
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Tips
2 | some tips useful to keep in mind and perhaps make life easier
3 |
--------------------------------------------------------------------------------
/Remove Old Profiles.ps1:
--------------------------------------------------------------------------------
1 | function Remove-OldProfile
2 | {
3 | <#
4 | .SYNOPSIS
5 | Remove old profiles on remote computer
6 |
7 | .DESCRIPTION
8 | Remove old profile on remote computer
9 | All profiles reponding to the following conditions
10 | - Not loaded
11 | - And with a LocalPath
12 | - And Not special profil
13 | - And with LastUseTime later than the $MaxDay variable
14 | could be eligible for deletion
15 |
16 | .PARAMETER MaxDay
17 | Number of Day to keep a profile before deletion
18 | It's a positive number
19 |
20 | .PARAMETER ComputerName
21 | Name of the remote computer.
22 |
23 | .INPUTS
24 | None
25 |
26 | .OUTPUTS
27 | [SystemObject]
28 |
29 | .NOTES
30 | File Name : Remove-OldProfile.ps1
31 | Version : V.1.0
32 | Date : 08/01/2024
33 | Author : O. FERRIERE
34 | Change : V.1.0 - 08/01/2024 - initial version
35 |
36 | .EXAMPLE
37 | Remove-OldProfile -Computer 'ASUS11' -MaxDay 30 -WhatIf -Verbose
38 | Simulation mode only + verbose
39 |
40 | .EXAMPLE
41 | Remove-OldProfile -Computer 'ASUS11' -MaxDay 30 -WhatIf
42 | Simulation mode only
43 |
44 | .EXAMPLE
45 | Remove-OldProfile -Computer 'ASUS11' -MaxDay 30
46 | Action mode using the value passed for parameters
47 |
48 | .EXAMPLE
49 | Remove-OldProfile -Computer 'Asus11'
50 | Action mode using the default value of MaxDay parameter
51 |
52 | .EXAMPLE
53 | Get-Help Remove-OldProfile -ShowWindow
54 | Full help about the function in a separate windows
55 | #>
56 | [CmdletBinding(SupportsShouldProcess)]
57 | param (
58 | # Number of Day to keep a profile before deletion
59 | [Parameter()]
60 | [int]
61 | $MaxDay = '30',
62 |
63 | # Remote computer Name
64 | [Parameter()]
65 | [string]
66 | $Computer = $Env:COMPUTERNAME
67 | )
68 |
69 | begin
70 | {
71 | if ($PSBoundParameters.ContainsKey('verbose') )
72 | {
73 | Write-Verbose -Message 'PSBoundParameters and values'
74 | $PSBoundParameters
75 | }
76 | }
77 |
78 | process
79 | {
80 | Write-Verbose -Message "Gathering profiles Not loaded And with a LocalPaht And Not special And with LastUseTime later than $MaxDay days"
81 | $UnloadedProfiles = Get-CimInstance -ClassName Win32_UserProfile -ComputerName $Computer |
82 | Where-Object {(!$_.Special) -and (!$_.Loaded) -and ($null -ne $_.LocalPath) -and ($_.LastUseTime -lt (Get-Date).AddDays(-$MaxDay))}
83 |
84 | Write-Verbose -Message "Number of profiles count : $($UnloadedProfiles.count)"
85 | Write-Verbose -Message "List of unloaded profiles on computer : $($Unloadedprofiles.LocalPath -join ("`r`n")) "
86 | }
87 | end
88 | {
89 | if ($UnloadedProfiles)
90 | {
91 | Write-Verbose -Message "there are [$($UnloadedProfiles.count)] old profiles to remove"
92 | if ($PSBoundParameters.ContainsKey('Whatif') )
93 | {
94 | Write-Verbose -Message 'Enter in simulation mode only'
95 | $UnloadedProfiles | Remove-CimInstance -ComputerName $Computer -WhatIf
96 | }
97 | else
98 | {
99 | Write-Verbose -Message 'Enter in action mode'
100 | $UnloadedProfiles | Remove-CimInstance -ComputerName $Computer
101 | }
102 | }
103 | }
104 | }
105 |
106 |
--------------------------------------------------------------------------------
/Script - Get Organizational Unit Acls.ps1:
--------------------------------------------------------------------------------
1 | $OUs = Get-ADOrganizationalUnit -Filter * # Add the -SearchBase parameter to begin at a specific point
2 | $Result = [System.Collections.Generic.List[PSObject]]::new()
3 | ForEach ($OU In $OUs)
4 | {
5 | # Gathering OU 's Acls
6 | $Path = 'AD:\' + $OU.DistinguishedName
7 | $ACLs = (Get-Acl -Path $Path).Access
8 | ForEach ($ACL in $ACLs)
9 | {
10 | # Retrieve only inherited Acls
11 | If ($ACL.IsInherited -eq $False)
12 | {
13 | # Building a PSObject
14 | $Properties = [PSCustomObject][Ordered]@{
15 | OU = $OU.DistinguishedName
16 | IdentityReference = $ACL.IdentityReference
17 | ActiveDirectoryRights = $ACL.ActiveDirectoryRights
18 | InheritanceType = $ACL.InheritanceType
19 | IsInherited = $ACL.IsInherited
20 | AccessControlType = $ACL.AccessControlType
21 | InheritanceFlags = $ACL.InheritanceFlags
22 | PropagationFlags = $ALC.PropagationFlags
23 | }
24 | # Add the Psobject to $Result
25 | $Result.Add($Properties)
26 | }
27 | }
28 | }
29 |
30 | $Result | Out-GridView
31 | $Result | Export-Csv -Path ...
--------------------------------------------------------------------------------
/Script - Get WIndows 10-11 version.ps1:
--------------------------------------------------------------------------------
1 | $BuildNumber = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\').currentbuildnumber
2 |
3 | # Windows 10 release history. Source : https://docs.microsoft.com/en-us/windows/release-health/release-information
4 | switch ($BuildNumber)
5 | {
6 | 10240
7 | {
8 | Write-Host 'Windows 10 - 1507 - RTM - End of Servicing - Extended support 14/10/2025' -ForegroundColor Green
9 | }
10 | 10586
11 | {
12 | Write-Host 'Windows 10 - 1511 - End of Servicing' -ForegroundColor Green
13 | }
14 | 14393
15 | {
16 | Write-Host 'Windows 10 - 1607 - End of Servicing -Extended support 13/10/2026' -ForegroundColor Green
17 | }
18 | 15063
19 | {
20 | Write-Host 'Windows 10 - 1703 - End of servicing' -ForegroundColor Green
21 | }
22 | 16299
23 | {
24 | Write-Host 'Windows 10 - 1709 - End of Servicing' -ForegroundColor Green
25 | }
26 | 17134
27 | {
28 | Write-Host 'Windows 10 - 1803 - End of Servicing' -ForegroundColor Green
29 | }
30 | 17763
31 | {
32 | Write-Host 'Windows 10 - 1809 - End of Servicing - Extended support 09/01/2029' -ForegroundColor Green
33 | }
34 | 18362
35 | {
36 | Write-Host 'Windows 10 - 1903 - End of Servicing' -ForegroundColor Green
37 | }
38 | 18363
39 | {
40 | Write-Host 'Windows 10 - 1909' -ForegroundColor Green
41 | }
42 | 19041
43 | {
44 | Write-Host 'Windows 10 - 2004 - End of Servicing' -ForegroundColor Green
45 | }
46 | 19042
47 | {
48 | Write-Host 'Windows 10 - 20H2' -ForegroundColor Green
49 | }
50 | 19043
51 | {
52 | Write-Host 'Windows 10 - 21H1' -ForegroundColor Green
53 | }
54 | 19044
55 | {
56 | Write-Host 'Windows 10 - 21H2' -ForegroundColor Green
57 | }
58 | 19045
59 | {
60 | Write-Host 'Windows 10 - 22H2' -ForegroundColor Green
61 | }
62 | 22000
63 | {
64 | Write-Host 'Windows 11 - 21H2' -ForegroundColor Green
65 | }
66 | 22621
67 | {
68 | Write-Host 'Windows 11 - 22H2' -ForegroundColor Green
69 | }
70 | 22631
71 | {
72 | Write-Host 'Windows 11 - 23H2' -ForegroundColor Green
73 | }
74 | Default
75 | {
76 | Write-Host 'unable to determine the release off of the build number ' -ForegroundColor Green
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/Script- Troubleshoot GroupPolicy on a Workstation.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | Run on W7 or W10 to troubleshoot
3 | A exécuter sur un poste W7 ou W10 qui pose pb pour le GPOs.
4 | #>
5 |
6 | [CmdletBinding()]
7 | Param
8 | (
9 | # Date la plus ancienne pour recherche dans les eventlogs
10 | # Older Date to search in Eventlogs
11 | [Parameter()]
12 | [DateTime]
13 | $StartTime = $(Get-Date -Year 2022 -Month 6 -Day 8 -Hour 08 -Minute 00 -Second 00),
14 |
15 | # Date la plus récente pour recherche dans les eventlogs
16 | # recent date to search in EventLogs
17 | [Parameter()]
18 | [DateTime]
19 | $EndTime = $(Get-Date -Year 2022 -Month 6 -Day 9 -Hour 08 -Minute 00 -Second 00)
20 | # On peut mettre aussi Get-Date pour obtenir la date et l'heure courante
21 | # We can also use Get-Date to get current date et hour
22 | )
23 |
24 |
25 | if (-not (Test-Path -Path 'HKLM:\software\Microsoft\Windows NT\CurrentVersion\Diagnostics') )
26 | {
27 | Write-Host "Diagnostics Key does'nt exist, creating" -ForegroundColor Green
28 | New-Item 'HKLM:\SOFTWARE\Microsoft\Windows NT\currentVersion\Diagnostics'
29 | Write-Host 'Creation GPSvcDebugLevel parameter and setting' -ForegroundColor Green
30 | New-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\currentVersion\Diagnostics' -Name GPSvcDebugLevel -PropertyType DWORD -Value '0x30002'
31 | }
32 | else
33 | {
34 | Write-Host 'Diagnostics Key exists' -ForegroundColor Green
35 | Write-Host 'Settings GPSvcDebugLevel' -ForegroundColor Green
36 | Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\currentVersion\Diagnostics' -Name GPSvcDebugLevel -Value '0x30002'
37 | }
38 | Write-Host 'Go to [%windir%\debug\usermode\gpsvc.log] to see logs file about GroupPolicy in debug mode' -ForegroundColor Green
39 | Invoke-Item $env:windir\debug\usermode\gpsvc.log
40 | Write-Host 'Gathering debug file to later analysis' -ForegroundColor Green
41 | Write-Host 'Also check GroupPolicy event login eventlog applications ==> Microsoft ==> Windows ==> GroupPolicy ==> Operational' -ForegroundColor Green
42 | Write-Host 'LogFile Name : Microsoft-Windows-GroupPolicy/Operational' -ForegroundColor Green
43 | Write-Host 'Export to a .csv file to later analysis, more efficient and faster.' -ForegroundColor Green
44 |
45 | $Query = Get-WinEvent -FilterHashtable @{
46 | LogName = 'Microsoft-Windows-GroupPolicy/Operational'
47 | StartTime = $StartTime
48 | EndTime = $EndTime
49 | #ID = filtering on Id
50 | #Level = filtering on Level. Accepted values : LogAlways 0 - Critical 1 - Error 2 - Warning 3 - Informational 4 - Verbose 5
51 | #UserID = filtering on account SID
52 | }
53 | $Query | Out-GridView
54 | $QuerySelectedProperties = $query |
55 | Select-Object @{Label = 'TimeCreated' ; Expression = { $_.TimeCreated } },
56 | @{Label = 'ID' ; Expression = { $_.ID } },
57 | @{Label = 'MachineName' ; Expression = { $_.MachineName } },
58 | @{Label = 'LevelDisplayName' ; Expression = { $_.LevelDisplayName } },
59 | @{Label = 'Message' ; Expression = { $_.Message } }
60 | # Check if existing c:\temp, Otherwise creating
61 | if (-not (Test-Path -Path C:\Temp) )
62 | {
63 | Write-Host "[C:\temp] doesn't exist, creating" -ForegroundColor Yellow
64 | New-Item -Path 'C:\Temp' -ItemType Directory
65 | }
66 | # Export in a .csv file
67 | $QuerySelectedProperties | Export-Csv -Path 'C:\Temp\GroupPolicy.csv' -Delimiter ';' -NoTypeInformation -Encoding UTF8
68 | # I'm using -Delimiter ";", cause in my culture the default is ";"
69 | Write-Host 'Exporting events in a file : OK' -ForegroundColor Green
70 |
71 |
72 | # Later, don't forget to remove the previous registry key created
73 | Remove-Item -Path 'HKLM:\software\Microsoft\Windows NT\CurrentVersion\Diagnostics' -Recurse
--------------------------------------------------------------------------------
/TIP - How to set up a time counter for your scripts.md:
--------------------------------------------------------------------------------
1 | # Tip : How to set up a time counter for your script
2 |
3 | ## Preface
4 |
5 | Sometimes, you need to know how long did a script run. To do this, we'll implement a time watcher.
6 |
7 |
8 | ## 1 - Define and starting a watcher
9 | Place the following code at the beginning of your script
10 |
11 | ````powershell
12 | $Watcher = [System.Diagnostics.Stopwatch]::StartNew()
13 | ````
14 | ## 2 - Do some code
15 |
16 | Place some code in your script (thanks doctor obvious :-) )
17 |
18 | ## 3 - and at the end of your script : stop the watcher
19 |
20 | ````Powershell
21 | $Watcher.Stop()
22 | ````
23 |
24 | The ``$Watcher`` object has some interresting properties. Let's see them.
25 |
26 | ````powershell
27 | $Watcher
28 |
29 | IsRunning Elapsed ElapsedMilliseconds ElapsedTicks
30 | --------- ------- ------------------- ------------
31 | False 00:00:07.3024634 7302 73024634
32 |
33 |
34 | $Watcher | Get-Member
35 |
36 | TypeName : System.Diagnostics.Stopwatch
37 |
38 | Name MemberType Definition
39 | ---- ---------- ----------
40 | Equals Method bool Equals(System.Object obj)
41 | GetHashCode Method int GetHashCode()
42 | GetType Method type GetType()
43 | Reset Method void Reset()
44 | Restart Method void Restart()
45 | Start Method void Start()
46 | Stop Method void Stop()
47 | ToString Method string ToString()
48 | Elapsed Property timespan Elapsed {get;}
49 | ElapsedMilliseconds Property long ElapsedMilliseconds {get;}
50 | ElapsedTicks Property long ElapsedTicks {get;}
51 | IsRunning Property bool IsRunning {get;}
52 | ````
53 |
54 | but let's see in depth, the property ``Elapsed``. It's a ``TimeSpan`` type.
55 |
56 | ````powershell
57 | $Watcher.Elapsed
58 |
59 | Days : 0
60 | Hours : 0
61 | Minutes : 0
62 | Seconds : 7
63 | Milliseconds : 302
64 | Ticks : 73024634
65 | TotalDays : 8,45192523148148E-05
66 | TotalHours : 0,00202846205555556
67 | TotalMinutes : 0,121707723333333
68 | TotalSeconds : 7,3024634
69 | TotalMilliseconds : 7302,4634
70 | ````
71 |
72 |
73 | ## 4 - Display watcher in console
74 |
75 | here different ways to display the elapsed time counted by the watcher.
76 |
77 | ````powershell
78 | $Watcher.Elapsed
79 | # basic output
80 | write-Host "$($Watcher.Elapsed.Hours)"," h, ","$($Watcher.Elapsed.Minutes)"," min, ","$($Watcher.Elapsed.Seconds)", " sec : Elapsed since the script has started"
81 | # or
82 | Write-Output "$($Watcher.Elapsed.Hours) h, $($Watcher.Elapsed.Minutes) min, $($Watcher.Elapsed.Seconds) sec : Elapsed since the script has started"
83 |
84 | # output using Write-Color cmdlet from PSWriteColor module
85 | Write-Color -Text "$($Watcher.Elapsed.Hours)"," h, ","$($Watcher.Elapsed.Minutes)"," min, ","$($Watcher.Elapsed.Seconds)", " sec : Elapsed since the script has started" -Color Yellow, Green, Yellow, Green, Yellow, Green
86 | ````
87 |
88 |
89 | Hope this will be useful
--------------------------------------------------------------------------------
/TIPS- How to do something using a NT AUTHORITY Account.md:
--------------------------------------------------------------------------------
1 | # TIPS - How to do something using a NT AUTHORITY Account
2 |
3 | ## Introduction
4 |
5 | When I design a script, I want the code to be "code-reuse" as much as possible. However, I often have to work on OSs of different languages.
6 |
7 | This is why when for example I want to search for the members of a particular group, rather than searching for the group by its name, I search for it by its Weel-known SID.
8 | There is a list on the MS site but also here: https://morgantechspace.com/2013/10/well-known-sids-and-built-in-groups.html
9 |
10 | But I am often asked how to do when I want to use an account such as NT AUTHORITY\System, NT AUTHORITY\Local service...
11 | Through the following example, I will describe the approach
12 |
13 | How to retrieve this account ?
14 | There is a well-known SID (S-1-5-19) for this account, but it's not like a Domain Account.
15 | Thanks to https://new.reddit.com/user/PinchesTheCrab/ for the solution.
16 |
17 | The solution is to a WMI query (or CIM query) using the ClassName ````WIN32_Service````
18 |
19 | The example consists of configuring a service to run with the NT AUTHORITY\Local Service account
20 |
21 | ````powershell
22 | Get-CimInstance -ClassName Win32_SystemAccount -Filter 'sid = "S-1-5-19"'
23 | <#
24 | The output looks like the following :
25 |
26 | Caption Domain Name SID
27 | ------- ------ ---- ---
28 | ASUS10\SERVICE LOCAL ASUS10 SERVICE LOCAL S-1-5-19
29 | #>
30 | ````
31 |
32 | It's not enough to get the Name of the account
33 |
34 | Let's use ````Get-CimAssociatedInstance```` cmdlet after the pipeline
35 |
36 | ````powershell
37 | Get-CimInstance -ClassName Win32_SystemAccount -Filter 'sid = "S-1-5-19"' |
38 | Get-CimAssociatedInstance -ResultClassName Win32_SID
39 | <#
40 | The output looks like the following :
41 |
42 | AccountName : SERVICE LOCAL
43 | BinaryRepresentation : {1, 1, 0, 0...}
44 | ReferencedDomainName : AUTORITE NT
45 | SID : S-1-5-19
46 | SidLength : 12
47 | PSComputerName :
48 | #>
49 | ````
50 |
51 | The property ````ReferenceDomaineName```` and the property```` AccountName```` give me enough info to build the real Name of the search account, regardless the culture of the OS.
52 |
53 | ````powershell
54 | $LocalService = Get-CimInstance -ClassName Win32_SystemAccount -Filter 'sid = "S-1-5-19"' |
55 | Get-CimAssociatedInstance -ResultClassName Win32_SID
56 |
57 | $AccountName = "$($localService.ReferencedDomainName)\$($localService.AccountName)"
58 | $AccountName
59 | <#
60 | The output looks like the following :
61 |
62 | AUTORITE NT\SERVICE LOCAL
63 | #>
64 | ````
65 |
66 | At this step, it easy to set a service to run with the identified account
67 | let's do this
68 |
69 | A good way to change the service account using by the service is to use ````Invoke-CimMethod```` cmdlet.
70 |
71 | ````powershell
72 | $ServiceName = 'AJRouter' # here I'm using a ramdom service
73 | $ServiceName |
74 | Invoke-CimMethod -MethodName Change -Arguments @{
75 | StartName = '{0}\{1}' -f $localService.ReferencedDomainName,$localService.AccountName
76 | #StartPassword = $null
77 | DesktopInteract = $false
78 | }
79 | # take care to do this in RunAsAdmin else the return value will be 2.
80 | <#
81 | The output looks like the following :
82 |
83 | ReturnValue PSComputerName
84 | ----------- --------------
85 | 0
86 | #>
87 | ````
88 | Let's check the service set
89 |
90 | ````powershell
91 | $ServiceName | Get-CimInstance | Select-Object name, DesktopInteract, StartName
92 | ````
93 |
94 | > [Nota] that if the service is already running you'll probably need to restart it.
95 |
96 |
--------------------------------------------------------------------------------
/Tip - A elegant way to convert a String to a [TimeSpan].md:
--------------------------------------------------------------------------------
1 | # A elegant way to convert a `[String]` to a `[TimeSpan]`
2 |
3 | ## The [TimeSpan] type
4 |
5 | Do you know that this type has some interresting static méthods ?
6 |
7 | Let's see them.
8 |
9 | ```` powershell
10 | [TimeSpan] | Get-Member -MemberType Method -Static
11 |
12 | TypeName : System.TimeSpan
13 |
14 | Name MemberType Definition
15 | ---- ---------- ----------
16 | Compare Method static int Compare(timespan t1, timespan t2)
17 | Equals Method static bool Equals(timespan t1, timespan t2), static bool Equals(System.Object objA, System.Object objB)
18 | FromDays Method static timespan FromDays(double value)
19 | FromHours Method static timespan FromHours(double value)
20 | FromMilliseconds Method static timespan FromMilliseconds(double value)
21 | FromMinutes Method static timespan FromMinutes(double value)
22 | FromSeconds Method static timespan FromSeconds(double value)
23 | FromTicks Method static timespan FromTicks(long value)
24 | new Method timespan new(long ticks), timespan new(int hours, int minutes, int seconds), timespan new(int days, int hours, int minutes, int seconds), timespan new(int days,...
25 | Parse Method static timespan Parse(string s), static timespan Parse(string input, System.IFormatProvider formatProvider)
26 | ParseExact Method static timespan ParseExact(string input, string format, System.IFormatProvider formatProvider), static timespan ParseExact(string input, string[] formats, Syste...
27 | ReferenceEquals Method static bool ReferenceEquals(System.Object objA, System.Object objB)
28 | TryParse Method static bool TryParse(string s, [ref] timespan result), static bool TryParse(string input, System.IFormatProvider formatProvider, [ref] timespan result)
29 | TryParseExact Method static bool TryParseExact(string input, string format, System.IFormatProvider formatProvider, [ref] timespan result), static bool TryParseExact(string input, st...
30 | ````
31 | In the present case, let's take a look to the 6 methods `From****`.
32 |
33 | ````powershell
34 | $Milliseconds = "100"
35 |
36 | # In the present case, $milliseconds is a [String]
37 | $Milliseconds.GetType()
38 |
39 | IsPublic IsSerial Name BaseType
40 | -------- -------- ---- --------
41 | True True String System.Object
42 | ````
43 |
44 | Now let's convert it to a `[TimeSpan]`
45 |
46 | ```` powershell
47 | $Time= [TimeSpan]::FromMilliseconds($milliseconds)
48 |
49 | $Time
50 | Days : 0
51 | Hours : 0
52 | Minutes : 0
53 | Seconds : 0
54 | Milliseconds : 100
55 | Ticks : 1000000
56 | TotalDays : 1,15740740740741E-06
57 | TotalHours : 2,77777777777778E-05
58 | TotalMinutes : 0,00166666666666667
59 | TotalSeconds : 0,1
60 | TotalMilliseconds : 100
61 | ````
62 | It's a `[timespan]`
63 |
64 | [Nota] : **This tip works also when the input is a `[Int]`**
65 |
66 | ## And does it work if we want to add them together ?
67 |
68 | Let's try
69 |
70 | ````powershell
71 | $sec = "50"
72 | $Min = "3"
73 | $Hour = "1"
74 |
75 | $Time = [TimeSpan]::FromSeconds($sec) + [TimeSpan]::FromMinutes($Min) + [TimeSpan]::FromHours($Hour)
76 | $Time
77 |
78 | Days : 0
79 | Hours : 1
80 | Minutes : 3
81 | Seconds : 50
82 | Milliseconds : 0
83 | Ticks : 38300000000
84 | TotalDays : 0,0443287037037037
85 | TotalHours : 1,06388888888889
86 | TotalMinutes : 63,8333333333333
87 | TotalSeconds : 3830
88 | TotalMilliseconds : 3830000
89 | ````
90 |
91 | This works fine.
92 |
93 | I hope this will be useful to you.
94 |
95 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/Tip - A small fonction to create a Calendar DialogBox in script.md:
--------------------------------------------------------------------------------
1 | # Tip - A small fonction to create a Calendar DialogBox in script
2 |
3 | ## The Use Case
4 |
5 | Imagine that you have a script intended solely for interactive use. In this script, you ask the user to enter a Date.
6 |
7 | Naturally, different ways could be used to avoid errors in the date entered by the user. But we must also take Culture into account (a Date in English does not appear in the same order as a date in French. yyyy MM dd vs dd MM yyyy).
8 |
9 | I offer you below a small advanced function (With no parameter, but it's advanced nonetheless because I added a verbose mode using [Cmdletbinding()]).
10 |
11 | This uses a calendar-style dialog box.
12 |
13 | I didn't invent anything, I just took the code provided on the [Microsoft site](https://learn.microsoft.com/en-us/powershell/scripting/samples/creating-a-graphical-date-picker? view=powershell-5.1) and created a function with all this stuff.
14 |
15 | This can be interesting in your scripts (interactive only)
16 |
17 | ## The Code
18 |
19 | Before the code perhaps a small point of attention.
20 |
21 | I've chosen to use `ShowTodayCircle = $True` in the code below. Feel free to use `$False` for your uses.
22 |
23 |
24 | ```Powershell
25 | function Get-aDate
26 | {
27 | [CmdletBinding()]
28 | param ()
29 |
30 | begin
31 | {
32 | Write-Verbose -Message 'loading .NET classes'
33 | Add-Type -AssemblyName System.Windows.Forms
34 | Add-Type -AssemblyName System.Drawing
35 |
36 | Write-Verbose -Message 'Loading a new instance of [Windows.Forms.form]'
37 | $Form = New-Object Windows.Forms.Form -Property @{
38 | StartPosition = [Windows.Forms.FormStartPosition]::CenterScreen
39 | Size = New-Object Drawing.Size 243, 230
40 | Text = 'Select a Date'
41 | Topmost = $true
42 | }
43 |
44 | Write-Verbose -Message 'adding a MonthCalendar control to the DialogBox'
45 | $Calendar = New-Object Windows.Forms.MonthCalendar -Property @{
46 | ShowTodayCircle = $True
47 | MaxSelectionCount = 1
48 | }
49 | $Form.Controls.Add($Calendar)
50 |
51 | Write-Verbose -Message 'Adding a OK button to the DialogBox'
52 | $OkButton = New-Object Windows.Forms.Button -Property @{
53 | Location = New-Object Drawing.Point 38, 165
54 | Size = New-Object Drawing.Size 75, 23
55 | Text = 'OK'
56 | DialogResult = [Windows.Forms.DialogResult]::OK
57 | }
58 | $form.AcceptButton = $OkButton
59 | $form.Controls.Add($OkButton)
60 |
61 | Write-Verbose -Message 'Adding a Cancel button to the DialogBox'
62 | $CancelButton = New-Object Windows.Forms.Button -Property @{
63 | Location = New-Object Drawing.Point 113, 165
64 | Size = New-Object Drawing.Size 75, 23
65 | Text = 'Cancel'
66 | DialogResult = [Windows.Forms.DialogResult]::Cancel
67 | }
68 | $form.CancelButton = $CancelButton
69 | $form.Controls.Add($CancelButton)
70 | }
71 | process
72 | {
73 | Write-Verbose -Message 'Display the DialogBox'
74 | $Result = $Form.ShowDialog()
75 |
76 |
77 | if ($Result -eq [Windows.Forms.DialogResult]::OK)
78 | {
79 | $Date = $Calendar.SelectionStart
80 | <# Optional
81 | Write-Output "Date selected: $($Date.ToShortDateString())"
82 | #>
83 | }
84 |
85 | }
86 | end
87 | {
88 | $Date
89 | }
90 | }
91 | ```
92 |
93 | Use :
94 |
95 |
96 | ```powershell
97 | # normal Mode
98 | Get-aDate
99 | Date selected: 06/09/2023
100 |
101 | # verbose Mode
102 | Get-aDate -Verbose
103 | COMMENTAIRES : loading .NET classes
104 | COMMENTAIRES : Loading a new instance of [Windows.Forms.form]
105 | COMMENTAIRES : adding a MonthCalendar control to the DialogBox
106 | COMMENTAIRES : Adding a OK button to the DialogBox
107 | COMMENTAIRES : Adding a Cancel button to the DialogBox
108 | COMMENTAIRES : Display the DialogBox
109 | Date selected: 06/09/2023
110 | ```
111 |
112 | >[Nota] : The function return the $Date variable in console if a the OK button has been chosen, else nothing is return. This is why I've chosen `ShowTodayCircle = $True`. If the OK button is pressed, but no date chosen, we'll have today's date.
113 |
114 |
--------------------------------------------------------------------------------
/Tip - Copy-Item (then Remove-Item) or Move-Item.md:
--------------------------------------------------------------------------------
1 | # `Copy-Item` (then `Remove-Item`) or `Move-Item` directly ?
2 |
3 | ## Introduction
4 |
5 | I have always advised both my fellow admins and users to copy files (and then possibly delete them afterwards) rather than move them.
6 |
7 | It doesn't matter whether the operation is done via GUI (copy, paste) or via Powershell (`copy-Item`, `Move-Item`)
8 |
9 | This is to prevent any kind of problem. I will try to explain why.
10 |
11 | ## 1st assertion
12 |
13 | **Any file created inherits the NTFS permissions of its parent.**
14 |
15 | > [**Nota**] : Naturally, for NTFS permissions that can be propagated. A permission like "the Folder only" will not be propagated to a child file.
16 |
17 | ## 2nd Assertion
18 |
19 | **When a file is moved, on a given volume, there is no file creation.**
20 |
21 | Only the allocation table is modified (the file was in such a tree and is now in another tree). Therefore, the file retains its original NTFS permissions.
22 |
23 | ## 3rd assertion
24 |
25 | **If a file is moved to another volume, a file is created on the destination volume.**
26 |
27 | >[Nota] : Each volume has its own allocation table.
28 |
29 | ## And now you see the problems coming ?
30 |
31 | A user notices that a file located in ***\\server\share1*** should be located in **\\server\share2**. He has access to both shares. He drags and drops it. However, his colleague, who only has access to the share ***share2***, cannot access the file.
32 |
33 | The user cannot know if ***Share1*** and ***Share2*** are on the same volume.
34 | Therefore, you must tell him to do a ***Copy/Paste*** (and then delete the file at the source), rather than doing a ***Cut/Paste***. A simple doc or video can do the trick.
35 |
36 | ## And for Admins ?
37 |
38 | And there, you will tell me that for admins it is different, because they know, and above all, don't think that admins don't use the GUI too.
39 |
40 | Do you believe, really ? Some maybe, but not all of you know. **Do not believe, do not presume, be sure**.
41 |
42 | >[Nota ] : Just think of your colleague who is affected by the **Dunning-Kruger effect** (see [English](https://en.wikipedia.org/wiki/Dunning%E2%80%93Kruger_effect) or [French](https://fr.wikipedia.org/wiki/Effet_Dunning-Kruger) ref.). :-)
43 |
44 | But GUI or powershell, as I said in the introduction, that is not the problem. The same assertions apply.
45 |
46 | Does this seem obvious to you ? Not so much actually. How many times have I seen Admins move files via the GUI or in powershell (using `Move-Item`) and thus do stupid things.
47 |
48 | >[**Nota**] : While strangely, for large volumes to move they use **Robocopy** (so copy). Look for the error ! (Don't look, It's [the Fucking Human Factor](https://www.francetvinfo.fr/culture/people/video-le-putain-de-facteur-humain-l-explication-d-hubert-reeves-sur-l-inaction-des-hommes-face-a-l-etat-de-la-terre_3231465.html), as [Hubert Reeves](https://en.wikipedia.org/wiki/Hubert_Reeves) would say).
49 |
50 | ## Wrap up
51 |
52 | So be very careful in your use of `Move-Item` (***Cut/Paste*** in GUI), always keep in mind what is happening under the hood.
53 |
54 | If you must create a script, **use `Copy-Item` rather `than Move-Item`**. If no errors occured, then delete (Using **`Remove-Item`**) the files at the source.
55 |
56 |
57 | I hope this helps some.
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/Tip - How to check the Active Directory related ports with Powershell.md:
--------------------------------------------------------------------------------
1 | # How to check the Active Directory related ports with powershell
2 |
3 | ## the AD related ports are:
4 |
5 | | Service | Port | Protocol | Description |
6 | | :--------------------- | :---------: | :------: | :---------------------------------------------------------------------- |
7 | | LDAP | 389 | TCP/UDP | Lightweight Directory Access Protocol for directory queries and updates
8 | | LDAP over SSL/TLS | 636 | TCP | LDAP over SSL for secure communication
9 | | Kerberos | 88 | TCP/UDP | Authentication protocol used in AD
10 | | DNS | 53 | TCP/UDP | Domain Name System for name resolution and service discovery
11 | | Global Catalog (LDAP) | 3268 | TCP | Directory access to the global catalog (without SSL)
12 | | Global Catalog (LDAPS) | 3269 | TCP | Global Catalog over SSL for secure directory queries and updates
13 | | Netlogon | 445 | TCP | Netlogon service for authentication and replication
14 | | SMB/CIFS | 445 | TCP | Server Message Block for file sharing and AD replication
15 | | RPC | 135 | TCP | Remote Procedure Call | often used for DCOM services
16 | | Dynamic RPC Ports | 49152-65535 | TCP | Dynamically assigned ports for RPC connections | often used for replication
17 |
18 | The list above highlights the most critical ports. However, if your server hosts additional roles and features, there will inevitably be other open ports.
19 |
20 | # How to check the ports with PowerShell
21 |
22 | ````Powershell
23 | Get-NetTCPConnection -State Established
24 | ````
25 |
26 | Alternatively, you can use the `netstat` command. This is combined in the following example:
27 |
28 | ````DOS
29 | Netstat -an | Select-String ":389|:636|:88|:53|:3268|:3269|:445|:135|49152-65535"
30 | ````
31 |
32 | **Get more process details**
33 |
34 | If you want detailed information about the processes running on different ports, use the following command:
35 | ````Powershell
36 | Get-Process -Id (Get-NetTCPConnection | Where-Object { $_.LocalPort -eq 389 }).OwningProcess
37 |
38 | Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessNameNPM()
39 | ------- ------ ----- ----- ------ -- -- ----------- ---
40 | 1986 281 128692 119888 126.41 844 0 lsass
41 | ````
42 | This command lets you determine which processes are running on a specific port (in this case, 389 for LDAP).
43 |
44 |
45 | **Check Firewall configuration**
46 |
47 | Sometimes, checking firewall settings to ensure that needed ports are open is useful. PowerShell is also great for this:
48 | ````Powershell
49 | Get-NetFirewallRule | Where-Object { $_.LocalPort -in 389, 636, 88, 53, 3268, 3269, 445, 135 }
50 | ````
51 |
52 | This command lists all firewall rules for the major Active Directory ports.
53 |
--------------------------------------------------------------------------------
/Tip - Quickly Open an .exe file maximized windows.md:
--------------------------------------------------------------------------------
1 | # Quickly Open an .exe file in maximized windows
2 |
3 | Sometimes in a script we need to open a .exe file in a maximized windows.
4 |
5 | ````powershell
6 | Start-Process -FilePath -WindowStyle ([System.Diagnostics.ProcessWindowStyle]::Maximized)
7 | # or a shorter version
8 | Start-Process -FilePath -WindowStyle Maximized
9 |
10 | # and open a browser with a predefined page.
11 | Start-Process -FilePath msedge -ArgumentList '--new-window www.google.com --start-maximized'
12 | ````
13 |
14 | This could be useful to keep in mind.
--------------------------------------------------------------------------------
/Tips - Some words about Compare-Object.md:
--------------------------------------------------------------------------------
1 |
2 | # Some words about ````Compare-Object````
3 |
4 | ## Compare-Object not working if I don't list the properties
5 |
6 | if you don't specify the ````-Property```` parameter, ````Compare-Object```` doesn't compare all properties, it compares the results of invoking the ````.ToString()```` method on both objects. So, ````Compare-Object $DataTable $DataTable2```` compares ````$DataTable1.ToString()```` with ````$DataTable1.ToString()````. The ````.ToString()```` method returns an empty string when invoked on a DataTable object, so there is no difference to report.
7 |
8 | For example:
9 |
10 | ````powershell
11 | $file1 = Get-Item somefilename
12 | $file1 = Get-Item anotherfilename
13 | Compare-Object $file1 $file2
14 | ````
15 |
16 | This will return the difference between the full paths of the two files, like this:
17 |
18 | ````powershell
19 | InputObject SideIndicator
20 | ---------- - ------------ -
21 | \anotherfilename =>
22 | \somefilename <=
23 | ````
24 |
25 | That's because invoking ````.ToString()```` on a FileInfo object returns its FullName property, so you're comparing the files's full path names.
26 |
27 | Although the ````-Property```` parameter accepts multiple properties, listing all the properties is not the solution. Aside from being very tedious, it will not give you the results you want. If you list multiple properties, ````Compare-Object```` compares the ***combination of all the properties***, and if any one of the listed properties is different, it returns a result showing all the listed properties (both ones that are the same and ones that are different) as a single difference.
28 |
29 | What you need to do is iterate over a list of properties, and invoke ````Compare-Object```` once for each property:
30 |
31 | ````powershell
32 | $properties = ($DataTable | Get-Member -MemberType Property | Select-Object -ExpandProperty Name)
33 | foreach ($property in $properties) {
34 | Compare-Object $DataTable $DataTable2 -Property "$property" | Format-Table -AutoSize
35 | }
36 | ````
37 |
38 | In most cases, when comparing all properties of two objects, you'd want to use ````Get-Member -MemberType```` Properties, in order to get cover all property types. However, if you're comparing DataTable objects, you're better off using ````Get-Member -MemberType Property```` so that you're comparing only the properties corresponding to data fields, not other properties of the DataTable objects that have nothing to do with the data.
39 |
40 | This is written assuming that ***the number of columns is the same***, as you stated, ***or at least that the number of columns in $DataTable2 doesn't exceed the number of columns in $DataTable***.
41 |
42 | If you can't reliably assume that, derive the $properties array from whichever one has more columns, by comparing ````($DataTable | Get-Member -MemberType Property).Count```` with ````($DataTable2 | Get-Member -MemberType Property).Count```` and using the properties from whichever is greater.
43 |
44 | Using ````Format-Table```` is important, it's not just there to make things look pretty. If you list multiple objects of the same type (in this case, arrays), PowerShell remembers the format of the first object, and uses it for all subsequent objects, unless you explicitly specify a format. Since the name of the first column will be different for each property (i.e., each column from the spreadsheet), the first column will be empty for all but the first difference encountered.
45 |
46 | The ````-AutoSize```` switch is optional. That is there just to make things look pretty. But you must pipe the results to a formatting filter. You can also use ````Format-List```` if you prefer.
47 |
48 | Ref. Source : from mklement0
49 |
50 | ## ````Compare-Object```` with ````Get-Content```` of .txt files
51 |
52 | ````powershell
53 | $ref = Get-Content C:\Temp\37896.txt
54 | $new = Get-Content C:\temp\41553.txt
55 | Compare-Object -ReferenceObject $ref -DifferenceObject $new
56 | ````
57 |
58 | This returns, that expected : comparing content of the 2 files
59 |
60 | ````powershell
61 | InputObject SideIndicator
62 | ----------- -------------
63 | Just a simple demo file <=
64 | ````
65 |
66 | ## ````Compare-Object```` with ````Import-Csv```` of .csv files
67 |
68 | ````powershell
69 | $ref = Import-Csv -Path C:\Temp\csv1.csv
70 | $ref
71 |
72 | Autre ID
73 | ----- --
74 | sefsdf 1
75 | fsgsdg 2
76 | rgerg 3
77 | $new = Import-Csv -Path C:\temp\csv2.csv
78 | $new
79 |
80 | Autre ID
81 | ----- --
82 | sefsdf 1
83 | fsgsdg 4
84 | rgerg 5
85 | Compare-Object -ReferenceObject $ref -DifferenceObject $new -Property ID
86 | ID SideIndicator
87 | -- -------------
88 | 4 =>
89 | 5 =>
90 | 2 <=
91 | 3 <=
92 | ````
93 |
94 | As we can see, this returns the expected result, because we've compared a specific property
95 |
96 |
97 | Hope this help
98 |
--------------------------------------------------------------------------------
/Tips - 2 ways to rename file in mass.ps1:
--------------------------------------------------------------------------------
1 | # 2 ways to rename file in mass
2 |
3 | <# I need to rename a set of 540 files as such:
4 | Folder: C:\ABCD
5 | Original names are varied, mostly "ABCD (#).raw", but others can be "ABCD _longtext (#)"
6 | Destination names: ABCD-YYYY-MM-DD-HH-SS.RAW where the timestamp is the ModificationDate (not creation).
7 | #>
8 |
9 |
10 | (Get-ChildItem -File C:\abcd\abcd*.raw) |
11 | Rename-Item -NewName {
12 | "ABCD-{0:yyyy-MM-dd-HH-mm-ss}.raw" -f $_.LastWriteTime
13 | }
14 |
15 | # With regex
16 | $files = Get-ChildItem -Path 'c:\temp\test' '*.raw'
17 | foreach ($file in $files){
18 | $my_date = "{0:yyyy-MM-dd-HH-mm-ss}" -f $file.LastWriteTime
19 | $Begin_name = [regex]::Match($file.name,'^([A-Z]+)[\d\(_\s].+\.raw$').groups[1].value
20 | $New_name = $Begin_name +'-'+ $my_date + '.RAW'
21 | Rename-Item -Path $file.fullname -NewName $New_name -WhatIf
22 | }
--------------------------------------------------------------------------------
/Tips - 2 ways to split a root UNC Path and get the Parent and the children without any caracter.md:
--------------------------------------------------------------------------------
1 | # THE PROBLEM
2 |
3 | Recently, i was confronted to a the following question : How can i split a path and get the Parent and the children without "\\\\" or "\\\"
4 |
5 | - The path is a UNC path, like this ````\\server\share````
6 | - Just ````\\server\share````, not ````\\server\share\...\file````
7 |
8 | Grr, the Split-Path cmdlet seems doesn't work with root UNC Path
9 |
10 | Finally, i've found on the Internet 2 different ways to resolve this problem :
11 |
12 | # FIRST WAY : SPLIT METHOD
13 |
14 | ````powershell
15 | $FullPath = "\\server\folder"
16 | $FullPath | get-Member
17 | ````
18 |
19 | return
20 |
21 | ````powershell
22 | TypeName : System.String
23 |
24 | Name MemberType Definition
25 | ---- ---------- ----------
26 | Clone Method System.Object Clone(), System.Object ICloneable.Clone()
27 | CompareTo Method int CompareTo(System.Object value), int CompareTo(string strB), int IComparable.CompareTo(System.Object obj...
28 | Contains Method bool Contains(string value)
29 | ...
30 | Split Method string[] Split(Params char[] separator), string[] Split(char[] separator, int count), string[] Split(char[]...
31 | ...
32 | ````
33 |
34 | OK, it's a ````[system.string]````. Let's try the split method
35 |
36 | ````powershell
37 | $FullPath = "\\server\folder"
38 | $FullPath.Split("\")
39 | ````
40 |
41 | This return
42 |
43 | ````powershell
44 |
45 |
46 | server
47 | folder
48 | ````
49 |
50 | 4 lines. 2 blank lines, and 2 lines with useful info
51 | Now it should be easy to get differential values by adding the line number.
52 | [0] is used for the first line,
53 | [1] for the second, etc.
54 | Nota : it seems that then number [-1] represents the last line (could be useful some times)
55 |
56 | ````powershell
57 | $ParentPath = $FullPath.Split("\")[2])
58 | $ParentPath
59 | ````
60 |
61 | return
62 |
63 | ````powershell
64 | server
65 | ````
66 |
67 | and
68 |
69 | ````powershell
70 | $ChildrenPath = $FullPath.Split("\")[3])
71 | $ChildrenPath
72 | ````
73 |
74 | return
75 |
76 | ````powershell
77 | folder
78 | ````
79 |
80 | Yes !
81 |
82 | ## comments
83 |
84 | Transform a ````[System.String]```` with the split method is very easy to use and probably the easiest way.
85 |
86 | Ref :
87 |
88 | # SECOND WAY : Use [System.Uri]
89 |
90 | ````powershell
91 | $FullPath = "\\server\folder"
92 | $URI = New-Object System.Uri($FullPath) # or $URI = [URI]$FullPath
93 | $URI
94 | ````
95 |
96 | This return
97 |
98 | ````powershell
99 | AbsolutePath : /folder
100 | AbsoluteUri : file://server/folder
101 | LocalPath : \\server\folder
102 | Authority : server
103 | HostNameType : Dns
104 | IsDefaultPort : True
105 | IsFile : True
106 | IsLoopback : False
107 | PathAndQuery : /folder
108 | Segments : {/, folder}
109 | IsUnc : True
110 | Host : server
111 | Port : -1
112 | Query :
113 | Fragment :
114 | Scheme : file
115 | OriginalString : \\server\folder
116 | DnsSafeHost : server
117 | IdnHost : server
118 | IsAbsoluteUri : True
119 | UserEscaped : False
120 | UserInfo :
121 | ````
122 |
123 | Now, we can use the following property to have the result
124 |
125 | ````powershell
126 | $ParentPath = $URI.Host
127 | $ParentPath
128 | ````
129 |
130 | return
131 |
132 | ````powershell
133 | server
134 | ````
135 |
136 | and
137 |
138 | ````powershell
139 | $ChildrenPath = $URI.AbsolutePath
140 | $ChildrenPath
141 | ````
142 |
143 | return
144 |
145 | ````powershell
146 | /folder
147 | ````
148 |
149 | Not exactly that i would like. Adding Split method
150 |
151 | ````powershell
152 | $ChildrenPath = $URI.AbsolutePath.Split("/")
153 | $ChildrenPath
154 | ````
155 |
156 | return
157 |
158 | ````powershell
159 |
160 | folder
161 | ````
162 |
163 | Not yet, one line more.
164 | Let's play with the split method again.
165 | Adding a second parameter : number of elements to return. As we can see 2 Lines/elements, choose 2.
166 | Adding a third parameter : option. Option will be a ````[System.StringSplitOptions]::RemovingEmptyEntries````
167 |
168 | ````powershell
169 | $option = [System.StringSplitOptions]::RemoveEmptyEntries
170 | $ChildrenPath = $URI.AbsolutePath.Split("/",2,$option)
171 | $ChildrenPath
172 | ````
173 |
174 | return
175 |
176 | ````powershell
177 | folder
178 | ````
179 |
180 | Yes !
181 |
182 | ## comments
183 |
184 | Transform the ````[System.String]```` to ````[System.Uri]```` like this ````[URI]$FullPath```` return 2 important properties ````Host```` and ````AbsolutePath````
185 | And it was easy to get the parent, but for the children it's not so : using split method with option.
186 | If you don't know which option to use, well, it's not easy
187 |
188 | Ref :
189 |
--------------------------------------------------------------------------------
/Tips - 3 ways to unzip compressed Files.ps1:
--------------------------------------------------------------------------------
1 | # This tip is based on the following article from Prateeek Singh (https://ridicurious.com/2019/07/29/3-ways-to-unzip-compressed-files-using-powershell/)
2 |
3 | $ZippedFilePath = "c:\temp\archive.zip"
4 | $DestinationFolder = "c:\temp2"
5 | Get-ChildItem -Path $DestinationFolder -Recurse | Remove-Item # initialization
6 | $Iterations = "20"
7 |
8 |
9 | #region Using Expand-Archive Cmdlet -PS V5)
10 |
11 | $ExpandArchive = 0..$Iterations | Measure-command -Expression {
12 | Expand-Archive -Path $ZippedFilePath -DestinationPath $DestinationFolder
13 | Get-ChildItem -Path $DestinationFolder -Recurse
14 | Get-ChildItem -Path $DestinationFolder -Recurse | Remove-Item
15 | }
16 |
17 | #endregion
18 |
19 | #region Using .Net v4.5 class [System.IO.Compression.ZipFile]
20 | $NetClass = 0..$Iterations | Measure-command -Expression {
21 | [System.IO.Compression.ZipFile]::extractToDirectory($ZippedFilePath, $DestinationFolder)
22 | Get-ChildItem -Path $DestinationFolder -Recurse
23 | Get-ChildItem -Path $DestinationFolder -Recurse | Remove-Item
24 | }
25 | #endregion
26 |
27 | #region Using Folder.CopyHere() Method of Shell.Application Class
28 | $FolderCopyHere = 0..$Iterations | Measure-command -Expression {
29 | $Shell = New-Object -ComObject shell.application
30 | $Shell.Namespace($DestinationFolder).copyhere($Shell.NameSpace($ZippedFilePath).Items(), 4)
31 | Get-ChildItem -Path $DestinationFolder -Recurse
32 | Get-ChildItem -Path $DestinationFolder -Recurse | Remove-Item
33 | }
34 | #endregion
35 |
36 | #region Results
37 | Write-Host "ExpandArchive : " -ForegroundColor Green -NoNewline
38 | Write-Host "$($ExpandArchive.TotalMilliseconds)" -ForegroundColor Yellow -NoNewline
39 | Write-Host " Ms" -ForegroundColor Green
40 |
41 | Write-Host "NetClass : " -ForegroundColor Green -NoNewline
42 | Write-Host "$($NetClass.TotalMilliseconds)" -ForegroundColor Yellow -NoNewline
43 | Write-Host " Ms" -ForegroundColor Green
44 |
45 | Write-Host "FolderCopyHere : " -ForegroundColor Green -NoNewline
46 | Write-Host "$($FolderCopyHere.TotalMilliseconds)" -ForegroundColor Yellow -NoNewline
47 | Write-Host " Ms" -ForegroundColor Green
48 | #endregion
49 |
50 | #region comments
51 | <# I've tested on differents zip files. Here a result for looping 20 times
52 | ExpandArchive : 1157.6897 Ms
53 | NetClass : 879.3944 Ms
54 | FolderCopyHere : 21007.1486 Ms
55 |
56 | Synthesis of the performance tests :
57 | ... and the result is always the same. Using Net Class is fastest, and Shell.Application class the slowest from ... far. :-(
58 | The native cmdlet have often executing time not far as .Net class, but in my opinion, the cmdlet is more readable, more scriptable,
59 | more practical to use ... and it's matching the opposite cmldlet : Compress-Archive.
60 | #>
61 | #endregion
62 |
63 |
--------------------------------------------------------------------------------
/Tips - About [DateTime].md:
--------------------------------------------------------------------------------
1 | # About [DateTime]
2 |
3 | ## DaysInMonth property
4 |
5 | The class [DateTime] has an interesting property : DaysInMonth
6 | The syntax is : ````[DateTime]::DaysInMonth(, )````
7 | i.e.
8 |
9 | ````powershell
10 | $Current Date / Times
11 | $CurrentYear = $((Get-Date).Year) #Current Year
12 | $CurrentMonth = $((Get-Date).Month) #Current Month
13 | $LastDayofCurrentMonth = [DateTime]::DaysInMonth($CurrentYear, $CurrentMonth)
14 | Write-Host "Current Year is $CurrentYear " -ForegroundColor Green
15 | Write-Host "Current Month is $CurrentMonth" -ForegroundColor Green
16 | Write-host "Last Day of CurrentMonth is $LastDayofCurrentMonth" -ForegroundColor Green
17 | Current Year is 2020
18 | Current Month is 2
19 | Last Day of CurrentMonth is 29
20 | ````
21 |
22 | This Could be useful when we want to work with date.
23 |
24 | ## First Day of the month
25 |
26 | To get the first day of the month we can do like the following
27 |
28 | ````powershell
29 | $FirstDateTimeoftheMonth = Get-Date -Day 1 -Month $CurrentMonth -Year $CurrentYear -Hour 0 -Minute 0 -Second 0
30 | $FirstDateTimeoftheMonth
31 | samedi 1 février 2020 00:00:00
32 | ````
33 |
34 | Of course, this object is a [DateTime] object, then we can query its specifics property.
35 |
36 | ````powershell
37 | $FirstDateTimeoftheMonth.Date
38 | $FirstDateTimeoftheMonth.Day
39 | $FirstDateTimeoftheMonth.DayOfWeek
40 | ````
41 |
42 | ## Last [DateTime] of the month
43 |
44 | Use the previous var to build an new [DateTime] object giving the desired information
45 |
46 | ````powershell
47 | $LastDateTimeoftheMonth = Get-Date -Day $LastDayofCurrentMonth -Month $CurrentMonth -Year $CurrentYear -Hour 23 -Minute 59 -Second 59
48 | ````
49 |
50 |
51 |
52 | references :
53 |
--------------------------------------------------------------------------------
/Tips - Appropriate way to have an output.md:
--------------------------------------------------------------------------------
1 | # Quick Tips about appropriate ways to have an output
2 |
3 | ## Good : Only for on-screen output
4 |
5 | ````powershell
6 | Get-Stuff |
7 | Format-Table Prop1, @{n='CalculatedProp';e={$_.ExtraStuff}}
8 | ````
9 |
10 | ## Bad : Format cmdlet passing down the pipe
11 |
12 | ````powershell
13 | Get-Stuff |
14 | Format-Table Prop1, @{n='CalculatedProp';e={$_.ExtraStuff}} |
15 | Export-Csv "C:\results.csv"
16 | ````
17 |
18 | The ````Format-Table```` cmdlet is designed to ````Out-```` cmdlets only
19 |
20 | ## Fixed : Selecting when passing down the pipeline
21 |
22 | ````powershell
23 | Get-Stuff |
24 | Select-Object Prop1, @{n='CalculatedProp';e={$_.ExtraStuff}} |
25 | Export-Csv "C:\results.csv" Appropriate way to have an output
26 | ````
27 |
28 | ## Explanations
29 |
30 | Let's take some samples
31 |
32 | ````powershell
33 | Get-Service |
34 | Format-Table Name, @{n='DisplayName';e={$_.DisplayName}} | Get-Member
35 | TypeName : Microsoft.PowerShell.Commands.Internal.Format.FormatStartData
36 |
37 | Name MemberType Definition
38 | ---- ---------- ----------
39 | Equals Method bool Equals(System.Object obj)
40 | GetHashCode Method int GetHashCode()
41 | GetType Method type GetType()
42 | ToString Method string ToString()
43 | autosizeInfo Property Microsoft.PowerShell.Commands.Internal.Format.AutosizeInfo, System.Management.Automation...
44 | ClassId2e4f51ef21dd47e99d3c952918aff9cd Property string ClassId2e4f51ef21dd47e99d3c952918aff9cd {get;}
45 | groupingEntry Property Microsoft.PowerShell.Commands.Internal.Format.GroupingEntry, System.Management.Automatio...
46 | pageFooterEntry Property Microsoft.PowerShell.Commands.Internal.Format.PageFooterEntry, System.Management.Automat...
47 | pageHeaderEntry Property Microsoft.PowerShell.Commands.Internal.Format.PageHeaderEntry, System.Management.Automat...
48 | shapeInfo Property Microsoft.PowerShell.Commands.Internal.Format.ShapeInfo, System.Management.Automation, V...
49 |
50 |
51 | TypeName : Microsoft.PowerShell.Commands.Internal.Format.GroupStartData
52 |
53 | Name MemberType Definition
54 | ---- ---------- ----------
55 | Equals Method bool Equals(System.Object obj)
56 | GetHashCode Method int GetHashCode()
57 | GetType Method type GetType()
58 | ToString Method string ToString()
59 | ClassId2e4f51ef21dd47e99d3c952918aff9cd Property string ClassId2e4f51ef21dd47e99d3c952918aff9cd {get;}
60 | groupingEntry Property Microsoft.PowerShell.Commands.Internal.Format.GroupingEntry, System.Management.Automatio...
61 | shapeInfo Property Microsoft.PowerShell.Commands.Internal.Format.ShapeInfo, System.Management.Automation, V...
62 | # ...
63 |
64 | Get-Service |
65 | Select-Object -Property Name, @{n='DisplayName';e={$_.DisplayName}} | Get-Member
66 | TypeName : Selected.System.ServiceProcess.ServiceController
67 |
68 | Name MemberType Definition
69 | ---- ---------- ----------
70 | Equals Method bool Equals(System.Object obj)
71 | GetHashCode Method int GetHashCode()
72 | GetType Method type GetType()
73 | ToString Method string ToString()
74 | DisplayName NoteProperty System.String DisplayName=Agent Activation Runtime_1058c1
75 | Name NoteProperty string Name=AarSvc_1058c1
76 | ````
77 |
78 | What you are seeing is that ````Format-Table```` transforms the objects into a stream of formatting directives.
79 | These are then consumed by one of the ````Out-```` Cmdlets (````Out-Host````, ```Out-File```, ````Out-String````, ````Out-Printer````).
80 |
81 | This is why you ***can’t pipe format-table*** to ````Export-Csv````.
82 |
--------------------------------------------------------------------------------
/Tips - Beginning parallel processing in PowerShell.ps1:
--------------------------------------------------------------------------------
1 | # Ping a subnet : Beginning parallel processing in PowerShell
2 | # ref : https://hkeylocalmachine.com/?p=612
3 |
4 | # Gather network address (must end in .0)
5 | $network = read-host "Enter network address";
6 | $networkcheck = $network.substring($network.length-2);
7 |
8 | if ($networkcheck -eq ".0") {
9 | # Drop the .0 from the network address
10 | $network = $network.substring(0,$network.length-1);
11 |
12 | # Create Runspace Pool with 500 threads
13 | $pool = [RunspaceFactory]::CreateRunspacePool(1, 500)
14 | $pool.ApartmentState = "MTA"
15 | $pool.open()
16 | $runspaces = @()
17 |
18 | # The script you want run against each host
19 | $scriptblock = {
20 |
21 | # Take the IP address as a parameter
22 | param ([string]$ip);
23 |
24 | # Ping IP address
25 | $online = test-connection $ip -count 1 -ea 0;
26 |
27 | # Print IP address if online
28 | if ($online) {
29 | $ip;
30 | }
31 | }
32 |
33 | # Loop through numbers 1 to 254
34 | foreach ($hostnumber in 1..254) {
35 |
36 | # Set full IP address
37 | $ip = $network + $hostnumber;
38 |
39 | $runspace = [powershell]::create()
40 |
41 | # Add script block to runspace (use $null to avoid noise)
42 | $null = $runspace.addscript($scriptblock)
43 |
44 | # Add IP address as an argument to the scriptblock (use $null to avoid noise)
45 | $null = $runspace.addargument($ip)
46 |
47 | # Add/create new runspace
48 | $runspace.runspacepool = $pool
49 | $runspaces += [pscustomobject]@{pipe=$runspace; Status=$runspace.begininvoke() }
50 | }
51 |
52 | # Prepare the progress bar
53 | $currentcount = 0;
54 | $totalcount = ($runspaces | measure-object).count;
55 |
56 | # Pause until all runspaces have completed
57 | while ($runspaces.status -ne $null)
58 | {
59 | $completed = $runspaces | where { $_.status.iscompleted -eq $true };
60 |
61 | # Update progress bar
62 | $currentcount = $currentcount + ($completed | measure-object).count;
63 | write-progress -activity "Pinging IP Addresses..." -percentcomplete (([int]$currentcount/[int]$totalcount)*100);
64 |
65 | # Clear completed runspaces
66 | foreach ($runspace in $completed)
67 | {
68 | $runspace.pipe.endinvoke($runspace.status)
69 | $runspace.status = $null
70 | }
71 | }
72 |
73 | # Clean-up Runspace Pool
74 | $pool.close();
75 | $pool.dispose();
76 |
77 | } else {
78 | write-host "NOT A VALID NETWORK ADDRESS" -foregroundcolor "red" -backgroundcolor "black";
79 | }
--------------------------------------------------------------------------------
/Tips - Check of DNS for specific old records.ps1:
--------------------------------------------------------------------------------
1 | <#check of DNS for specific old records
2 | https://hkeylocalmachine.com/?p=620
3 |
4 | When decommissioning servers (Domain Controllers especially) there are often errand and orphaned DNS records.
5 | This script takes a wildcard search parameter (EG below is *dc04*) and searches all zones for any trace of it,
6 | and return the results in a handy-dandy grid view,
7 | including the zone and location of the record (very handy when you’re searching dozens of zones).
8 | You could further extend this script to clean up and remove these records, but I’ll leave that up to you.
9 | #>
10 |
11 |
12 | # The data we're looking for (MUST INCLUDE asterisks). Can use HostNames, Domain Names, IP Addresses.
13 | $FindForMe = "*dc04*"
14 |
15 | # Create an empty results array
16 | $Results = @()
17 |
18 | # Get all primary forward lookup DNS zones
19 | # we're looking only on primary and forward zones (not reverses zones or secondary zones). howerver, It's a nonsense when the zones on the DNS server are AD integrated
20 | $Zones = Get-DnsServerZone | # here you can use the parameter -ComputerName to define the DNS Server where you run the query
21 | Where-Object { ($_.ZoneType -eq "primary") -and ($_.isReverseLookupZone -eq $false) }
22 |
23 |
24 | # Now, we Loop through each zone found
25 | foreach ($Zone in $Zones)
26 | {
27 | # Get DNS records that match with the search criteria
28 | $Records = Get-DnsServerResourceRecord -ZoneName $Zone.zonename |
29 | Where-Object { ($_.RecordData.ipV4Address.ipAddressToString -like $FindForMe) -or ($_.RecordData.HostNameAlias -like $FindForMe) -or ($_.HostName -like $FindForMe) -or ($_.RecordData.NameServer -like $FindForMe) -or ($_.RecordData.MailExchange -like $FindForMe) -or ($_.RecordData.DomainName -like $FindForMe) }
30 | # Loop through each record found
31 | foreach ($Record in $Records)
32 | {
33 | # Define the data, given the different record types
34 | switch ($Record.recordtype)
35 | {
36 | "A"
37 | {
38 | $Data = $Record.RecordData.ipv4Address.ipAddressToString
39 | }
40 | "NS"
41 | {
42 | $Data = $Record.RecordData.NameServer
43 | }
44 | "MX"
45 | {
46 | $Data = $Record.RecordData.MailExchange
47 | }
48 | "CNAME"
49 | {
50 | $Data = $Record.RecordData.HostNameAlias
51 | }
52 | "SRV"
53 | {
54 | $Data = $Record.RecordData.DomainName;
55 | }
56 | }
57 | # Add row of data to results array
58 | $Row = "" | Select-Object ZoneName, RecordType, HostName, Data
59 | $Row.ZoneName = $Zone.ZoneName
60 | $Row.RecordType = $record.RecordType
61 | $Row.HostName = $record.HostName
62 | $Row.Data = $Data
63 | $Results = $Results + $Row
64 | } # end foreach
65 | } # End foreach
66 |
67 | # Display results (here in the out-gridvwiew but you can choose to display in console, export in a.txt, a .csv, a .html or a .xml file, as you want)
68 | $Results | Sort-Object ZoneName, RecordType, HostName, Data | Out-GridView
69 |
--------------------------------------------------------------------------------
/Tips - CmdLets Useful for network.ps1:
--------------------------------------------------------------------------------
1 | # Tester la connectivité (ping)
2 | Test-Connection -ComputerName RemoteComputer
3 | Test-Connection -ComputerName 10.8.10.80
4 |
5 | # boucle de ping
6 | 1..254 | ForEach-Object { Test-Connection 10.10.10.$_ } | Format-Table -AutoSize
7 |
8 | # Tester la connectivité basé sur le port ou le service
9 | Test-NetConnection -ComputerName RemoteComputer -CommonTCPPort RDP # (RDP, HTTP, SMB, WINRM)
10 | Test-NetConnection -ComputerName RemoteComputer -Port 80
11 | # Trace route
12 | Test-NetConnection -ComputerName RemoteComputer -TraceRoute
13 | Test-NetConnection -ComputerName RemoteComputer -Hops 10
14 | Test-NetConnection -ComputerName RemoteComputer -DiagnoseRouting
15 |
16 | # voir les routes
17 | Get-NetRoute
18 | Get-NetRoute -InterfaceAlias Ethernet
19 |
20 |
21 |
22 | # Obtenir la configuration IP
23 | Get-NetIPConfiguration
24 | Get-NetIPConfiguration -Detailed # équivalent à IPconfig /all
25 |
26 | # Résolution DNS
27 | Resolve-DnsName -Name RemoteComputer
28 | Resolve-DnsName -Name RemoteComputer -Type A | Select-Object -Property Name, Type, IpAddress
29 | Resolve-DnsName -Name 10.8.10.80 -Type PTR
30 |
31 | # voir les connexions TCP courantes (équivalent de arp -a)
32 | Get-NetTCPConnection
33 | Get-NetTCPConnection -State Established
34 |
35 |
36 | # voir les informations DNS (info de l'onglet DNS dans la conf IP)
37 | Get-DnsClient
38 | Get-DnsClientServerAddress -AddressFamily IPv4 -InterfaceAlias Ethernet
39 |
40 | # flush DNS cache (équivalent à ipconfig /flushDNS)
41 | Clear-DnsClientCache
42 |
43 | # Lister toutes les cartes réseaux
44 | Get-NetAdapter
45 | Get-NetAdapter | Select-Object -Property *
46 | Get-NetAdapter | Select-Object -Property Name, MacAddress, InterfaceAlias, TransmitLinkSpeed
47 | Get-NetAdapter | Select-Object -Property Name, DriverVersion, DriverInformation, DriverFileName
48 |
49 | # Informations Hardware
50 | Get-NetAdapterHardwareInfo
51 |
52 | # Activer/désactiver une carte réseau
53 | Enable-NetAdapter -Name xxx
54 | Disable-NetAdapter -Name xxx
55 | Rename-NetAdapter -Name "Ethernet" -NewName "EthernetFilaire"
56 |
57 |
58 | Get-NetIPAddress -InterfaceIndex 4
59 | (Get-NetAdapter -Name "Local Area Connection" | Get-NetIPAddress).IPAddress
60 | Get-NetAdapter -Name "Local Area Connection" | Get-DnsClientServerAddress
61 |
62 | # Ajouter une adresse IP
63 | New-NetIPAddress -InterfaceAlias Ethernet -IPAddress 10.0.0.10 -DefaultGateway 10.0.0.254 -AddressFamily IPv4 -PrefixLength 24
64 | # changer une adresse IP existante
65 | Set-NetIPAddress -InterfaceAlias Ethernet -IPAddress 10.0.0.10 -PrefixOrigin Manual
66 | Set-NetIPInterface -InterfaceAlias Wireless -Dhcp Enabled
67 |
68 |
69 | # Créer un nouveau TEAM (lbfo Team ou Load-balancing with failover team)
70 | New-NetLbfoTeam -Name NICTeam -TeamMembers Ethernet1, Ethernet2 -TeamingMode Lacp -TeamNicName NICTEAM -LoadBalancingAlgorithm Dynamic
71 |
72 | # Obtenir la table ARP
73 | Get-NetTCPConnection
74 | # ou pour filtrer sur les connexions établies uniquement
75 | Get-NetTCPConnection | Where-Object {$_.State -like "Established"}
76 |
77 | # Obtenir la MACaddress
78 | Get-WmiObject win32_networkadapterconfiguration | select description, macaddress
79 | Get-CimInstance win32_networkadapterconfiguration | select description, macaddress
80 |
81 |
82 |
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/Tips - Comment s'assurer qu'un booléen ne retourne que True ou False.ps1:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myusefulrepo/Tips/8da8779d41efb663ecb1ef39734a57eea1afe699/Tips - Comment s'assurer qu'un booléen ne retourne que True ou False.ps1
--------------------------------------------------------------------------------
/Tips - Count number of the week in a month based on each week start on a specific day.md:
--------------------------------------------------------------------------------
1 | # The challenge
2 |
3 | Get the number of weeks per month based on the assumption that each week starts on a specifc day (i.e. : Sunday)
4 |
5 | ## Define the day starting the week
6 |
7 | ````powershell
8 | $FirsDayofweek = "sunday"
9 | ````
10 | [Nota] : You must use the Day in ````en-US```` language, even your current language is different.
11 |
12 |
13 | ## retrieve the Year, month and Days in month
14 |
15 | ````powershell
16 | $Year = (Get-Date).Year
17 | $Month = (Get-Date).Month
18 | $days = [datetime]::DaysInMonth($Year,$Month)
19 | <#
20 | Year = 2021
21 | Month = 5
22 | Days = 31
23 | #>
24 | ````
25 | Here I'm using the Method DayInMonth. The syntax is the following
26 |
27 | ````powershell
28 | [datetime]::DaysInMonth
29 | OverloadDefinitions
30 | -------------------
31 | static int DaysInMonth(int year, int month)
32 | ````
33 |
34 | ## Calculate the number of day in the month
35 |
36 | ````powershell
37 | $list = @() # initialization of the array
38 | $list = ( 1..$days | foreach { Get-Date -Month $Month -Year $Year -Day $_} )
39 | <#
40 | samedi 1 mai 2021 08:49:36
41 | dimanche 2 mai 2021 08:49:36
42 | lundi 3 mai 2021 08:49:36
43 | mardi 4 mai 2021 08:49:36
44 | mercredi 5 mai 2021 08:49:36
45 | jeudi 6 mai 2021 08:49:36
46 | vendredi 7 mai 2021 08:49:36
47 | samedi 8 mai 2021 08:49:36
48 | dimanche 9 mai 2021 08:49:36
49 | lundi 10 mai 2021 08:49:36
50 | mardi 11 mai 2021 08:49:36
51 | mercredi 12 mai 2021 08:49:36
52 | jeudi 13 mai 2021 08:49:36
53 | vendredi 14 mai 2021 08:49:36
54 | samedi 15 mai 2021 08:49:36
55 | dimanche 16 mai 2021 08:49:36
56 | lundi 17 mai 2021 08:49:36
57 | mardi 18 mai 2021 08:49:36
58 | mercredi 19 mai 2021 08:49:36
59 | jeudi 20 mai 2021 08:49:36
60 | vendredi 21 mai 2021 08:49:36
61 | samedi 22 mai 2021 08:49:36
62 | dimanche 23 mai 2021 08:49:36
63 | lundi 24 mai 2021 08:49:36
64 | mardi 25 mai 2021 08:49:36
65 | mercredi 26 mai 2021 08:49:36
66 | jeudi 27 mai 2021 08:49:36
67 | vendredi 28 mai 2021 08:49:36
68 | samedi 29 mai 2021 08:49:36
69 | dimanche 30 mai 2021 08:49:36
70 | lundi 31 mai 2021 08:49:36
71 | #>
72 | ````
73 |
74 |
75 |
76 | ## Identify days corresponding to $FirstDayOfWeek
77 |
78 | ````powershell
79 | $list | Where { $_.DayofWeek -like $FirsDayofweek }
80 | <#
81 | dimanche 2 mai 2021 08:49:36
82 | dimanche 9 mai 2021 08:49:36
83 | dimanche 16 mai 2021 08:49:36
84 | dimanche 23 mai 2021 08:49:36
85 | dimanche 30 mai 2021 08:49:36
86 | #>
87 | ````
88 |
89 | ## and finally, count the number of weeks
90 |
91 | ````powershell
92 | $Weeks = ($list | Where { $_.DayofWeek -like $FirsDayofweek }).Count
93 | Write-Host " there are $Weeks in this month" -ForegroundColor Green
94 | there are 5 in this month
95 | ````
96 |
97 |
98 | Hope this help
99 |
--------------------------------------------------------------------------------
/Tips - Create a .csv file from a PSCustomObject with Multivalues parameters.ps1:
--------------------------------------------------------------------------------
1 | # The Pb : A PSCustom Object contains a multivalues property
2 | $info = [pscustomobject]@{
3 | First = "jason"
4 | Last = "Bourne"
5 | Location = @('US','FR')
6 | }
7 | $info
8 |
9 | # If i want to export the object in a .csv file
10 | $info |Export-Csv -Path C:\Temp\test.csv
11 | # and use it later
12 | Import-Csv -Path C:\temp\test.csv
13 | # Biiip, Location is shown as a [System.object]
14 |
15 | ################################################
16 | # The question is : How to solve this ? #
17 | ################################################
18 |
19 |
20 | # Solution 1 - Modify the PSCustomObject with Out-String cmdlet and Trim() method :
21 | $info1 = [pscustomobject]@{
22 | First = "Jason"
23 | Last = "Bourne"
24 | Location = (@('US','FR')|Out-String).Trim()
25 | }
26 | $info1
27 |
28 | # Let's see that is the content for the different part of the property
29 | @('US','FR').trim()
30 | (@('US','FR')| Out-String)
31 | @('US','FR') | Get-Member # [System.String]
32 | (@('US','FR')| Out-String) | Get-Member # [System.String]
33 | # It seems to be no change, but on the second we have the method Trim()
34 |
35 | (@('US','FR')| Out-String).Trim() # this is exactly that we're looking for.
36 |
37 | # Solution 2 : Modify the PSCustomObject with -Join and the separate caracter.
38 | $info2 = [pscustomobject]@{
39 | First = "Jason"
40 | Last = "Bourne"
41 | Location = (@('US','FR') -join ',')
42 | }
43 | $info2
44 |
45 | (@('US','FR') -join ',')
46 | (@('US','FR') -join ',') | Get-Member # always [System.String]
47 |
48 | $info1
49 | $info2
50 | # note that the output is slightly different ... but I don't know why.
51 |
52 |
53 |
--------------------------------------------------------------------------------
/Tips - Create a Scheduled Task.md:
--------------------------------------------------------------------------------
1 | In a Active Directory Domain context there are several ways to create Scheduled tasks on computers.
2 | The question in not "is it a GUI way or a Powershell way". OK, I concede that i prefer the powershell way.
3 |
4 | In this post, i will mention mainly security considerations and maintaining in operational conditions.
5 |
6 | # CONSIDERATIONS
7 |
8 | ## Pre-requisite
9 | - AD In functional level > 2012 (AD Schema must be 52)
10 | - The first master root key (KDS Root Key) for Active Directory has been created on the forest
11 |
12 | ## Security considerations
13 | - Scheduled Task should run respecting the principle of least privilege
14 | - Script, executing by the Scheduled Task, should run respecting the principle of least privilege
15 |
16 | ## Maintaining in operational conditions
17 | - Reduce to the minimal some boring - but necessary - administratives tasks like changing regularly the accounts password.
18 |
19 | If you have an Active Directory with functional level 2012, then you can use G.M.S.A. (Group Managed Service Accounts). A GMSA is a new AD Object, and this object can be use to run a Windows Service on a remote computer of the AD Domain.
20 | It respect the principle of least privilege on the remote computer. Technically the remote computer has delegation for renew the GMSA password with the Active Directory. The settings about password are in the GMSA properties.
21 | A GMSA have also a property name ApplyTo. This property could be a AD Computer or a Global Security Group populate with AD Computers. If you use this last way you can use the same GMSA on several computers.
22 |
23 | But do you know that a GMSA could be use also with scheduled Task respecting the principle of least privilege too ?
24 |
25 |
26 | # Let's go to do this, with powershell
27 | ## Create a Global Security Group
28 | ````Powershell
29 | $NewADServiceAccountArgs = @{
30 | Name = "GMSA-ScheduledTasks"
31 | Description = "GMSA use tu run scheduled Tasks"
32 | DNSHostName = "GMSA-ScheduledTasks.myDomain.com"
33 | Enabled = $true
34 | PrincipalsAllowedToRetrieveManagedPassword = "SG-ScheduleTask" # this is the previously created Global Security Group
35 | }
36 | <# Note :
37 | - I don't define Path parameter, the default path in Active Directory is "Managed Service Accounts" builtin container but you can use an OU if you want.
38 | - I don't define ManagedPasswordIntervalInDays parameter. By default, it value is 30 Days. The password change interval can only be set during creation.
39 | If you need to change the interval, you must create a new gMSA and set it at creation time.
40 | #>
41 | ````
42 | ## Deploy the GMSA on a remote computer (locally)
43 | ````powershell
44 | # To Deploy on a computer
45 | Install-ADServiceAccount -AuthType=0 -Identity "GMSA-ScheduledTasks"
46 | # To check if GMSA is deployed on a computer
47 | Test-ADServiceAccount "GMSA-ScheduledTasks" # this return True if it's OK
48 | ````
49 | ## Deploy the GMSA on remote computers (remotly)
50 | ````powershell
51 | $Servers = Get-ADGroupMember -Identity "SG-ScheduleTask" | Select-Object Name
52 | foreach ($server in $server)
53 | {
54 | Invoke-Command -ComputerName $server.Name -ScriptBlock {
55 | Install-ADServiceAccount -AuthType=0 -Identity "GMSA-ScheduledTasks";
56 | Test-ADServiceAccount "GMSA-ScheduledTasks"
57 | }
58 | }
59 | ````
60 |
61 | ## Create a Scheduled Task on the computer
62 | This couldn't be done by GUI, only powershell is possible. Normal, we can't know the GMSA password, only the concerning computer (s) know it.
63 | First you can enter in a PSSession
64 | Example in action :
65 | ````powershell
66 | Enter-PSSession -ComputerName "RemoteComputer"
67 | $Action = New-ScheduledTaskAction "c:\scripts\MyScript.ps1" # this script contains the code seen in "Deploy the GMSA on a remote computer (locally)" section
68 | $Trigger = New-ScheduledTaskTrigger -At 23:00 -Daily
69 | $Principal = New-ScheduledTaskPrincipal -UserID MyDomain\GMSA-ScheduledTask$ -LogonType Password
70 | # nota 1 : the $ at the end of the GMSA Account, like a computer account
71 | # Nota 2 : The value "password" in the parameter logonType is not the password. It just requires the scheduled task to receive the current password of the gMSA of a domain controller
72 | Register-ScheduledTask "MyScheduledTask" -Action $Action -Trigger $Trigger -Principal $Principal
73 | ````
74 |
75 | # SYNTHESIS
76 | By this way, it's very easy to have a single account for the different tasks scheduled on different computers whose password is maintained by design securely by the AD.
77 |
78 | **Secure** - **Automatically maintained and managed**, what more could you wish for ?
79 |
--------------------------------------------------------------------------------
/Tips - Create a bunch of dummy files.md:
--------------------------------------------------------------------------------
1 | # Create a bunch of Dummy files
2 |
3 | ## Create empty file with defined size
4 |
5 | ````powershell
6 | $File = New-Object -TypeName System.IO.FileStream c:\temp\test.txt, Create, ReadWrite
7 | # Here, we define the size of the file
8 | $File.SetLength(10Mb)
9 | ````
10 |
11 | ## closing and creating file
12 |
13 | ````powershell
14 | $File.Close()
15 | ````
16 |
17 |
18 | ## Create a bunch of file with different sizes and in a different quantity
19 |
20 | ````powershell
21 | $PathForDummyFiles = "c:\temp"
22 | $NumberOf100KB = 200
23 | $NumberOf1MB = 100
24 | $NumberOf10MB = 10
25 | $NumberOf100MB = 5
26 |
27 | # Creating 1 KB files
28 | for ($i = 1; $i -lt $NumberOf100KB; $i++)
29 | {
30 | $File = New-Object -TypeName System.IO.FileStream "$PathForDummyFiles\test100KB-$I.txt", Create, ReadWrite
31 | $File.SetLength(100KB)
32 | $File.Close()
33 | }
34 |
35 | # Creating 1 MB files
36 | for ($i = 1; $i -lt $NumberOf1MB; $i++)
37 | {
38 | $File = New-Object -TypeName System.IO.FileStream "$PathForDummyFiles\test1MB-$I.txt", Create, ReadWrite
39 | $File.SetLength(1MB)
40 | $File.Close()
41 | }
42 |
43 | # Creating 10 MB files
44 | for ($i = 1; $i -lt $NumberOf10MB; $i++)
45 | {
46 | $File = New-Object -TypeName System.IO.FileStream "$PathForDummyFiles\test10MB-$I.txt", Create, ReadWrite
47 | $File.SetLength(10MB)
48 | $File.Close()
49 | }
50 |
51 | # Creating 100 MB files
52 | for ($i = 1; $i -lt $NumberOf100MB; $i++)
53 | {
54 | $File = New-Object -TypeName System.IO.FileStream "$PathForDummyFiles\test100MB-$I.txt", Create, ReadWrite
55 | $File.SetLength(100MB)
56 | $File.Close()
57 | }
58 | ````
59 |
60 | In this sample to create 311 files, this take 291 ms. To create 2146 files, this takes 2,2 sec.
61 |
62 | ## We can also use another way to do the same thing
63 |
64 | ````powershell
65 | $Path = "C:\temp2"
66 | $File = "MyFile.txt"
67 | $MyArray = New-Object -TypeName Byte[] -ArgumentList 10Kb
68 | $OBJ = New-Object -TypeName System.Random
69 | $OBJ.NextBytes($MyArray)
70 | Set-Content -Path $Path\$File -Value $MyArray -Encoding Byte
71 | ````
72 |
73 | there is no real difference in execution time. However, you will notice that the files are not empty but filled with weird characters
74 |
75 | # we can also use the legacy DOS command fsutil
76 |
77 |
78 | ````DOS
79 | fsutil file createnew C:\Temp2\test1.txt 10000
80 | ````
81 |
82 | >[Nota] : the size is in Bytes. It's not really obvious to play with this.
83 |
84 |
85 |
86 | Hope this will be useful
87 |
--------------------------------------------------------------------------------
/Tips - Create registry values.ps1:
--------------------------------------------------------------------------------
1 | # RUN AS ADMINISTRATOR if you modify HKLM due to security restrictions
2 |
3 | # New key : complete Path
4 | New-Item -Path 'HKCU:\SOFTWARE\Test'
5 |
6 | # New Key: Path + Name
7 | New-Item -Path 'HKCU:\SOFTWARE\Test' -Name 'App'
8 | Get-ChildItem -Path 'HKCU:\SOFTWARE\Test'
9 |
10 | # Replace Existing Key
11 | New-Item -Path 'HKCU:\SOFTWARE\Test' -Force
12 | Get-ChildItem -Path 'HKCU:\SOFTWARE\Test'
13 | <# With the parameter -Force, it replace the existing key
14 | Note that All Items in this key disapear
15 | #>
16 |
17 | # Create and assign a Value
18 | New-Item -Path 'HKCU:\SOFTWARE\Test\Website' -Value 'Https://www.mywebsite.com'
19 | # Note that the default key name is "Default"
20 | Get-Item -Path 'HKCU:\SOFTWARE\Test\Website'
21 | Get-ItemProperty -Path 'HKCU:\SOFTWARE\Test\Website'
22 |
23 | # Rename a value
24 | Rename-ItemProperty -Path 'HKCU:\SOFTWARE\Test\Website' -Name "(default)" -NewName "WebSite"
25 | Get-ItemProperty -Path 'HKCU:\SOFTWARE\Test\Website'
26 | # Note that the "(default)" key still exists but the value is set to $null
27 |
28 | # Add another key/value
29 | New-ItemProperty -Path 'HKCU:\SOFTWARE\Test\Website' -Name "website2" -Value 'Https://www.mywebsite2.com'
30 |
31 | # Define the PropertyType of a value
32 | $Name = "Version"
33 | $Value = "1.0"
34 | $PropertyType = "DWORD"
35 | New-ItemProperty -Path 'HKCU:\SOFTWARE\Test\Website' -Name $Name -Value $Value -PropertyType $PropertyType
36 |
37 | # Testing the existence of the registry Key and the value
38 | $RegistryPath = 'HKCU:\SOFTWARE\Test\Website'
39 | $Name = "Version"
40 | $Value = "2"
41 | if (Test-Path $RegistryPath)
42 | {
43 | New-ItemProperty -Path $registryPath -Name $name -Value $value -PropertyType DWORD -Force
44 | Write-host "The Key is existing... and the value updated" -ForegroundColor Green
45 | }
46 | else { # registry Key doesn't exist
47 | New-Item -Path $registryPath -Force | Out-Null # to avoid console output
48 | New-ItemProperty -Path $registryPath -Name $name -Value $value -PropertyType DWORD -Force | Out-Null
49 | }
50 | Get-ItemProperty -Path $RegistryPath -Name $Name
51 |
52 | # Tips to save time
53 | Set-Location -Path 'HKCU:\SOFTWARE\Test\'
54 | # and now use the cmdlet Pop-Location to return to the starting working location.
55 | Pop-Location
56 | New-Item -Path .\Website\ -Name 'AnotherKey'
57 |
--------------------------------------------------------------------------------
/Tips - DeleteFilesOlderThanXDays.ps1:
--------------------------------------------------------------------------------
1 | #Shows what files would be deleted from c:\windows\temp (older than 10 days) if run. Remove 'whatif' to implement.
2 |
3 | filter FileAge($days) { if ( ($_.LastWriteTime -le (Get-Date).AddDays($days * -1) )) { $_ } }
4 |
5 | get-childitem c:\windows\temp -recurse | FileAge 10 | del -whatif
--------------------------------------------------------------------------------
/Tips - Explanations about different outputs.md:
--------------------------------------------------------------------------------
1 | # Explanations about different outputs
2 |
3 |
4 | what is the difference between
5 | ````(Get-ComputerInfo).csprocessors````
6 | and ````Get-ComputerInfo | Select-Object -Property csprocessors```` ?
7 |
8 | Indeed, the 2 previous commands show a different outputs. Let's show the output for each command.
9 |
10 | ````powershell
11 | (Get-ComputerInfo).csprocessors
12 |
13 | Name : Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz
14 | Manufacturer : GenuineIntel
15 | Description : Intel64 Family 6 Model 94 Stepping 3
16 | Architecture : x64
17 | AddressWidth : 64
18 | DataWidth : 64
19 | MaxClockSpeed : 4001
20 | CurrentClockSpeed : 4001
21 | NumberOfCores : 4
22 | NumberOfLogicalProcessors : 8
23 | ProcessorID : BFEBFBFF000506E3
24 | SocketDesignation : LGA1151
25 | ProcessorType : CentralProcessor
26 | Role : CPU
27 | Status : OK
28 | CpuStatus : Enabled
29 | Availability : RunningOrFullPower
30 | ````
31 |
32 | and
33 |
34 | ````powershell
35 | (Get-ComputerInfo).csprocessors
36 |
37 |
38 | Name : Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz
39 | Manufacturer : GenuineIntel
40 | Description : Intel64 Family 6 Model 94 Stepping 3
41 | Architecture : x64
42 | AddressWidth : 64
43 | DataWidth : 64
44 | MaxClockSpeed : 4001
45 | CurrentClockSpeed : 4001
46 | NumberOfCores : 4
47 | NumberOfLogicalProcessors : 8
48 | ProcessorID : BFEBFBFF000506E3
49 | SocketDesignation : LGA1151
50 | ProcessorType : CentralProcessor
51 | Role : CPU
52 | Status : OK
53 | CpuStatus : Enabled
54 | Availability : RunningOrFullPower
55 | ````
56 |
57 | The first one expands the value and the second one does not. The curly braces on the second one means its an **array** and in this case it is an array of objects.
58 |
59 | Nota : If you we use in the second command the parameter ````-ExpandProperty````, the output will be the same as the first command.
60 |
61 | ````powershell
62 | Get-ComputerInfo | Select -ExpandProperty CsProcessors
63 |
64 |
65 | Name : Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz
66 | Manufacturer : GenuineIntel
67 | Description : Intel64 Family 6 Model 94 Stepping 3
68 | Architecture : x64
69 | AddressWidth : 64
70 | DataWidth : 64
71 | MaxClockSpeed : 4001
72 | CurrentClockSpeed : 1700
73 | NumberOfCores : 4
74 | NumberOfLogicalProcessors : 8
75 | ProcessorID : BFEBFBFF000506E3
76 | SocketDesignation : LGA1151
77 | ProcessorType : CentralProcessor
78 | Role : CPU
79 | Status : OK
80 | CpuStatus : Enabled
81 | Availability : RunningOrFullPower
82 | ````
83 |
84 | Hope this help to understand how Powerwhell works.
85 |
--------------------------------------------------------------------------------
/Tips - Find the total number of servers in each Organizational Unit.ps1:
--------------------------------------------------------------------------------
1 | # find the total number of servers in each Organizational Unit
2 | # source : https://hkeylocalmachine.com/?p=809
3 |
4 | $StartTime = Get-Date
5 |
6 | # Get all servers from AD
7 | $Servers = Get-ADComputer -Filter {OperatingSystem -like "*server*"}
8 |
9 | # Loop through each server found
10 | foreach ($Server in $Servers) {
11 |
12 | # Strip out OU from DN
13 | $dnSplit = $Server.DistinguishedName -split ",";
14 | $Server.ou = $dnsplit[1..$dnSplit.count] -join ",";
15 | }
16 |
17 | # Save results into a table and sort from highest to lowest
18 | $ResultTable = $Servers | Group-Object -NoElement -Property ou | Sort-Object count -Descending
19 |
20 | # Record end of test 1
21 | $EndTime = Get-Date
22 |
23 | # Calculate total test time
24 | $TestTime = New-TimeSpan -Start $StartTime -End $EndTime;
25 |
26 | #---------- Output ----------#
27 | # Show results of times taken to perform each test
28 | Write-Host -ForegroundColor Cyan "Test :" $TestTime.totalseconds "seconds";$TestTime
--------------------------------------------------------------------------------
/Tips - Get the week of the month or the quarter from a Date.md:
--------------------------------------------------------------------------------
1 |
2 | # The challenge
3 |
4 | You must determine the week number because you would like to do something in Week1, otherthing in week2 ...
5 |
6 | There is a class named WIN32-LocalTime to do this
7 |
8 |
9 | ````powershell
10 | Get-CimInstance -ClassName WIN32_LocalTime
11 |
12 | Day : 7
13 | DayOfWeek : 1
14 | Hour : 12
15 | Milliseconds :
16 | Minute : 2
17 | Month : 3
18 | Quarter : 1
19 | Second : 7
20 | WeekInMonth : 2
21 | Year : 2022
22 | PSComputerName :
23 | ````
24 |
25 | ## Get only the Week Number in the month
26 |
27 | ````powershell
28 | (Get-CimInstance -ClassName WIN32_LocalTime).WeekInMonth
29 | 2
30 | ````
31 |
32 | As you can see in the first previous cmdlet, by the same way, you could get the ***quarter*** on the year
33 |
34 | Hope this Help
--------------------------------------------------------------------------------
/Tips - HardDiskUsage.ps1:
--------------------------------------------------------------------------------
1 | # Initialise machine name
2 | $server = $env:COMPUTERNAME
3 |
4 | # Get fixed drive info
5 | $disks = Get-WmiObject -ComputerName $server -Class Win32_LogicalDisk -Filter "DriveType = 3"
6 |
7 | foreach($disk in $disks)
8 | {
9 | $deviceID = $disk.DeviceID
10 | [float]$size = $disk.Size
11 | [float]$freespace = $disk.FreeSpace
12 |
13 | $percentFree = [Math]::Round(($freespace / $size) * 100, 2)
14 | $sizeGB = [Math]::Round($size / 1073741824, 2)
15 | $freeSpaceGB = [Math]::Round($freespace / 1073741824, 2)
16 |
17 | Write-Host "server,DeviceID,SizeGB,FreeSpaceGB,PercentFree"
18 | Write-Host "$server,$deviceID,$sizeGB,$freeSpaceGB,$percentFree"
19 | }
20 |
--------------------------------------------------------------------------------
/Tips - How to Rename a bunch of files.md:
--------------------------------------------------------------------------------
1 | # How to rename a bunch of files
2 |
3 | ## the case
4 | You have a large number of files that are all named something like this
5 | ````
6 | James Bob- Certificate - 09012021
7 | Johnson Steve - Certificate-09012021
8 | Thompson John - Certificate- 09012021
9 | ````
10 | ... and you would like to rename them kike the following :
11 |
12 | ````
13 | James Bob
14 | Johnson Steve
15 | Thompson John
16 | ````
17 |
18 | How to achieve this goal ?
19 |
20 | ## The solution
21 | For this i would simulate the case using an array of names. This is useful to find the appropriate way to reach the goal.
22 |
23 |
24 | ````Powershell
25 | $a="James Bob- Certificate - 09012021","Johnson Steve - Certificate-09012021","Thompson John - Certificate- 09012021"
26 | $a
27 | James Bob- Certificate - 09012021
28 | Johnson Steve - Certificate-09012021
29 | Thompson John - Certificate- 09012021
30 | ````
31 | As you can see, this reflects the situation
32 |
33 | As a first step, I'm using the ````split```` separator withe the separator character ````"-"````
34 | The result will be :
35 |
36 | ````powershell
37 | $a | foreach{$_.split("-")}
38 | James Bob
39 | Certificate
40 | 09012021
41 | Johnson Steve
42 | Certificate
43 | 09012021
44 | Thompson John
45 | Certificate
46 | 09012021
47 | ````
48 | As you can see, this split the array on the separator character.
49 |
50 | As a second step, I'm using the ````[0]```` to return only the fisrt string of the split
51 |
52 |
53 | ````powershell
54 | $a | foreach{$_.split("-")[0]}
55 | James Bob
56 | Johnson Steve
57 | Thompson John
58 | ````
59 |
60 | As you can see (yes, yes look carefully :-) ), there are still some trailing spaces at the end of each lines
61 |
62 | as the third step, I'm removing these trailing space using de ````TrimEnd```` method
63 |
64 | > [Nota : ] if you have some trailing space at the beginning, you could use the ````Trim```` Method.
65 |
66 | ````powershell
67 | $a | Foreach {$_.split("-")[0].trimend()}
68 | James Bob
69 | Johnson Steve
70 | Thompson John
71 | ````
72 | As you can see, there is no trailing space, and we have achieve the goal.
73 |
74 | ## Now apply the method to a bunch of files
75 |
76 | ````powershell
77 | $AllFiles = Get-ChildItem -Path \\path\to\files\
78 | foreach ($File in $AllFiles)
79 | {
80 | $ActualName = $File.Name
81 | # Nota 1 : The Path parameter must be the FullName of the file
82 | # Nota 2 : The NewName parameter must be the Name with the extension
83 | # but we haven't it, resulting of using the split method and keep only the first string using [0]
84 | # Then, we're adding the actual extension to the NewName, to reach this goal
85 | Rename-Item -Path $File.FullName -NewName "$($ActualName.split("-")[0].trimEnd())$($file.Extension)"
86 | }
87 |
88 | # another way, it to use the following :
89 | Get-ChildItem -Path \\path\to\files\ |
90 | Foreach-Object
91 | {
92 | $ActualName = $_.Name
93 | # Nota 1 : The Path parameter must be the full name of the file
94 | # Nota 2 : The NewName parameter must be the Name with the extension
95 | # but we haven't it, resulting of using the split method and keep only the first string using [0]
96 | # Then we're adding the actual extension to the NewName, to reach this goal
97 | Rename-Item -Path $_.FullName -NewName "$($ActualName.split("-")[0].trimEnd())$($_.Extension)"
98 | }
99 | ````
100 |
101 | ### Some words about differences between the 2 ways
102 |
103 | The ````Foreach-Object```` is **more efficient in terms of memory utilization** but **lacks the performance**
104 |
105 | The ````foreach```` loop control is **more efficient in terms of performance** but might **utilize more memory** depending on the objects you are looping through.
106 |
107 | You can easily understand this because, the Foreach-Object is used via Pipeline that means one object is passed to this at a time so there is no need to store anything when you are processing huge data.
108 | When it comes to foreach loop control statement, data should be in one variable which you will loop through one at a time so requires some space in memory.
109 |
110 | ## The final word
111 |
112 | I hope this could be useful to everyone read (and understand :-) ) this.
113 |
--------------------------------------------------------------------------------
/Tips - How to Send a Beautiful Email Report (html) with result of differents queries.md:
--------------------------------------------------------------------------------
1 | # How to send a beautiful report - html format - with the result of differents queries
2 |
3 | To illustrate the methodogy, I'm using a simple sample.
4 |
5 | ## Step 1 : Query informations an put in a var
6 |
7 | below it's a fictional sample. TO illustrate this, I'm puting the result of the query in a var like a Csv.
8 |
9 | ````powershell
10 | $Query = @"
11 | Item1, A
12 | Item2, B
13 | "@ | ConvertFrom-Csv
14 | ````
15 |
16 | As you can see there are several value/key in my $query var
17 |
18 | ````powershell
19 | $Query
20 | Item1 A
21 | ----- -
22 | Item2 B
23 | ````
24 |
25 | ## Step 2 : Unit treatment
26 |
27 | Now, unit treatment for each pair Key/value in my $Query var
28 |
29 | ````powershell
30 | $Body =@() # intialization of hash table array
31 | $Body += foreach ($Item in $query)
32 | {
33 | # do something and put the result in a [PSCustomObject]
34 | [PSCustomObject]@{
35 | Prop1 = "value1"
36 | Prop2 = "value2"
37 | Prop3 = "Value3"
38 | }
39 | }
40 | ````
41 |
42 | Each turn of the ````Foreach```` loop, I'm building a ````[PSCustomObject]```` and feed the result in a Var [Array] using ````+=````.
43 |
44 | Here, named $Body because this represent the Body of our future mail report
45 |
46 | Take a look on $Body Var
47 |
48 | ````powershell
49 | $Body.GetType()
50 | IsPublic IsSerial Name BaseType
51 | -------- -------- ---- --------
52 | True True Object[] System.Array
53 | ````
54 |
55 | As we can see, the $Body is an [Array]
56 |
57 | ## Step 3 - Preparing format for the mail
58 |
59 | ### Step 3.1 - Preparing Header
60 |
61 | The header define some html properties is an internal CSS. This allow to have a beautiful html report. Customize it as you want.
62 |
63 | ````powershell
64 | $Header = @"
65 |
72 | "@
73 | ````
74 |
75 | As you can see, the $Header is an Here-String
76 |
77 | ````powershell
78 | $Header.gettype()
79 | IsPublic IsSerial Name BaseType
80 | -------- -------- ---- --------
81 | True True String System.Object
82 | ````
83 |
84 | ### Step 3.2 : Preparing Splat to Send mail message
85 |
86 | ````powershell
87 | $SendMailParams = @{
88 | From = "it.admin@contoso.com"
89 | To = "it.manager@contoso.com"
90 | Subject = "Daily report"
91 | Body = $Body
92 | BodyAsHtml = $true
93 | SmtpServer = "smtp.contoso.com"
94 | }
95 | ````
96 |
97 | A splat is a Hash table
98 |
99 | ````powershell
100 | $SendMailParams.gettype()
101 | IsPublic IsSerial Name BaseType
102 | -------- -------- ---- --------
103 | True True Hashtable System.Object
104 | ````
105 |
106 | Why use Splat ? When and what info put in the Splat ?
107 |
108 | - When there is more than 4 parameters (mandatory or not)
109 | - What : All commons parameters, mandatory or not, put them in a splat and pass specific value for one or more parameters out of the splat
110 | - Why : a splat is easy to read and give short cmdline to use. Very practical and useful
111 |
112 | ## Step 4 : Send Mail Message report
113 |
114 | ### without Additional parameter
115 |
116 | ````powershell
117 | Send-MailMessage @SendMailParams
118 | ````
119 |
120 | ### with Additional parameter (s)
121 |
122 | ````powershell
123 | Send-MailMessage @SendMailParams -Cc "additionalUser.contoso.com"
124 | ````
125 |
--------------------------------------------------------------------------------
/Tips - How to Set Up Powershell to run in a secure Environment.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | The Context :
3 | > Servers and Admin Servers/Workstations haven't any Internet Access
4 | > You would like to have external help updated ( a .xml file) for these computers
5 | > You would like to have only Approval modules installed on your servers
6 | > You would like to manage Powershell activity
7 |
8 | Solution :
9 | > Create a SBM share to store help files and Modules
10 |
11 | > Put all reference for modules and help files in this share, and feed it with the corresponding files
12 |
13 | > Set up Servers and Admin Servers/workstation to look at this share to update their modules and help files
14 |
15 | > Set up Servers and Admin Servers/workstation to follow your Policy about Powershell
16 |
17 | #>
18 |
19 | #region Step 1 - Create a SBM share to store help files and Modules
20 | <# See : Tips - How to Update-Help in a secure Environment
21 | It's exactly the same for modules and Help File
22 | You can use one SMB share for all or 2 differents SMB Shares
23 | #>
24 | #endregion
25 |
26 | #region Step 2 - Put all reference for modules and help files in this share, and feed it with the corresponding files
27 | <# See : Tips - How to Update-Help in a secure Environment
28 | It's exactly the same for modules and Help File
29 | #>
30 | #endregion
31 |
32 | #region Step 3 - Set up Servers and Admin Servers/workstation to look at this share to update their modules and help files
33 | <#
34 | To do this, a simple and secure way in to set a Group Policy Object (GPO)
35 |
36 | Computer Configuration > Policies > Administratives Templates > Windows Components > Windows Powershell
37 | > Set the default source path for Update-Help
38 | EnableUpdateHelpDefaultSourcePath = True
39 | Path = "\\server\PSHelpFile"
40 | Formally :
41 | HKLM\Software\Policies\Microsoft\Windows\PowerShell\UpdatableHelp!EnableUpdateHelpDefaultSourcePath
42 |
43 | > Turn on Module Logging
44 | EnableModuleLogging = True
45 | ModuleName = *
46 | Formally :
47 | HKLM\Software\Policies\Microsoft\Windows\PowerShell\ModuleLogging!EnableModuleLogging
48 |
49 | >Turn on PowerShell Script Block Logging
50 | EnableScriptBlockLogging = true
51 | EnableScriptBlockInvocationLogging = true
52 | Formally :
53 | HKLM\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging!EnableScriptBlockLogging
54 | HKLM\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging!EnableScriptBlockInvocationLogging
55 |
56 | > Turn on PowerShell Transcription
57 | EnableTranscripting = true
58 | EnableInvocationHeader= true
59 | OutputDirectory = C:\PSLogs
60 | Formally
61 | HKLM\Software\Policies\Microsoft\Windows\PowerShell\Transcription!EnableTranscipting
62 | HKLM\Software\Policies\Microsoft\Windows\PowerShell\Transcription!EnableInvocationHeader
63 |
64 |
65 | > Turn on Script Execution
66 | EnableScripts = true
67 | ExecutionPolicy = RemoteSigned
68 | Formally
69 | HKLM\Software\Policies\Microsoft\Windows\PowerShell!EnableScripts
70 | HKLM\Software\Policies\Microsoft\Windows\PowerShell!ExecutionPolicy
71 |
72 | A next step to perform is to set up the Internal Share as a "trusted" PSRepository and Set up the
73 | > Create a .pS1 file named Microsoft.PowerShell_profile.ps1 (an another named Microsoft.PowerShellISE_profile.ps1 for ISE, and another Microsoft.VSCode_profile.PS1 for VSCode).
74 | > Feed the .ps1 file with the following
75 | Import-Module PowerShellGet
76 | $Path = '\\Server\Share\PSInternalRepo'
77 | $Myrepository = @{
78 | Name = 'PSInternalRepository'
79 | SourceLocation = $Path
80 | PublishLocation = $Path
81 | InstallationPolicy = 'Trusted'
82 | }
83 | Register-PSRepository @Myrepository
84 | Put this .ps1 file in '\\Server\Share\PSProfiles'
85 | Nota : Some other things could populate the profile files as your need.
86 |
87 | > Create another .ps1 script Profiles.ps1 an put it on a DC in the following %SystemRoot%\SYSVOL\sysvol\\Policies\{GUID}\User\Scripts\Logon
88 | $Source = '\\Server\Share\PSProfiles'
89 | $Destination = 'C:\users\*\WindowsPowerShell\'
90 | Get-ChildItem $Destination | ForEach-Object {Copy-Item -Path $Source -Destination $_ -Force -Recurse}
91 |
92 | > Force the application of this last .ps1 script by the logon script in a GPO
93 | Computer Configuration > Policies > Windows Settings > Scripts
94 | Name : Profile.ps1
95 |
96 | At this step, all users receive by a GPO a "Compliance" profile that Register-PSrepository with your Internal Repository
97 | #>
98 | #endregion
--------------------------------------------------------------------------------
/Tips - How to Update-Help in a secure Environment.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | The Context :
3 | > Servers and Admin Servers/Workstations haven't any Internet Access
4 | > You would like to have external help updated ( a .xml file) for these computers
5 |
6 | Solution :
7 | > Create a SBM share to store help files
8 |
9 | > Put all reference for help files in this share, and feed it with the corresponding help files
10 |
11 | > Set up Servers and Admin Servers/workstation to look at this share to update their help
12 |
13 | #>
14 |
15 |
16 | # In action
17 |
18 | #region Step one : Create a smb Share and set up Share access And NTFS Permissions
19 | $SMBShareParams=@{
20 | Path = "\\server\PSHelpFile"
21 | Name = "PSHelpFile"
22 | Description = "Help file for PS Modules"
23 | ReadAccess = Everyone
24 | FullAccess = "Domain\Domain Admins" # replace by you appropriate Admin Group for the domain
25 | }
26 | New-SmbShare @SMBShareParams
27 | $NTFSAccessUsersParams = @{
28 | Path = "\\server\PSHelpFile"
29 | Account = "Domain\Domain Users"
30 | AccessRights = ReadAndExecute
31 | AccessType = Allow
32 | }
33 | Add-NTFSAccess @NTFSAccessAdminParams
34 | $NTFSAccessUsersParams = @{
35 | Path = "\\server\PSHelpFile"
36 | Account = "Domain\Domain Admins" # replace by you appropriate Admin Group for the domain
37 | AccessRights = FullControl
38 | AccessType = Allow
39 | }
40 | Add-NTFSAccess @NTFSAccessAdminParams
41 | #endregion
42 |
43 | #region Step 2 - Put all all help files in this share
44 | <#
45 | From a Admin server/Workstation where all modules you use in your environment run the following :
46 | This Will export in a .xml file the file containing all references for the module you use
47 | #>
48 | $ModuleList = "x:\PSHelpFiles\ModuleList.xml"
49 | Get-Module -ListAvailable | Export-Clixml -Path $ModuleList
50 |
51 | # Then go to a computer that have access to the Internet ans run the following
52 | $ModuleList = "x:\PSHelpFiles\ModuleList.xml"
53 | Save-Help -DestinationPath $ModuleList
54 | <#Assume that servers can't access to the Internet and can't feed directly the share
55 | > Save the files on a local folder then copy it to a removable media.
56 | > Finally, transport the removable media back to server hosting the share.
57 |
58 | Nota 1 : for the next time you perform this action, you will perform only "Update-Help" instead of "Save-Help"
59 | In this context, you can't update by a scheduled scipt because, it requires that the removable media be present
60 | Don't forget to do this update task somtetimes.
61 |
62 | Nota 2 : You can Save-Help for different Language like this:
63 | Save-Help -UICulture de-DE, en-US, fr-FR, ja-JP -DestinationPath $ModuleList
64 |
65 | Nota 3 : You can Save-Help for one or more specifics modules like this :
66 | Save-Help -Module Microsoft.PowerShell* -DestinationPath $ModuleList
67 |
68 |
69 | #>
70 | $ModuleList = "x:\PSHelpFiles"
71 | $PSHelpFile = "\\server\PSHelpFile"
72 | Copy-Item -Path $ModuleList -Destination $PSHelpFile -Recurse -Force
73 | #endregion
74 |
75 | #region Step Three : Set up Servers and Admin Servers/workstation to look at this share to update their help
76 | <#
77 | To do this, a simple and secure way in to set a Group Policy
78 | Computer Configuration > Policies > Administratives Templates > Windows Components > Windows Powershell
79 | > Set the default source path for Update-Help
80 | Enable = True
81 | Path = "\\server\PSHelpFile"
82 | Formally : HKLM\Software\Policies\Microsoft\Windows\PowerShell\UpdatableHelp!EnableUpdateHelpDefaultSourcePath
83 | #>
84 | #endregion
85 |
86 |
--------------------------------------------------------------------------------
/Tips - How to check if there are new or modified files in a path with a lot of files.md:
--------------------------------------------------------------------------------
1 | # How to check if there are new or modified files in a path with a lot of files
2 |
3 | This is a big question. Let's go this together.
4 |
5 | If I'm using ````Get-ChildItem```` to collect file, and put the result in a var, this could be a first step.
6 |
7 | ````powershell
8 | $AllFiles = Get-ChildItem -Path 'C:\temp' -Recurse -File | Select-Object -Property Name, FullName, length, CreationTime
9 | ````
10 |
11 | Later, I can run the same ````Get-ChildItem```` cmdlet and then I can use the ````Compare-Object```` cmdlet and compare with a specific (or more) property.
12 |
13 | ````powershell
14 | $AllFiles = Get-ChildItem -Path 'C:\temp' -Recurse -File | Select-Object -Property Name, FullName, length, CreationTime
15 | $AllFiles | Export-csv -Path C:\temp2\RefFile.csv -NoTypeInformation -UseCulture
16 | # creating a newfile, and gather the files again
17 | $CurrentFiles = Get-ChildItem -Path 'C:\temp' -Recurse -File | Select-Object -Property Name, FullName, length, CreationTime
18 | $Ref = Import-Csv -Path C:\temp2\RefFile.csv -Delimiter ";"
19 | Compare-Object -ReferenceObject $Ref -DifferenceObject $CurrentFiles -Property Name
20 | Name SideIndicator
21 | ---- -------------
22 | Newfile.txt =>
23 | |````
24 |
25 | Now I modify an existing file. (just add a single char). and run again
26 |
27 | ````powershell
28 | $Ref = Import-Csv -Path C:\temp2\RefFile.csv -Delimiter ";"
29 | Compare-Object -ReferenceObject $Ref -DifferenceObject $CurrentFiles -Property Length
30 | ````
31 |
32 | This is not a good way in this case. Let's try with ````Get-FileHash```` cmdlet
33 |
34 | ````powershell
35 | $AllFilesHash = Get-ChildItem -Path 'C:\temp' -Recurse -File |
36 | Get-FileHash -Algorithm MD5
37 | # export filehash in a file for later use
38 | $AllFilesHash |Export-Csv -Path C:\temp2\FileHashes.csv -UseCulture -NoTypeInformation -Append
39 | ````
40 |
41 | Modify an existing file or add or delete afile
42 |
43 | ````powershell
44 | $CurrentHash = Get-FileHash -Path C:\Temp -Algorithm MD5
45 | # looking for new files or modified files based on the fullName
46 | $NewFilesByFullName = Get-ChildItem -Path C:\Temp -Recurse -File |
47 | Where-Object {$_.FullName -notin $($AllFilesHash.Path)}
48 |
49 | Répertoire : C:\Temp
50 |
51 |
52 | Mode LastWriteTime Length Name
53 | ---- ------------- ------ ----
54 | -a---- 18/06/2020 12:00 0 newfile2.txt
55 | ````
56 |
57 | Yes, I've grab new files and modified files !
58 |
59 | Depending on how many files we need to process, that could get pretty time consuming to gather ````Get-ChildItem```` and ````Get-FileHash````, and this continue increasing as more files get processed.
60 | It could be better like the following and by this respect the rule "filter left, format right" :-)
61 | Like this :
62 |
63 | ````powershell
64 | Get-ChildItem -Path C:\Temp -Recurse -File -Exclude $($Ref.Name) |
65 | Get-FileHash -Algorithm MD5
66 | Algorithm Hash Path
67 | --------- ---- ----
68 | MD5 5058F1AF8388633F609CADB75A75DC9D C:\Temp\Newfile.txt
69 | MD5 D41D8CD98F00B204E9800998ECF8427E C:\Temp\newfile2.txt
70 | ````
71 |
72 | Is it really efficient ? Let's measure.
73 |
74 | ````powershell
75 | (Measure-Command -Expression {Get-ChildItem -Path 'C:\temp' -Recurse -File |
76 | Get-FileHash -Algorithm MD5}).TotalMilliseconds
77 |
78 | (Measure-Command -Expression {Get-ChildItem -Path 'C:\temp' -Recurse -File -Exclude $($Ref.Name) |
79 | Get-FileHash -Algorithm MD5}).TotalMilliseconds
80 |
81 | <#
82 | (Measure-Command -Expression {Get-ChildItem -Path 'C:\temp' -Recurse -File |
83 | Get-FileHash -Algorithm MD5}).TotalMilliseconds
84 | 693,7484
85 |
86 | (Measure-Command -Expression {Get-ChildItem -Path 'C:\temp' -Recurse -File -Exclude $($Ref.Name) |
87 | Get-FileHash -Algorithm MD5}).TotalMilliseconds
88 | 80,0364
89 | #>
90 | ````
91 |
92 | Nice, consuming time to run is smaller !
93 |
94 | Last step, I would like to update my reference file to have only new (modify) entries regularly.
95 |
96 | ````powershell
97 | $AllFilesHash | Export-Csv -Path "C:\temp2\FileHashes.csv" -UseCulture -NoTypeInformation -Append
98 | $FilesHashes = Import-Csv "C:\temp2\FileHashes.csv" -Delimiter ";"
99 | $CurrentHashes = Get-ChildItem -Path "C:\Temp" -Recurse -File -Exclude $($FilesHashes.path) |
100 | Get-FileHash -Algorithm MD5
101 | $CurrentHashes | Export-Csv -Path 'D:\MyFileshashes.csv' -UseCulture -NoTypeInformation
102 | ````
103 |
104 | Hope this helpful.
105 |
--------------------------------------------------------------------------------
/Tips - How to create Virtual Machines from a spreadsheet and a Posh Script.md:
--------------------------------------------------------------------------------
1 | # How to create Virtual Machines from a spreadsheet and a Posh Script
2 |
3 | This demonstration uses Cmdlets from the Posh module HyperV. If the hypervisor is different, the principles are the same.
4 | i.e. VMware.VimAutomation.Core or the other modules for VMWare infrastructure.
5 |
6 | We'll build our virtual environment based on a .csv file with all necessary informations.
7 |
8 | ## First Step : Create a .csv file
9 |
10 | 
11 |
12 | As you can see, in this first step I've only completed few informations : VMName, Memory VMGeneration, but you can add all necessary informations to build a complete infrastructure.
13 |
14 | ## Second Step : Import the .csv file in Posh
15 |
16 | ````powershell
17 | $VMsList = Import-Csv -Path 'C:\users\Olivier\Desktop\Infra.csv'
18 | Name Memory CPU
19 | ---- ------ ---
20 | VM1 1073741824 2
21 | VM2 42994967296 2
22 | VM3 536870912 2
23 | # You can also add other parameters with the cmdlet Import-Csv if necessary
24 | # -Delimiter : to define delimiter from your .csv file if it's not a comma
25 | # -Header : if you have a .csv with other informations, using this parameter you can import only named parameters. i.e. -Header Name, Memory
26 | Name Memory
27 | ---- ------
28 | Name Memory
29 | VM1 1073741824
30 | VM2 42994967296
31 | VM3 536870912
32 | # -Encoding : this parameter could be useful if your .csv file has some language specific characters. i.e : -Encoding UTF8
33 | ````
34 |
35 | >[Nota] : you'll notice that Memory Size is on Bytes.
36 |
37 | ## Step 3 : Using a foreach loop to build VMs
38 |
39 | As you can see, I'm using a splat to pass parameters. It's not mandatory but if you have to pass more than 3-4 parameter, this makes the script more readable.
40 |
41 | ````powershell
42 | foreach ($VM in $VMsList)
43 | {
44 | # VM parameters using a splat
45 | $VMParams = @{
46 | Name = $VM.Name # Specifies the name of the new virtual machine. The default name is New virtual machine.
47 | MemoryStartupBytes = $VM.Memory # Specifies the amount of memory, in bytes, to assign to the virtual machine. The default value is 512 MB.
48 | ComputerName = $VM.HostName # name of the Host on witch the VM is to be created
49 | Generation = $VM.Generation # Specifies the generation, as an integer, for the virtual machine. The values that are valid in this version of Windows are 1 and 2.
50 | NewVHDPath = $VM.VHDPath # Creates a new virtual hard disk with the specified path and connects it to the new virtual machine. Absolute paths are allowed. If only a file name is specified, the virtual hard disk is created in the default path configured for the host.
51 | NewVHDSizeBytes = $VM.VHDSizeBytes # Specifies the size of the dynamic virtual hard disk that is created and attached to the new virtual machine.
52 | SwitchName = $VM.SwitchName # Specifies the friendly name of the virtual switch if you want to connect the new virtual machine to an existing virtual switch to provide connectivity to a network. Hyper-V automatically creates a virtual machine with one virtual network adapter, but connecting it to a virtual switch is optional.
53 | BootDevice = $VM.BootDevice # Specifies the device to use as the boot device for the new virtual machine. Allowed values are CD , Floppy , LegacyNetworkAdapter , IDE , NetworkAdapter, and VHD.
54 | # ....
55 | }
56 | # Create VM
57 | New-VM @VMParams
58 | }
59 | ````
60 |
61 | ## Step 4 : save your final script and run it
62 |
63 | Of course you are free to add all necessary parameters to build your environment.
64 |
65 | >[Nota] : Refer to the Online Help of the cmdlet you'll use to define what parameter is mandatory, what parameter have default value if not define, ...
66 |
67 | You can also notice in the output generated by the script that the new VMs are in a State ***Off*** by default.
68 | If you want to start them automatically then, just add after the ````New-VM```` a pipeline with the ````Start-VM```` cmdlet.
69 |
70 | With this same principle (pipeline), you can also use other cmdlets like ````Set-VM````, and other ````Set-VMxxx```` cmdlets.
71 |
72 | >[Nota] : if you don't start the VM, the output show that MemoryAssigned to the VMs is O. This will change when the VM will start.
73 |
74 | ## Final word
75 |
76 | Of course, it's just a basic and primary approach. Feel free to complete and modify as you want.
77 |
78 | Hope this help
79 |
--------------------------------------------------------------------------------
/Tips - How to cut a big file (.txt or .csv) to many small files of defined size.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | Sometimes wewould like to split a large file (.txt or .csv) into several small files.
3 |
4 | The goal of the following function without pretention is to do this boring cutting work for you.
5 | #>
6 |
7 | function Split-File
8 | {
9 | [CmdletBinding()]
10 | #region params
11 | Param(
12 | # FilePath Help Description
13 | [Parameter(
14 | Mandatory = $true,
15 | HelpMessage = "Location of the input file",
16 | Position = 0
17 | )]
18 | [System.String]$FilePath,
19 |
20 | # $BlockSize Help Description
21 | [Parameter(
22 | Mandatory = $true,
23 | HelpMessage = "Number of lines desired in each new files",
24 | Position = 1
25 | )]
26 | [System.String]$BlockSize
27 | )
28 | #endregion
29 |
30 | <#
31 | .SYNOPSIS
32 | Split a large file (.txt or .csv) into several small files.
33 |
34 | .DESCRIPTION
35 | Split a large file (.txt or .csv) into several small files.
36 |
37 | .PARAMETER FilePath
38 | Path of the input file
39 |
40 | .PARAMETER BlockSize
41 | Number of lines in each generated split files
42 |
43 | .INPUTS
44 | None
45 |
46 | .OUTPUTS
47 | [System.Object]s
48 |
49 | .EXAMPLE
50 | PS> Split-File -FilePath c:\temp\Ipaddresses.csv -BlockSize 10 -Verbose
51 | COMMENTAIRES : The input file C:\temp\IPAddresses.csv has 254 lines
52 | COMMENTAIRES : It will be cut in 25 new files
53 | COMMENTAIRES : Creating The file IPAddresses-000.csv starting at 0 and ending at 9 of the original file C:\temp\IPAddresses.csv
54 | COMMENTAIRES : the file was created here : C:\Temp\Blocks\IPAddresses-000.csv
55 | COMMENTAIRES : Creating The file IPAddresses-001.csv starting at 10 and ending at 19 of the original file C:\temp\IPAddresses.csv
56 | COMMENTAIRES : the file was created here : C:\Temp\Blocks\IPAddresses-001.csv
57 | COMMENTAIRES : Creating The file IPAddresses-002.csv starting at 20 and ending at 29 of the original file C:\temp\IPAddresses.csv
58 | ...
59 | All files has been created here : C:\Temp\Blocks
60 |
61 | Split the input file in many file with 10 lines in verbose mode
62 |
63 | .EXAMPLE
64 | PS> Split-File -FilePath c:\temp\Ipaddresses.csv -BlockSize 10
65 | All files has been created here : C:\Temp\Blocks
66 |
67 | Split the input file in many file with 10 lines in normal mode (quiet)
68 |
69 |
70 | .NOTES
71 | Version : 1.0
72 | Author : O. FERRIERE
73 | Creation Date : 28/08/2019
74 | Purpose/Change : Initial script development
75 |
76 | TO DO
77 | Add Header in each generated file
78 | #>
79 | #region Check
80 | # ExportPath is still existing
81 | $ExportPath = Join-Path -Path (Get-Location) -ChildPath "Blocks"
82 | if (-not (Test-Path $ExportPath))
83 | {
84 | New-Item -Path $ExportPath -ItemType Directory
85 | Write-Verbose "A directory for the new export Files has been created here : $ExportPath"
86 | }
87 | # Extension is .csv or .txt
88 | if ( (((Get-Item -Path $FilePath).Extension) -ne ".csv") -and (((Get-Item -Path $FilePath).Extension) -ne ".txt") )
89 | {
90 | Write-Verbose "The Input file must be a .csv or .txt file. Exit Script"
91 | throw
92 | }
93 | #endregion
94 |
95 | #region Execution
96 | $File = Get-Content -Path $FilePath
97 | $FileSizeMeaningZero = $File.count - 1
98 | Write-Verbose "The input file $FilePath has $FileSizeMeaningZero lines"
99 |
100 | # Calculation of the number of blocks (files) to do
101 | $NrOfBlockMeaningZero = [math]::Floor(($FileSizeMeaningZero + 1) / $BlockSize)
102 | Write-Verbose "It will be cut in $NrOfBlockMeaningZero new files"
103 |
104 | # File (blocks) creation with automatic namming
105 | $PrefixName = (Get-Item -Path $FilePath).baseName
106 | $ExtensionName = (Get-Item -Path $FilePath).Extension
107 | foreach ($Block in (0..$NrOfBlockMeaningZero))
108 | {
109 | $BlockFileName = "$prefixName-{0:000}$ExtensionName" -f $Block
110 | $StartLine = $Block * $BlockSize
111 | $EndLine = $StartLine + $BlockSize - 1
112 | if ($EndLine -gt $FileSizeMeaningZero)
113 | {
114 | $EndLine = $FileSizeMeaningZero
115 | }
116 | Write-Verbose "Creating The file $BlockFileName starting at $StartLine and ending at $EndLine of the original file $FilePath"
117 | $ExportFilePath = Join-Path -Path $ExportPath -ChildPath $BlockFileName
118 | $File[$StartLine..$EndLine] | Out-File -FilePath $ExportFilePath -Encoding utf8
119 | Write-Verbose "the file was created here : $ExportFilePath"
120 | }
121 | Write-Output "All files has been created here : $ExportPath"
122 | #endregion
123 | }
--------------------------------------------------------------------------------
/Tips - How to display a pop-up message box windows.md:
--------------------------------------------------------------------------------
1 | # How to display a pop-up message box with PowerShell in a script
2 |
3 |
4 | ## 1 - Load Assembly
5 | Using WPF (Windows Presentation Framework) provides a better look awith rich-looking UIs. It's better that using the legacy Windows Forms.
6 |
7 | ````powershell
8 | Add-Type -AssemblyName PresentationCore,PresentationFramework
9 | ````
10 |
11 | This helps to access the classes in the Windows Presentation Framework.
12 |
13 | ## Decide the Button Type to use
14 |
15 | ```` powershell
16 | $ButtonType = [System.Windows.MessageBoxButton]::YesNo
17 | ````
18 |
19 | There are 4 types of button Types :
20 | - OK
21 | - OKCancel
22 | - YesNo
23 | - YesNoCancel
24 |
25 | More Info about [MessageBoxButton Enum](https://docs.microsoft.com/en-us/dotnet/api/system.windows.messageboxbutton?redirectedfrom=MSDN&view=windowsdesktop-6.0)
26 |
27 |
28 | ## Decide the Message Icon to use
29 |
30 | ```` powershell
31 | $MessageIcon = [System.Windows.MessageBoxImage]::Question
32 | ````
33 |
34 | There are 8 types of Message Icons :
35 | - Asterik
36 | - Error
37 | - Exclamation
38 | - Hand
39 | - None
40 | - Question
41 | - Stop
42 | - Warning
43 |
44 | More Info about [MessageBoxImage Enum](https://docs.microsoft.com/en-us/dotnet/api/system.windows.messageboximage?redirectedfrom=MSDN&view=windowsdesktop-6.0)
45 |
46 |
47 | ## Define the Message Body to display
48 |
49 | ```` powershell
50 | $MessageBody = "Are you sure you want to delete the log file ?"
51 | ````
52 |
53 | No specific info about this, ti's just a string to display. Note that the [String] may contain variables.
54 |
55 | ## Define the message Title
56 |
57 | ```` powershell
58 | $MessageTitle = "Confirm Deletion"
59 | ````
60 | No specific info about this, ti's just a string to display. Note that the [String] may contain variables.
61 |
62 | ## and finally display the result
63 |
64 | ````powershell
65 | $Result = [System.Windows.MessageBox]::Show($MessageBody,$MessageTitle,$ButtonType,$MessageIcon)
66 | ````
67 |
68 | As you can show, the ````Show```` Method has 4 parameters :
69 | - MessageBody
70 | - MessageTitle
71 | - ButtonType
72 | - MessageIcon
73 |
74 | Note that you must **respect this order**.
75 |
76 | More Info about the [MessageBox Class](https://docs.microsoft.com/en-us/dotnet/api/system.windows.messagebox?redirectedfrom=MSDN&view=windowsdesktop-6.0)
77 |
78 | ## Assemble all code
79 |
80 | ````powershell
81 | $var = "John"
82 | Add-Type -AssemblyName PresentationCore,PresentationFramework
83 | $ButtonType = [System.Windows.MessageBoxButton]::YesNoCancel
84 | $MessageIcon = [System.Windows.MessageBoxImage]::Error
85 | $MessageBody = "Are you sure you want to delete the log file, $var"
86 | $MessageTitle = "Confirm Deletion, $Var"
87 | $Result = [System.Windows.MessageBox]::Show($MessageBody,$MessageTitle,$ButtonType,$MessageIcon)
88 | Write-Host "Your choice is $Result"
89 | ````
90 |
91 | ## Additional explanations
92 |
93 | - If you would like to display a Message Info Box, in a scheduled Task script, assure the *"Run Only when user is logged on"* radio button is selected. Choosing the alternative (i.e. *"Run whether user is logged on or not"*) will hide the script, including any message boxes you have programmed in.
94 |
--------------------------------------------------------------------------------
/Tips - How to get your public address.md:
--------------------------------------------------------------------------------
1 | # How to get you Public address
2 |
3 | Using the ````Invoke-WebRequest```` Cmdlet, you can easily get your public Address.
4 |
5 | The Site can provide this information
6 |
7 | ````powershell
8 | Invoke-WebRequest -Uri "http://ipconfig.me"
9 | StatusCode : 200
10 | StatusDescription : OK 8
11 | Content : xxx.xxx.xxx.xxx
12 | RawContent : HTTP/1.1 200 OK
13 | Access-Control-Allow-Origin: *
14 | Content-Length: 14
15 | Content-Type: text/plain; charset=utf-8
16 | Date: Mon, 20 Jul 2020 05:46:50 GMT
17 | Via: 1.1 google
18 |
19 | xxx.xxx.xxx.xxx
20 | Forms : {}
21 | Headers : {[Access-Control-Allow-Origin, *], [Content-Length, 14], [Content-Type, text/plain;
22 | charset=utf-8], [Date, Mon, 20 Jul 2020 05:46:50 GMT]...}
23 | Images : {}
24 | InputFields : {}
25 | Links : {}
26 | ParsedHtml : mshtml.HTMLDocumentClass
27 | ````
28 |
29 | And now, if you focus only on the property **Content**, this return the only information we're looking for : Public IP Address
30 |
31 | ````powershell
32 | (Invoke-WebRequest -Uri "http://ipconfig.me").content
33 | xxx.xxx.xxx.xxx
34 | ````
35 |
36 | Simple, isn't it ?
37 |
38 | Hope this help.
39 |
--------------------------------------------------------------------------------
/Tips - How to install software from the Internet.md:
--------------------------------------------------------------------------------
1 | # How to install software from the Internet silently
2 | In this sample, i'm choosing VScode
3 |
4 | 1 - Define the URL and the install Arguments
5 |
6 | ````powershell
7 | $Url = "https://aka.ms/win32-x64-user-stable"
8 | $Install_Args = '/verysilent /suppressmsgboxes /mergetasks=!runcode'
9 | ````
10 |
11 | 2 - Define the install location UNC or Local
12 |
13 | ````powershell
14 | $CurrentDir = (Get-Location).Path
15 | ````
16 |
17 | 3 - Get latest download url for Visual Studio Code
18 |
19 | ````powershell
20 | # Set the Net.SecurityProtocolType to TLS 1.2
21 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
22 |
23 | $webRequest = [Net.WebRequest]::Create("$Url")
24 | $Asset = $Webrequest.GetResponse()
25 | $Uri = $Asset.ResponseUri.AbsoluteUri
26 | $InstallerPath = "$CurrentDir\$($Uri.Split('/')[-1])"
27 | ````
28 |
29 | 4 - download to the $InstallerPath
30 |
31 | ````powershell
32 | if (-not (Test-Path $InstallerPath))
33 | {
34 | $ProgressPreference = 'SilentlyContinue'
35 | Invoke-WebRequest -Uri $Uri -OutFile $InstallerPath
36 | }````
37 |
38 | 5 - Run installer, silently
39 |
40 | ````Powershell
41 | Start-Process -FilePath $InstallerPath -ArgumentList $Install_args -Wait
42 | ````
43 |
44 | 6 - Post-Installation, Customizations : Install Extensions ms-vscode-powershell and vscode-Icons-team
45 |
46 | ````Powershell
47 | Set-Location -Path $Env:LOCALAPPDATA\Program\Microsoft VS Code\"
48 | code --install-extension ms-vscode.powershell --force
49 | code --install-extension vscode-icons-team.vscode-icons --force
50 | ````
51 |
52 | 7 - Post-Installation, Defining some Settings for VScode
53 |
54 | ````Powershell
55 | $SettingJSONfile = "$($env:APPDATA)\Code\User\settings.json"
56 | # Define VSCode settings as a Here-String
57 | $SettingJSON = @"
58 | {
59 | //"powershell.powerShellDefaultVersion": "Windows PowerShell (x64)",
60 | "editor.renderWhitespace": "all",
61 | "terminal.integrated.profiles.windows": {
62 | "PowerShell": {"path": "C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"}
63 | },
64 | "terminal.integrated.automationShell.linux": ""
65 | }
66 | "@
67 | Set-Content -Path $SettingJSONfile -Value $SettingJSON
68 | ````
69 |
70 | [Nota] : Based on the following article
71 | https://azurecloudai.blog/2021/04/26/how-i-install-microsoft-code/
72 |
--------------------------------------------------------------------------------
/Tips - How to launch a script as RunAsAdmin.md:
--------------------------------------------------------------------------------
1 | # How to launch a script as RunAsAdmin
2 |
3 | There are miltiple ways to to this. I'm thinking the easiest way is to test test if the script is running in ````RunAsAdmin```` mode.
4 | If not, the script will start a new process Powershell using the ````-Verb```` parameter with the value ````RunAs```` and execute the script in this new process.
5 |
6 | For that, in the beginning of the script add this :
7 |
8 | ````powershell
9 | if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
10 | {
11 | Start-Process powershell.exe "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs; exit
12 | }
13 | ````
14 | ## Final word
15 | This tips is a reminder for me ... and I hope for other people.
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/Tips - How to pass Robocopy parameters in a PS script.ps1:
--------------------------------------------------------------------------------
1 | # requires RunAsAdministrator
2 |
3 | $source = "C:\temp\test"
4 | $Destination = "c:\temp3"
5 | $what = @("/COPYALL", "/B", "/SEC", "/MIR") # copy type mirroring (as a lazy admin, i'm calling the same code to inital copy and final sync)
6 | $options = @("/R:0", "/W:0", "/NP") # and all other options you want to use
7 | $user = "Hodor"
8 | $log = @("/Log+:c:\temp\logrobo-$User.log") # Useful if you want a different log file for each treatement (here user)
9 | $RoboArgs = @($Source, $Destination, $what, $options, $log)
10 |
11 | & robocopy @RoboArgs
12 |
13 | <#
14 | As you can see, the first Tips is to pass robocopy params in an array
15 | The second one is to concatenate them with splating method
16 | #>
17 |
18 | $RoboArgs =@(
19 | "C:\temp\test",
20 | "C:\temp3\",
21 | "/COPYALL", "/B", "/SEC", "/MIR", "/R:0", "/W:0", "/NP",
22 | "/Log+:c:\temp\logrobo.log"
23 | )
24 | # is working fine too
--------------------------------------------------------------------------------
/Tips - How to pass credentials as a parameter in a script.md:
--------------------------------------------------------------------------------
1 | # TIP - Pass credentials as a parameter in a script
2 |
3 | Sometimes we need to pass credentials in a script. Of course, we can do it with 2 variables (ex. $UserName and $Password) but why not do it with a single variable ?
4 |
5 | ## the code
6 | ````powershell
7 | [CmdletBinding()]
8 | param(
9 | [Parameter(
10 | Position = 0,
11 | HelpMessage = 'Credentials to authenticate agains a remote computer')]
12 | [System.Management.Automation.PSCredential]
13 | [System.Management.Automation.CredentialAttribute()]
14 | $Credential
15 | )
16 | ````
17 |
18 | and now a demo how to use it. I've a simple script with the following content :
19 |
20 | ````powershell
21 | [CmdletBinding()]
22 | param(
23 | [Parameter(
24 | Position = 0,
25 | HelpMessage = 'Credentials to authenticate agains a remote computer')]
26 | [System.Management.Automation.PSCredential]
27 | [System.Management.Automation.CredentialAttribute()]
28 | $Credential
29 | )
30 | Write-Host "the credentials passed in parameter are : "
31 | $Credential
32 | ````
33 | and now running the script :
34 |
35 | ````powershell
36 | & '.\test-get-credentials.ps1' -Credential asus10\olivier
37 |
38 | UserName Password
39 | -------- --------
40 | asus10\olivier System.Security.SecureString
41 | ````
42 |
43 | >[Nota] : The parameter ````-Credential```` is mandatory. The value passed is just the ***Domain\userName*** or ***Computer\UserName***.
44 |
45 | This could be useful
46 |
47 | Hope this help
48 |
49 |
50 |
--------------------------------------------------------------------------------
/Tips - How to resolve PS Error Message Warning - unable to resolve package source.md:
--------------------------------------------------------------------------------
1 | # How to resolve PS Error Message Warning - unable to resolve package source
2 |
3 | Recently, after I've just done a fresh install of a new Windows 2016 server, I wanted to install some useful Powershell module.
4 |
5 | A message appears in console "WARNING: Unable to resolve package source 'https://www.powershellgallery.com/api/v2'.
6 |
7 | ````powershell
8 | Install-Module pswritehtml
9 | AVERTISSEMENT : Unable to resolve package source 'https://www.powershellgallery.com/api/v2'.
10 | PackageManagement\Install-Package : Aucune correspondance trouvée pour les critères de recherche spécifiés et le nom de module pswritehtml. Utilisez Get-PSRepository pour afficher
11 | pour toutes les repositories de module enregistrées disponibles.
12 | ````
13 |
14 | This is linked to the insecure security protocols being used by powershell and it will need to be set to a supported protocol, this could be either if you were behind a proxy configured a certain way or the site you are connecting to only supports certain protocols.
15 |
16 | I've already correct this in the past, but how ? After a look on the Internet, I've found this post :
17 |
18 | and I tested this successfully. I've decided to post this in my Gibhub in the Tips section, to have a permanent reminder for me and for all people.
19 |
20 | First check current security Protocols you are running
21 |
22 | ````powershell
23 | [Net.ServicePointManager]::SecurityProtocol
24 | Ssl3, Tls
25 | ````
26 |
27 | Then, i've changed this to use TLS1.2 bu running the following command
28 |
29 | ````powershell
30 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
31 | # And now let's install a module
32 | Install-Module -Name pswritehtml -Verbose -Force
33 | COMMENTAIRES : Utilisation du fournisseur PowerShellGet pour la recherche de packages.
34 | COMMENTAIRES : Le paramètre -Repository n'a pas été spécifié. PowerShellGet utilisera tous les référentiels enregistrés.
35 | COMMENTAIRES : Obtention de l'objet fournisseur pour le fournisseur PackageManagement NuGet.
36 | COMMENTAIRES : Emplacement spécifié : https://www.powershellgallery.com/api/v2. Fournisseur PackageManagement : NuGet.
37 | ...
38 | COMMENTAIRES : Le module PSWriteHTML a été installé dans le chemin d’accès C:\Program Files\WindowsPowerShell\Modules\PSWriteHTML\0.0.88.
39 | ````
40 |
41 | It's fine, but when I've closed my PS session, settings returned to the previous version.
42 |
43 | This post : explain why.
44 |
45 | A solution to have TLS 1.2 permanently enabled is the following : set up the Powershell Profile File
46 |
47 | ````powershell
48 | # Setting to use TLS1.2 for updating PS modules on PowershellGallery à partir from 1st April 2020
49 | # ref : https://devblogs.microsoft.com/powershell/powershell-gallery-tls-support/
50 | Write-Host "Settings to use TLS1.2 for updating PS Modules on PowershellGallery from 1st April 2020" -ForegroundColor 'DarkGray'
51 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
52 | ````
53 |
54 | Another solution is to use registry entries forcing all .NET processes targetting .NET 4.5 to use strong crypto.
55 |
56 | ````powershell
57 |
58 | $Newkeyparam1 = @{
59 | Path = "HKLM:\SOFTWARE\Microsoft\.NetFramework\v4.0.30319"
60 | Name = "SchUseStrongCrypto"
61 | Value = "1"
62 | Type = "DWord"
63 | Force = $true
64 | }
65 | Set-ItemProperty @Newkeyparam1
66 |
67 | $Newkeyparam2 = @{
68 | Path = "HKLM:\SOFTWARE\Wow6432Node\Microsoft\.NetFramework\v4.0.30319"
69 | Name = "SchUseStrongCrypto"
70 | Value = "1"
71 | Type = "DWord"
72 | Force = $true
73 | }
74 | Set-ItemProperty @Newkeyparam2
75 | ````
76 |
77 | Other Ref :
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/Tips - How to sort files in a directory basing on a réference .csv file.md:
--------------------------------------------------------------------------------
1 | # THE CASE
2 | We have thousands images in a folder.
3 | Each image is classified into a specific type (diagnosis, named dx in the input file) and classified in the Excel file. This specific type defined the name of the file.
4 |
5 | ## The goal is :
6 |
7 | How to sort these images in the folder in groups so that each group of images have the same specific Type.
8 |
9 | ## This question was originally posted on :
10 |
11 | https://superuser.com/questions/1485722/how-do-i-find-images-based-on-their-numbers-in-the-excel-file/1485788#1485788
12 |
13 | # THE APPROACH FOR THIS CASE
14 | I proposed the following approach, and perhaps it could be useful for someone else, then i post this as a tips
15 |
16 |
17 | ````powershell
18 | # Define some variables
19 | $BasedImages = "\\path\to\ImagesDirectory"
20 | $Csvfile = "\\path\to\InputCsvFile.csv"
21 |
22 | # Gather all sub-folders in the Images Directory (only first level) and put the name of these folder in a var. The only property useful for later use is the Directory name. Useless to gather all properties
23 | $ExistingSubDir = Get-ChildItem -Path $BasedImages -Directory | Select-Object -Property name
24 |
25 | # Gather unique diagnosis in the input file and put in a var. The only useful property in the dx property for a later use. Useless to collect more info.
26 | $UniqueDiagnosis = Import-Csv -Path $Csvfile | Select-Object -property dx -Unique
27 |
28 | # gather all images files FullName in the Images Directory and put in a var. it seems that only Name,DirectoryName, FullName properties will be useful for later use
29 | $AllImagesFiles = Get-ChildItem -Path $BasedImages -File | Select-Object -Property Name, DirectoryName FullName
30 |
31 | #region First Step : build a Tree with sub-folders named by the unique Diagnosis name.
32 | foreach ($Diagnosis in $UniqueDiagnosis)
33 | {
34 | # search if a diagnosis dir name (dx field in the input .csv file) exist in the ImageDirectory and put the result in a var
35 | if ($ExistingSubDir -contains $Diagnosis)
36 | {
37 | Write-Host "$ExistingSubDir is still existing, no action at this step" -ForegroundColor Green
38 | }
39 | else
40 | {
41 | New-Item -Path $BasedImages -Name $Diagnosis -ItemType Directory
42 | Write-Host "a sub-directory named $Diagnosis has been created in the folder $BasedImages" -ForegroundColor Yellow
43 | }
44 | }
45 | #endregion
46 |
47 | # At this step, you'll have some sub directories named with the name of all diagnosis (fied dx in the input file)
48 | #region Step 2 : Time to move the files in the root folder
49 | foreach ($image in $AllImagesFiles)
50 | {
51 | $TargetSubDir = Get-Item -Path $($image.fullName)
52 | Move-Item -Path $($Image.FullName) -Destination ( Join-Path -Path (Split-Path -Path $($Image.DirectoryName) -Parent) -ChildPath $TargetSubDir)
53 | Write-Host "the image named $($image.name) has been moved to the sud directory $TargetSubDir" -ForegroundColor Green
54 | }
55 | #endregion
56 | ````
57 |
--------------------------------------------------------------------------------
/Tips - How to sort properties diffently for each parameter.ps1:
--------------------------------------------------------------------------------
1 |
2 | # When you want to sort object
3 | Get-Command | Sort-Object -Property Source, Name, Version # default ascending
4 | Get-Command | Sort-Object -Property Source, Name, Version -Descending
5 |
6 | # if you want to perform ascending sort for some properties and descending for some others, you must proceed like the following :
7 | Get-Command | Sort-Object -Property @{Expression = ‘Source’; Ascending = $true},
8 | @{Expression = ‘Name’; Ascending = $true},
9 | @{Expression = ‘Version’; Descending = $true}
10 |
11 | # Once again a hash table is the answer (thanks Richard : https://richardspowershellblog.wordpress.com/2019/07/26/sort-direction/)
12 |
--------------------------------------------------------------------------------
/Tips - How to use $MyInvocation var in a script.md:
--------------------------------------------------------------------------------
1 | # Inside $MyInvocation automatic Variable
2 |
3 | This variable contains information about the current command, such as the name, parameters, parameter values, and information about how the command was started, called, or invoked, such as the name of the script that called the current command.
4 |
5 | ````powershell
6 | $MyInvocation
7 | ````
8 |
9 | this returns :
10 |
11 | ````powershell
12 | MyCommand : test.ps1
13 | BoundParameters : {}
14 | UnboundArguments : {}
15 | ScriptLineNumber : 0
16 | OffsetInLine : 0
17 | HistoryId : 4
18 | ScriptName :
19 | Line :
20 | PositionMessage :
21 | PSScriptRoot :
22 | PSCommandPath :
23 | InvocationName : C:\Users\UserName\Desktop\test.ps1
24 | PipelineLength : 2
25 | PipelinePosition : 1
26 | ExpectingInput : False
27 | CommandOrigin : Internal
28 | DisplayScriptPosition :
29 | ````
30 |
31 | As we can see, there are several interesting properties that we can use
32 |
33 | ````powershell
34 | $MyInvocation.MyCommand
35 | CommandType Name Version Source
36 | ----------- ---- ------- ------
37 | ExternalScript test.ps1 C:\Users\%UserName%\Desktop\test.ps1
38 | ````
39 |
40 | If i try ````$MyInvocation.MyCommand.Name```` this returns the name of the script that launchs the script.
41 | But it's the name with the extension. It's not very practical when you would like to use the script name for naming convention of a log file.
42 | I would like to **get the short Name of the script**.
43 |
44 | Ok, let's try to split this with using the ````.split```` method
45 |
46 | ````powershell
47 | ($MyInvocation.MyCommand.Name).split(".",2)
48 | test
49 | ps1
50 | # The parameter of the split method will be the "." separing Name and extension. In this particular case ($MyInvocation.MyCommand.Name).split(".")[0] is good too.
51 |
52 | ($MyInvocation.MyCommand.Name).split(".")[0]
53 | Test
54 |
55 | ($MyInvocation.MyCommand.Name).split(".")[1]
56 | .ps1
57 |
58 | # The [0] return only the first part of the split, then the name. If I use [1] is will be the second part (Extension)
59 | ````
60 |
61 | If i would like to **get the full path name of the script**, i'll use the following
62 |
63 | ````powershell
64 | $MyInvocation.InvocationName
65 | C:\Users\%UserName%\Desktop\test.ps1
66 |
67 | # Another method
68 | $PSCommandPath
69 | C:\Users\%UserName%\Desktop\test.ps1
70 | ````
71 |
72 | I could get the same information with the default variable ````$PSCommandPath````
73 |
74 | To **get the Parent Dir of the script**, we can use
75 |
76 | ````powershell
77 | $MyInvocation.$PSScriptRoot
78 | # or directly
79 | $PSScriptRoot
80 | ````
81 |
--------------------------------------------------------------------------------
/Tips - Identify Apps Packages and remove them.ps1:
--------------------------------------------------------------------------------
1 | # Get a list of the package names of all installed apps for current user
2 | Get-AppxPackage | Where-Object { if (-not($_.IsFramework -or $_.PublisherId -eq "cw5n1h2txyewy"))
3 | {$_}
4 | } |
5 | Select-Object Name, version, PackageFullName
6 |
7 | # and now you can remove them by the following
8 | Remove-AppXPackage
9 |
10 | # One-liner version
11 | Get-AppxPackage | Where-Object { if (-not($_.IsFramework -or $_.PublisherId -eq "cw5n1h2txyewy"))
12 | {$_}
13 | } |
14 | Select-Object Name, version, PackageFullName |
15 | Out-GridView -PassThru |
16 | Remove-AppXPackage
--------------------------------------------------------------------------------
/Tips - Increase Privacy By disabling Telemetry for Windows 10.ps1:
--------------------------------------------------------------------------------
1 | # Ref. : https://4sysops.com/archives/disable-windows-10-telemetry-with-a-powershell-script/#comment-656904
2 | # Run as admin
3 |
4 | Function ChangeReg {
5 | param ([string] $RegKey,
6 | [string] $Value,
7 | [string] $SvcName,
8 | [Int] $CheckValue,
9 | [Int] $SetData)
10 | Write-Host "Checking if $SvcName is enabled" -ForegroundColor Green
11 | if (!(Test-Path $RegKey)){
12 | Write-Host "Registry Key for service $SvcName does not exist, creating it now" -ForegroundColor Yellow
13 | New-Item -Path (Split-Path $RegKey) -Name (Split-Path $RegKey -Leaf)
14 | }
15 | $ErrorActionPreference = 'Stop'
16 | try{
17 | Get-ItemProperty -Path $RegKey -Name $Value
18 | if((Get-ItemProperty -Path $RegKey -Name $Value).$Value -eq $CheckValue) {
19 | Write-Host "$SvcName is enabled, disabling it now" -ForegroundColor Green
20 | Set-ItemProperty -Path $RegKey -Name $Value -Value $SetData -Force
21 | }
22 | if((Get-ItemProperty -Path $RegKey -Name $Value).$Value -eq $SetData){
23 | Write-Host "$SvcName is disabled" -ForegroundColor Green
24 | }
25 | } catch [System.Management.Automation.PSArgumentException] {
26 | Write-Host "Registry entry for service $SvcName doesn't exist, creating and setting to disable now" -ForegroundColor Yellow
27 | New-ItemProperty -Path $RegKey -Name $Value -Value $SetData -Force
28 | }
29 | }
30 |
31 | # Disabling Advertising ID
32 | $RegKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AdvertisingInfo"
33 | $Value = "Enabled"
34 | $SvcName = "Advertising ID"
35 | $CheckValue = 1
36 | $SetData = 0
37 | ChangeReg -RegKey $RegKey -Value $Value -SvcName $SvcName -CheckValue $CheckValue -SetData $SetData
38 | #Telemetry Disable
39 | $RegKey = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\DataCollection"
40 | $Value = "AllowTelemetry"
41 | $SvcName = "Telemetry"
42 | $CheckValue = 1
43 | $SetData = 0
44 | ChangeReg -RegKey $RegKey -Value $Value -SvcName $SvcName -CheckValue $CheckValue -SetData $SetData
45 | #SmartScreen Disable
46 | $RegKey = "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\AppHost\EnableWebContentEvaluation"
47 | $Value = "Enabled"
48 | $SvcName = "Smart Screen"
49 | $CheckValue = 1
50 | $SetData = 0
51 | ChangeReg -RegKey $RegKey -Value $Value -SvcName $SvcName -CheckValue $CheckValue -SetData $SetData
52 | Write-Host "Disabling DiagTrack Services" -ForegroundColor Green
53 | Get-Service -Name DiagTrack | Set-Service -StartupType Disabled | Stop-Service
54 | Get-Service -Name dmwappushservice | Set-Service -StartupType Disabled | Stop-Service
55 | Write-Host "DiagTrack Services are disabled" -ForegroundColor Green
56 | Write-Host "Disabling telemetry scheduled tasks" -ForegroundColor Green
57 | $tasks ="SmartScreenSpecific",
58 | "ProgramDataUpdater",
59 | "Microsoft Compatibility Appraiser",
60 | "AitAgent",
61 | "Proxy",
62 | "Consolidator",
63 | "KernelCeipTask",
64 | "BthSQM",
65 | "CreateObjectTask",
66 | "Microsoft-Windows-DiskDiagnosticDataCollector",
67 | "WinSAT",
68 | "GatherNetworkInfo",
69 | "FamilySafetyMonitor",
70 | "FamilySafetyRefresh",
71 | "SQM data sender",
72 | "OfficeTelemetryAgentFallBack",
73 | "OfficeTelemetryAgentLogOn"
74 | $ErrorActionPreference = 'Stop'
75 | $tasks | %{
76 | try{
77 | Get-ScheduledTask -TaskName $_ | Disable-ScheduledTask
78 | } catch [Microsoft.PowerShell.Cmdletization.Cim.CimJobException] {
79 | "task $($_.TargetObject) is not found"
80 | }
81 | }
--------------------------------------------------------------------------------
/Tips - Install-PowerShellCorePortable.ps1:
--------------------------------------------------------------------------------
1 | #requires -RunAsAdministrator
2 | #requires -Version 5
3 |
4 | ## Install latest PowerShell Core Zip x64 release
5 | $latestRelease = Invoke-WebRequest -Uri https://github.com/PowerShell/PowerShell/releases/latest -UseBasicParsing
6 | $zipRelativeLink = $latestRelease.Links | Where href -Match '-win-x64.zip' | % href
7 | $zipPath = Join-Path -Path $env:TEMP -ChildPath $zipRelativeLink.Split('/')[-1]
8 |
9 | if (-not (Test-Path -Path $zipPath)) {
10 | Invoke-WebRequest -Uri ('https://github.com{0}' -f $zipRelativeLink) -OutFile $zipPath
11 | }
12 |
13 | Unblock-File -Path $zipPath
14 |
15 | if (-not (Test-Path -Path "$env:ALLUSERSPROFILE\PowerShell")) {
16 | $null = New-Item -Path $env:ALLUSERSPROFILE -Name 'PowerShell' -ItemType Directory -Force
17 | }
18 |
19 | $folderName = $zipRelativeLink.Split('/')[-1] -replace '^PowerShell-','' -replace '-win-x64\.zip$',''
20 | $destinationFolder = Join-Path -Path "$env:ALLUSERSPROFILE\PowerShell" -ChildPath $folderName
21 | Expand-Archive -Path $zipPath -DestinationPath $destinationFolder
22 |
--------------------------------------------------------------------------------
/Tips - Optional Arguments in a Scheduled Task.ps1:
--------------------------------------------------------------------------------
1 | #Optional Arguments in a Scheduled Task
2 |
3 |
4 | -NonInteractive – Disable this interactive console to the user
5 | -WindowStyle Hidden – run PowerShell window in hidden mode, invisibly to the user
6 | -NoProfile – Prevents the user profile loading, which can slightly speed up the execution of script
7 | -NoExit – Leave the shell open after execution of the script. This can be useful when testing and debugging
8 | -ExecutionPolicy – Sets the script execution policy for the current session, it can be set to ByPass, Unrestricted, RemoteSigned, AllSigned or Restricted. The specified policy will be effective only in the current session and takes precedence over any previously established policies
9 | -NoLogo -
10 |
--------------------------------------------------------------------------------
/Tips - Ping and resolve DNSName at the same time.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | The purpose of this Tip is to show how to build quickly a graceful script to :
3 | > Ping several remote computers
4 | > resolve DNSName in the same time
5 | > Gather all informations in a PSCustomObject
6 | > Export the PSCustomObject to a file (.txt, .csv, html, xml, ... as you want)
7 | #>
8 |
9 | #region Define Input
10 | <# This could be :
11 | > IP adresses list,
12 | HostNames/IPs in a .csv file
13 | HostNames/IPs in a .txt file.
14 | #>
15 | $Entries = "192.168.0.14", "192.168.0.15", "192.168.0.16"
16 | #$csv = Import-Csv -Path C:\Temp\IPAddresses.csvs
17 | #$txt = Get-Content -Path C:\temp\ipAddress.txt
18 | #endregion
19 |
20 | #region Defin Path for Export file
21 | $ExportFile = "C:\temp\Result.csv"
22 | #endregion
23 |
24 | #region execution
25 | # replace $Entries by $csv or $txt as your need
26 | $Collection = foreach ($Server in $Entries)
27 | {
28 | # Define an Ordered PSCustomObject with some interesting properties
29 | $Status = [PsCustomObject][Ordered] @{
30 | 'Destination' = $Server
31 | 'TimeStamp' = Get-Date -Format g
32 | 'Results' = 'Down'
33 | 'IP' = 'N/A'
34 | 'HasDNS' = [bool](Resolve-DnsName -Name $Server -ErrorAction SilentlyContinue)
35 | 'HostName' = 'N/A'
36 | }
37 | # test connection (ping)
38 | $Ping = Test-Connection $server -Count 1 -ErrorAction SilentlyContinue
39 |
40 | if ($true -eq $Ping)
41 | {
42 | # Update values in the PSCustomObject
43 | $Status.Results = 'Up'
44 | $Status.IP = ($Ping.IPV4Address).IPAddressToString
45 | }
46 | if ($true -eq $Status.HasDNS)
47 | {
48 | # Update values in the PSCustomObject
49 | $Status.HostName = (Resolve-DnsName -Name $Server -ErrorAction SilentlyContinue).NameHost
50 | }
51 | $Status
52 | }
53 | #endregion
54 |
55 | #region Show result in the console shell
56 | $collection
57 | #endregion
58 |
59 | #region Export the result in a .csv file
60 | $collection | Export-Csv -Path $ExportFile -Delimiter ";" -NoTypeInformation -Encoding UTF8
61 | #endregion
62 |
63 | #region open the result file
64 | & $ExportFile
65 | #endregion
--------------------------------------------------------------------------------
/Tips - Powershell Remoting.ps1:
--------------------------------------------------------------------------------
1 | # Tips and Tricks : POwershell remoting cheatsheet
2 |
3 | # 1 - Enabling PowerShell Remoting
4 | Enable-PSRemoting –force # in a console in a runasadministrator mode
5 |
6 | # 2 - Troubleshoot
7 | # Make sure the WinRM service is setup to start automatically
8 | Get-service WinRM | Select-Object -Property Name, Status, startType, DisplayName # if StarType is not Automatic then...
9 | Set-Service WinRM -StartMode Automatic # Set start mode to automatic
10 | # Another method to get the service properties
11 | Get-WmiObject -Class win32_service | Where-Object {$_.name -like "WinRM"}
12 |
13 | # Check if remote hosts (not in the same AD Domain) are trusted.
14 | # Verify trusted hosts configuration
15 | Get-Item WSMan:\localhost\Client\TrustedHosts # show the trusted hosts
16 | Set-Item WSMan:localhost\client\trustedhosts -value server01 # set up the trusted host
17 | Set-Item WSMan:localhost\client\trustedhosts -value " " # remove all trusted hosts
18 | Set-Item WSMan:\localhost\Client\TrustedHosts -Concatenate -Value Server02 # Add another trusted host in the existing list
19 |
20 |
21 | # 3 - Executing Remote Commands with PowerShell Remoting
22 | # Executing a Single Command on a Remote System
23 | Invoke-Command –ComputerName MyServer1 -ScriptBlock {Hostname}
24 | Invoke-Command –ComputerName MyServer1 -Credential demo\serveradmin -ScriptBlock {Hostname}
25 | Get-ADComputer -Filter * -properties name | select @{Name="computername";Expression={$_."name"}} | Invoke-Command -ScriptBlock {hostname}
26 | # run scripts stored locally on your system against remote systems.
27 | Invoke-Command -ComputerName MyServer1 -FilePath c:\scripts\Get-Info.ps1
28 |
29 |
30 | # 4 - Establishing an Interactive PowerShell Console on a Remote System
31 | Enter-PsSession –ComputerName server1.domain.com
32 | Enter-PsSession –ComputerName server1.domain.com –Credentials domain\serveradmin
33 | Exit-PsSession # to exit the PSSession. This will send the session into the background.
34 |
35 | # 5 - Creating Background Sessions
36 | New-PSSession -ComputerName server1.domain.com -Name Server1 # creating PSSession
37 | New-PSSession -ComputerName server1.domain.com -Name server1 -Credential domain\serveradmin
38 | Enter-PSSession -Name server1 # and enter
39 |
40 | # 6 - Listing Background Sessions
41 | Get-PSSession
42 |
43 | # 7 - Interacting with Background Sessions
44 | Enter-PsSession –id 3
45 | Enter-PSSession -Name server1
46 |
47 | # 8 - Executing Commands through Background Sessions
48 | Invoke-Command -Session (Get-PSSession) -ScriptBlock {Hostname} # If your goal is to execute a command on all active sessions the “Invoke-Command” and “Get-PsSession” commands can be used together.
49 |
50 | # 9 - Removing Background Sessions
51 | Get-PSSession | Disconnect-PSSession
52 |
53 | # 10 - Last tips
54 | <#
55 | Use “Invoke-Command” if you’re only going to run one command against a system
56 | Use “Enter-PSSession” if you want to interact with a single system
57 | Use PSsessions when you’re going to run multiple commands on multiple systems
58 | #>
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/Tips - Powershell copy to clipboard.ps1:
--------------------------------------------------------------------------------
1 | Get-content -Path C:\temp\test1.txt # content is serv1, srv2, srv3
2 | Get-Clipboard -Format FileDropList
3 | # I've srv2,srv2 and Srv3 in the clipboard, and now
4 | Get-Clipboard -Format Text
5 | # same result
6 |
7 | Get-ChildItem -Path C:\temp -File # copy the console result in the clipboard
8 | Get-Clipboard -Format Text
9 | # as you can see, everything I have in the clipboard is copy to the console
10 |
11 | # I'm editing a image file (i.e. with Paint) select it and copy to the clipboard
12 | Get-Clipboard -Format Image
13 | # The result is the properties of the selected image
14 |
15 | # more info with
16 | Get-Help Set-Clipboard -Detailed
17 | # interesting parameters : -Append -Path
18 | Get-Help Get-Clipboard -Detailed
19 |
20 |
21 | # more help in https://adamtheautomator.com/powershell-copy-to-clipboard/
22 |
23 |
--------------------------------------------------------------------------------
/Tips - Quickly searching files in IsReadOnly mode and uncheck them.ps1:
--------------------------------------------------------------------------------
1 | # Searching for All Files in ReadOnly Mode and uncheck IsReadOnly Property
2 | Get-ChildItem c:\temp -Recurse | Set-ItemProperty -Name IsReadOnly -Value $false -ErrorAction SilentlyContinue
3 |
4 | # Searching for All specific extension Files in ReadOnly Mode and uncheck IsReadOnly Property
5 | Get-ChildItem c:\temp -Filter *.docx | Set-ItemProperty -Name IsReadOnly -Value $false
6 |
7 |
8 | # We could use the same way to perform other changes on the files properties
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/Tips - Resolving Get-NetTCPConnection owning service.md:
--------------------------------------------------------------------------------
1 | # Resolving Get-TCPConnection owning service and Remote Address
2 |
3 | ## The Challenge
4 | When using The Get-NetTCPConnection, we can see the RemoteAddresses but we haven't any idea of the Process Name calling these RemoteAddresses and we haven't any idea of the remote site using these RemoteAddresses
5 | They could be legitimate or illegitimate.
6 |
7 | Let's do this
8 |
9 | ````Powershell
10 | #Remote port you're looking for. If you use multi, separate with comma.
11 | $RemotePort = '443','80'
12 |
13 |
14 | # Identify the process Names
15 | $Process = @{
16 | Name = 'ProcessName'
17 | Expression = { (Get-Process -Id $_.OwningProcess).Name }
18 | }
19 |
20 | # Identify RemoteIP to Remote organisation using a public service
21 | $DarkAgent = @{
22 | Name = 'ExternalIdentity'
23 | Expression = {
24 | $IP = $_.RemoteAddress
25 | (Invoke-RestMethod -Uri "http://ipinfo.io/$IP/json" -UseBasicParsing -ErrorAction Ignore).org
26 | }
27 | }
28 |
29 | # And now, let's the magic works !
30 | Get-NetTCPConnection -RemotePort $RemotePort -State Established |
31 | Select-Object -Property RemoteAddress, OwningProcess, RemotePort, $Process, $DarkAgent |
32 | Format-Table
33 | ````
34 |
35 | [Nota] We could also use as alternatives
36 |
37 | ````powershell
38 | (Invoke-RestMethod -Uri "http://ipwhois.app/json/$IP").org
39 | (Invoke-RestMethod -Uri "http://iplist.cc/api/$IP").asn.name
40 | (Invoke-RestMethod -Uri "https://ipapi.co/$IP/json").org
41 | ````
42 |
43 | Thanks to u/Postanote in https://new.reddit.com/r/PowerShell/comments/mo1kyb/resolving_getnettcpconnection_owning_service/ to give this way.
44 |
--------------------------------------------------------------------------------
/Tips - Récupérer Logon-Logoff des utilisateurs & Machine.md:
--------------------------------------------------------------------------------
1 | # Récupérer Logon/Logoff des utilisateurs + Machine
2 |
3 | ## Le challenge
4 | Alimenter un fichier de log qui peut être exploité facilement plus tard, typiquement un .csv
5 | avec tous les logon/logoff des utilisateurs mais également le nom de la machine et le LogonServer
6 |
7 |
8 | ## Les différentes voies possibles et leur limitation
9 | **Query AD-Users** : on peut obtenir la Date de last login, mais pas la machine. De plus, on ne peut obtenir que la date du lastLogin : cela ne répond donc pas au besoin
10 |
11 | **EventLog Securité** : possible d'avoir toutes les informations recherchées mais il faut interroger tous les DCs, et ensuite "parser" les informations afin de ne récupérer que ce qu'on veut. Possible donc, mais pas facile à mettre en oeuvre
12 |
13 | **LogonScript/LogoffScript** : On peut récupérer toutes les info dans un fichier type .csv facilement pour l'exploiter plus tard
14 |
15 | ## Les étapes
16 | - Etape 1 : Créer un partage. Permissions NTFS Modification au groupe "Domain Computers" (c'est le compte machine qui exécute les GPOs)
17 | - Etape 2 : GPO avec LogonScript/LogoffScript - Lié à l'OU racine ou se trouve les comptes utilisateurs, on évitera autant que possible la racine du domaine. Ci-après les logonScript et LogoffScript
18 |
19 |
20 | ### LogonScript
21 | ````powershell
22 | $logon = [PSCustomObject]@{
23 | Login = $env:USERNAME
24 | ComputerName = $env:COMPUTERNAME
25 | Date = Get-Date -f "dd-MM-yyyy"
26 | Heure = Get-Date -Format "hh:mm:ss"
27 | LogonServer = $env:LOGONSERVER -replace("\\","")
28 | Action = "Logon"
29 | }
30 |
31 | # Afin d'éviter d'avoir un fichier qui deviendra énorme, on va générer un fichier par jour
32 | $Date = Get-Date -Format "dd-MM-yyyy"
33 | $logon | Export-Csv -Path \\Server\Share\LoginLogoff-$Date.csv -Encoding UTF8 -Delimiter ";" -NoTypeInformation -Append
34 | ````
35 | Vu le peu d'informations que l'on met à chaque fois dans le fichier, cela ne devrait pas poser de pb de contension. Cela reste cependant à vérifier dans un contexte important.
36 |
37 | Faire la même chose pour le Logoff soit dans le même fichier soit dans un fichier différent.
38 |
39 | ### LogoffScript
40 | ````powershell
41 | $logoff = [PSCustomObject]@{
42 | Login = $env:USERNAME
43 | ComputerName = $env:COMPUTERNAME
44 | Date = Get-Date -f "dd-MM-yyyy"
45 | Heure = Get-Date -Format "hh:mm:ss"
46 | LogonServer = $env:LOGONSERVER -replace("\\","")
47 | Action = "Logoff"
48 | }
49 | $Date = Get-Date -Format "dd-MM-yyyy"
50 | $logoff | Export-Csv -Path \\Server\Share\LoginLogoff-$Date.csv -Encoding UTF8 -Delimiter ";" -NoTypeInformation -Append
51 | ````
52 |
53 |
54 | ## Le mot final
55 |
56 | Simple, rapide, efficace
57 |
--------------------------------------------------------------------------------
/Tips - Sample Self-Auto elevated script.pS1:
--------------------------------------------------------------------------------
1 | # Set ExecutionPolicy for current user (TEST)
2 | # Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Force -Scope CurrentUser
3 |
4 | # Auto Elevate
5 | Write-Output 'Checking for Elevated privileges for this process'
6 | # Self-elevate the script if required
7 |
8 | # Get the ID and security principal of the current user account
9 | $MyWindowsID = [System.Security.Principal.WindowsIdentity]::GetCurrent()
10 | $MyWindowsPrincipal = New-Object System.Security.Principal.WindowsPrincipal($MyWindowsID)
11 |
12 | # Get the security principal for the Administrator role
13 | $AdminRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator
14 |
15 | # Check to see if we are currently running "as Administrator"
16 | if ($MyWindowsPrincipal.IsInRole($AdminRole))
17 | {
18 | # We are running "as Administrator" - so change the title and background color to indicate this
19 | # $Host.UI.RawUI.WindowTitle = $myInvocation.MyCommand.Definition + "(Elevated)"
20 | # $Host.UI.RawUI.BackgroundColor = "DarkBlue"
21 | # Clear-Host
22 | # Set execution policy to unrestricted
23 | # Echo Write-Host "Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Force -Scope CurrentUser" | PowerShell.exe -noprofile
24 | }
25 | else
26 | {
27 | # We are not running "as Administrator" - so relaunch as administrator
28 |
29 | # Create a new process object that starts PowerShell
30 | $NewProcess = New-Object System.Diagnostics.ProcessStartInfo 'PowerShell'
31 |
32 | # Specify the current script path and name as a parameter
33 | $NewProcess.Arguments = $MyInvocation.MyCommand.Definition
34 |
35 | # Indicate that the process should be elevated
36 | $NewProcess.Verb = 'runas'
37 |
38 | # Start the new process
39 | [System.Diagnostics.Process]::Start($NewProcess)
40 |
41 | # Exit from the current, unelevated, process
42 | exit
43 | }
44 |
45 |
46 | # Sample script code (replace by your own)
47 | Write-Output 'Hello PS is running in RunAsAdmin mode'
48 | Pause # in this sample code, pause is just to avoir the close the shell after executing the code
49 |
--------------------------------------------------------------------------------
/Tips - Some ways to check if a .ps1 file is running in RunAsAdmin Mode.md:
--------------------------------------------------------------------------------
1 | # Some ways to check if a .ps1 file is running in RunAsAdmin mode
2 |
3 | ## using a function
4 |
5 | ````powershell
6 | function Test-ISElevated
7 | {
8 | $id = [System.Security.Principal.WindowsIdentity]::GetCurrent()
9 | $p = New-Object System.Security.Principal.WindowsPrincipal ($id)
10 | if ($p.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator))
11 | {
12 | Write-Output $true
13 | Write-Output " You're in RunAsAdmin mode"
14 | }
15 | else
16 | {
17 | Write-Output $false
18 | }
19 | }
20 |
21 | if (-not $(Test-IsElevated))
22 | {
23 | Write-Error -Message 'Access Denied. Please run with Administrator privileges.'
24 | exit 1
25 | }
26 | ````
27 | The previous code test if the script is running in RunAsAdmin mode, but it doesn't switch to this mode.
28 |
29 |
30 | ## Self Evelevating script
31 |
32 | ````powershell
33 | if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]'Administrator'))
34 | {
35 | Start-Process powershell.exe '-NoProfile -ExecutionPolicy Bypass -File ' $PSCommandPath"" -Verb RunAs
36 | #exit
37 | }
38 | ````
39 | This code test if the script is running in a RinAsAdmin mode and if not the case, launch another powershell instance of powershell and exec the script once again
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/Tips - Sort PS results in both ascending and descending order.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | You want to sort something in powershell with one property sorte descending and another one sorting ascending.
3 | Use the property parameter with a hash table to specify the property names and their order
4 |
5 | Thanks Mike F Robbins : https://mikefrobbins.com/2019/05/09/sort-powershell-results-in-both-ascending-and-descending-order/
6 | #>
7 | Get-Service -Name Win* |
8 | Sort-Object -Property `
9 | @{expression = 'Status'
10 | descending = $true},
11 | @{expression = 'DisplayName'
12 | descending = $false}
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Tips - Speed up your Powershell Scripts.md:
--------------------------------------------------------------------------------
1 | # Speed up your powershell scripts
2 |
3 | 1 - Avoid using ````+=```` to add to an array that creates a new one every time you add anything to it.
4 | for small collections, the copy is fast. However, if your collection is large [lots of items OR ram-intensive items], you will see a steadily slower process.
5 | ==> the recommended solution is to use a collection that has a real ````.Add()```` method **OR** to assign the whole loop to your ````$Collection```` var.
6 |
7 | ````powershell
8 | # avoid
9 | $Array = @()
10 | foreach ($User in $UserList){
11 | $array += [PSCustomObject]@{
12 | UserName = $User.Name
13 | Email = $User.SamAccountName
14 | }
15 | }
16 |
17 | # prefer
18 | $Array = foreach ($User in $UserList){
19 | [PScustomObject]@{
20 | UserName = $User.Name
21 | Email = $User.SamAccountName
22 | }
23 | }
24 |
25 | # Another solution : less memory consumption
26 | $Array = [System.Collections.Generic.List[PSObject]]::New()
27 | $Array = foreach ($User in $UserList){
28 | $Array.Add([PSCustomObject]@{
29 | UserName = $User.Name
30 | Email = $User.SamAccountName
31 | })
32 | }
33 | ````
34 |
35 |
36 | 2 - the ````New-Object```` cmdlet is known for slowness.
37 | If you are only doing this a few times, it is not meaningful. However, it mounts up. Use the ````[PSCustomObject]```` accelerator instead.
38 |
39 | ````powershell
40 | # Avoid this
41 | $Object = "" | Select-Object -Property Int,Ext, AddText, RemText, FileName, FilePath, Error
42 | $Object = New-Object PSObject @{
43 | Int = ''
44 | Ext = ''
45 | AddText = ''
46 | RemText = ''
47 | FileName = ''
48 | FilePath = ''
49 | Error = ''
50 | }
51 |
52 | $CustomObject =[PSCustomObject]@{}
53 | foreach ($Param in $PSBoundParameters.GetEnumerator())
54 | {
55 | Add-Member -InputObject $CustomObject -MemberType NoteProperty -Name $Param.key -Value $Param.value
56 | }
57 |
58 | # prefer
59 | [PSCustomObject]@{
60 | Int = ''
61 | Ext = ''
62 | AddText = ''
63 | RemText = ''
64 | FileName = ''
65 | FilePath = ''
66 | Error = ''
67 | }
68 |
69 | # Or for Powershell V2
70 | New-Object PSObject@{
71 | Int = ''
72 | Ext = ''
73 | AddText = ''
74 | RemText = ''
75 | FileName = ''
76 | FilePath = ''
77 | Error = ''
78 | }
79 | $CustomObject = [PSCustomObject]@{
80 | Param = $Param
81 | }
82 | ````
83 |
84 | 3 - Assigning values to props after defining the object.
85 | It's a very minor thing, but you can make the assignment when you define the property.
86 |
87 | 4 - Avoid to use use pipeline to filter if you can filter in the first cmdlet
88 |
89 | ````powershell
90 | # avoid this
91 | Get-ChildItem c:\backups\*.bak | Copy-Item -Destination d:\backups
92 | # prefer this
93 | Get-ChildItem -Path c:\backups -filter *.bak | Copy-Item -Destination d:\backups
94 | ````
95 |
--------------------------------------------------------------------------------
/Tips - Split a DistinguishedName.ps1:
--------------------------------------------------------------------------------
1 | # Split un DistinguishedName pour en extraire une information précise
2 | $DistinguishedName = "CN=PC12345,OU=_Ordinateurs,OU=secteur,OU=direction,OU=entreprise,DC=domaine,DC=intra"
3 | $DistinguishedName
4 | # On voit que ce qui sépare le ComputerName du reste c'est "," ou encore ",OU="
5 | # On va utiliser l'opérateur de comparaison SPLIT
6 | # Le premier paramètre est "ce que recherché", le second paramètre est le nombre de fragments de recherche max
7 | $result = $DistinguishedName -split ",OU=", 2
8 | $result # On a bien le DistinguishedName de l'OU
9 | $result = $DistinguishedName -split ",", 2
10 | $result # ici aussi
11 | $result = $DistinguishedName -split ",", 5
12 | $result # et là encore, tout dépend de ce qu'on cherche
13 | <#
14 | Il suffit d'appeler le résultat et de sortir le fragment qu'on veut.
15 | [0] : retourne le 1er morceau : donc le CN
16 | [1] : retourne le 2ème morceau
17 | [2] : retourne le 3ème morceau
18 | ...
19 | [-1] : retourne le dernier morceau
20 | #>
21 | $result = $DistinguishedName -split ",OU=", 2
22 | $result[0]
23 | $result[1]
24 | $result[-1]
25 |
26 | # ex. d'Utilisation
27 | $AgeMax = "90"
28 | $DateLimite = (get-Date).AddDays(-$AgeMax)
29 | $Computers = Get-ADComputer -filter * -Properties Name,DistinguishedName,LastLogonDate -filter {LastLogonDate -lt $DateLimite} |
30 | Select-Object Name,
31 | DistinguishedName,
32 | LastLogonDate,
33 | @{ Label = "dif"; Expression = {((Get-Date) - $_.lastlogondate).days }}, # calcul nbre de jours depuis la dernière connexion / aujourd'hui
34 | @{ Label = "OU" ; Expression = {($_.DistinguishedName -split ",ou=",2)[1]}} # OU dans laquelle se situe la machine
35 |
--------------------------------------------------------------------------------
/Tips - Track changes to Active Directory group membership.ps1:
--------------------------------------------------------------------------------
1 | # Track changes to Active Directory group membership
2 | # ref : https://hkeylocalmachine.com/?p=792
3 |
4 | # Location to save log files to. DO NOT put a trailing back slash in here
5 | $FolderPath = "d:\temp\kamal";
6 |
7 | # Define the group name prefix (we will find all groups with this prefix - make sure to include at least one asterisk, or this will fail)
8 | $GroupNamePrefix = "aAdmin_*";
9 |
10 | # Get all groups matching a specific prefix
11 | $Groups = Get-ADGroup -Filter {name -like $GroupNamePrefix};
12 |
13 | # Format todays date to be appended to the output file name
14 | $Today = (Get-Date).tostring("yyyyMMdd");
15 |
16 | # Loop through each group found
17 | foreach ($Group in $Groups) {
18 |
19 | # Get the most recent export of group members (may or may not be yesterday - just most recent)
20 | $PreviousFileObject = Get-ChildItem $FolderPath |
21 | Where-Object {$_.name -like ("*" + $group.name + "*")} |
22 | Sort-Object lastwritetime -Descending |
23 | Select-Object -first 1
24 |
25 | $PreviousGroupMembers = Import-Csv -Path $PreviousFileObject.fullname
26 |
27 | # Get current group members from Active Directory
28 | $CurrentGroupMembers = $Group |
29 | Get-ADGroupMember |
30 | Select-Object samaccountname
31 |
32 | # Compare both lists to find new members and removed members
33 | $NewMembers = Compare-Object -ReferenceObject $CurrentGroupMembers -DifferenceObject $PreviousGroupMembers -Property samaccountname |
34 | Where-Object {$_.sideindicator -eq "<="} |
35 | Select-Object samaccountname
36 |
37 | $RemovedMembers = Compare-Object -ReferenceObject $CurrentGroupMembers -DifferenceObject $previousgroupmembers -Property samaccountname |
38 | Where-Object {$_.sideindicator -eq "=>"} |
39 | Select-Object samaccountname
40 |
41 |
42 | #----------------[ Do something with the the lists of new and removed users here ]----------------
43 |
44 |
45 | # Save current members to new file
46 | $NewFilePath = $FolderPath + "\" + $Today + "_" + $Group.name + ".csv"
47 | $CurrentGroupMembers | Export-Csv -Path $NewFilePath -Delimiter ";" -NoTypeInformation
48 | }
--------------------------------------------------------------------------------
/Tips - Use-RunAsAdministrator.ps1:
--------------------------------------------------------------------------------
1 | Write-host "Check if script is running as Administrator, and if not, use RunAs to launch a new shell in RunAs" -ForegroundColor Magenta
2 | # Get the ID and security principal of the current user account
3 | $myWindowsID = [System.Security.Principal.WindowsIdentity]::GetCurrent()
4 | $MyWindowsPrincipal = new-object System.Security.Principal.WindowsPrincipal($myWindowsID)
5 |
6 | # Get the security principal for the Administrator role
7 | $adminRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator
8 |
9 | if ($myWindowsPrincipal.IsInRole($adminRole)) # We are running "as Administrator"
10 | {
11 | $Host.UI.RawUI.WindowTitle = $myInvocation.MyCommand.Definition + "(Elevated)"
12 | #Clear-Host
13 | } # End if
14 | else # we are not running "as administrator"
15 | {
16 | # We are not running "as Administrator" - so relaunch as administrator
17 |
18 | <# Old and long way
19 | Create a new process object that starts PowerShell
20 | $NewProcess = New-Object System.Diagnostics.ProcessStartInfo "PowerShell_ISE" # Or Powershell
21 | # Specify the current script path and name as a parameter
22 | $NewProcess.Arguments = $myInvocation.MyCommand.Definition
23 | # Indicate that the process should be elevated
24 | $NewProcess.Verb = "runas"
25 | # Start the new process
26 | [System.Diagnostics.Process]::Start($newProcess)
27 | #>
28 | # New and short way
29 | Start-Process PowerShell_ise -Verb Runas -ArgumentList ($myInvocation.MyCommand.Definition)
30 | # Exit from the current, unelevated, process
31 |
32 | exit
33 | } # End else
34 |
35 | Write-Host "The Script is Running As Administrator" -ForegroundColor Magenta
36 |
--------------------------------------------------------------------------------
/Tips - Usefull Well-known Ports.md:
--------------------------------------------------------------------------------
1 | TCP:22 SSH
2 |
3 | TCP:53 DNS
4 |
5 | TCP:80 HTTP
6 |
7 | TCP:88 Kerberos
8 |
9 | TCP:135 RPC Endpoint mapper
10 |
11 | TCP:389 LDAP
12 |
13 | TCP:443 HTTPS
14 |
15 | TCP:445 SMB
16 |
17 | TCP:464 Kerberos Change/Set Password
18 |
19 | TCP:636 LDAP-S
20 |
21 | TCP:1025-5000 RPC Dynamic Port Range (Legacy)
22 |
23 | TCP:1688 Windows Activation
24 |
25 | TCP:3268 GC
26 |
27 | TCP:3269 GC-S
28 |
29 | TCP:3389 RDP (Remote Desktop)
30 |
31 | TCP:5722 RPC DFSR
32 |
33 | TCP:5723 SCOM
34 |
35 | TCP:5985 Windows Remote Management (WinRM)
36 |
37 | TCP:8014 SEP
38 |
39 | TCP:8530 SCCM
40 |
41 | TCP:8531 SCCM
42 |
43 | TCP:40960 AD Replication (Custom)
44 |
45 | TCP:40961 NetLogon (Custom)
46 |
47 | TCP:40962 DFS Replication (Custom)
48 |
49 | TCP:49152-65535 RPC Dynamic Port Range
50 |
51 | UDP:53 DNS
52 |
53 | UDP:67 DHCP
54 |
55 | UDP:123 NTP
56 |
57 | UDP:389 LDAP
58 |
59 | UDP:464 Kerberos Change/Set Password
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/Tips - Using différent menus in script.md:
--------------------------------------------------------------------------------
1 | # Using different Menus in a script
2 |
3 | ## Building a function
4 | The first step is to build a function, remember always thinking code reuse :-).
5 |
6 | The role of this function is to display the Menu, based of some imputs.
7 |
8 | ````powershell
9 | function Invoke-Menu
10 | {
11 | [CmdletBinding(SupportsShouldProcess = $true)]
12 | Param
13 | (
14 | # Menu Item
15 | [Parameter(Mandatory = $true,
16 | ValueFromPipeline = $true,
17 | ValueFromPipelineByPropertyName = $true,
18 | Position = 0)]
19 | $MenuItem,
20 |
21 | # Foreground Color
22 | [Parameter(Position = 1)]
23 | [String]
24 | $ForegroundColor = 'White',
25 |
26 | # Background Color
27 | [Parameter(Position = 2)]
28 | [String]
29 | $BackgroundColor = 'blue'
30 | )
31 |
32 | Begin
33 | {
34 | }
35 | Process
36 | {
37 | if ($pscmdlet.ShouldProcess('Computer'))
38 | {
39 | # Menu items are displayed consistently using a foreach loop
40 | foreach ($Item in $MenuItem)
41 | {
42 | Write-Host -Object "Enter $($Item.Key) for : $($Item.Text)" -ForegroundColor $ForegroundColor -BackgroundColor $BackgroundColor
43 | }
44 | }
45 | }
46 | End
47 | {
48 | # Read input from user
49 | $Result = Read-Host -Prompt 'Please select your choice'
50 | Write-Host 'the value selected var is : ' -ForegroundColor Green -NoNewline
51 | Write-Host "$Result" -ForegroundColor Yellow
52 | Return $Result
53 | }
54 | }# end function
55 | ````
56 |
57 | As you can see, Menu items are defined as array of hash tables in order to not needing long Write-Host sequences and to provide consistent formatting.
58 | Arrays are defined using @() Hashtables are defined using @{}
59 |
60 | Here a sample of this array of HashTable. I called it ````$MainMenu````
61 |
62 | ````powershell
63 | $MainMenu = @(
64 | @{Text = 'Menu item 1'; Key = 'a' }
65 | @{Text = 'Menu item 2'; Key = 'b' }
66 | @{Text = 'Menu item 3'; Key = 'c' }
67 | @{Text = 'Quit'; Key = 'q' }
68 | )
69 | ````
70 |
71 | ## Using the function
72 |
73 | At this step, we can use the previous function in a script
74 |
75 | ````powershell
76 | do
77 | {
78 | $Out = $false # Init
79 | $Result = Invoke-Menu -MenuItem $MainMenu -ForegroundColor yellow -BackgroundColor darkblue
80 | # Process the user input. In a real script, replace Write-Host with custom actions
81 | switch ($Result)
82 | {
83 | 'a'
84 | {
85 | Write-Host 'Do a'
86 | }
87 | 'b'
88 | {
89 | Write-Host 'Do b'
90 | }
91 | 'c'
92 | {
93 | Write-Host 'Do c'
94 | }
95 | # If user presses q, exit the Do ... While loop
96 | 'q'
97 | {
98 | $Out = $true
99 | }
100 | # The default branch is executed, if the user input is something
101 | # other.
102 | Default
103 | {
104 | Write-Host 'Wrong input' -BackgroundColor 'red' -ForegroundColor 'white'
105 | }
106 | }
107 | # Display the menu as an endless loop (until q is pressed).
108 | } While ($Out -ne $true)
109 | ````
110 |
111 | Later in a script, we could use another menu using the previous function.
112 |
113 | i.e. :
114 |
115 |
116 |
117 |
118 | ````powershell
119 | $SubMenu1 = @(
120 | @{Text = 'SubMenu item 1'; Key = 'a' }
121 | @{Text = 'SubMenu item 2'; Key = 'b' }
122 | @{Text = 'SubMenu item 3'; Key = 'c' }
123 | @{Text = 'Quit'; Key = 'q' }
124 | )
125 |
126 | do
127 | {
128 | $Out = $False # Init
129 | $Result = Invoke-Menu -MenuItem $SubMenu1
130 | # Process the user input. In a real script, replace Write-Host with custom actions
131 | switch ($Result)
132 | {
133 | 'a'
134 | {
135 | Write-Host 'Do a'
136 | }
137 | 'b'
138 | {
139 | Write-Host 'Do b'
140 | }
141 | 'c'
142 | {
143 | Write-Host 'Do c'
144 | }
145 | # If user presses q, exit the Do ... While loop
146 | 'q'
147 | {
148 | $Out = $true
149 | }
150 | # The default branch is executed, if the user input is something
151 | # other.
152 | Default
153 | {
154 | Write-Host 'Wrong input' -BackgroundColor 'red' -ForegroundColor 'white'
155 | }
156 | }
157 | # Display the menu as an endless loop (until q is pressed).
158 | } while ($Out -ne $true)
159 | ````
160 |
161 | > As you could see, in this second sample, I'm using the function with the default value for ````Background```` and ````Foreground```` Parameters.
162 |
163 | Hope this is useful
164 |
--------------------------------------------------------------------------------
/Tips - Utiliser le Module EZLog efficacement.ps1:
--------------------------------------------------------------------------------
1 | Import-Module EZLog
2 |
3 | ############################
4 | # Exemple d'utilisation
5 | ############################
6 |
7 |
8 |
9 | $Date = Get-Date -Format "dd-MM-yyyy-hh-MM-ss"
10 | #$LogFile = Join-Path -Path $PSScriptRoot MylogFile-$Date.log est préférable
11 | $LogFile = Join-Path -Path c:\temp -ChildPath MylogFile-$Date.log
12 |
13 | # Commencer par définir les paramètres une fois pour toutes pour se faciliter la tâche dans le script plus tard
14 | $PSDefaultParameterValues = @{ 'Write-EZLog:LogFile' = $LogFile ; # On dit à la cmdlet Write-EZlog que son paramètre Logfile a la valeur $Logfile
15 | 'Write-EZLog:Delimiter' = ';' ; # On dit à la cmdlet Write-EZlog que son paramètre défimiter est un ;
16 | 'Write-EZLog:ToScreen' = $true } # On dit à la cmdlet Write-EZlog que son paramètre ToScreen est à $true (affichage en console en plus d'inscription en fichier de log)
17 | ####" une fonction est définie dans mon script
18 | Function Myfunction
19 | {
20 | [CmdletBinding()]
21 | Param
22 | (
23 | # Name help description
24 | [Parameter(Mandatory=$true,
25 | ValueFromPipelineByPropertyName=$true,
26 | Position=0)]
27 | [String]$Name
28 | )
29 | Begin
30 | {
31 | Write-EZLog -Category INF -Message "Le nom qui a été saisi est $Name"
32 | }
33 | Process
34 | {
35 | Try { # On essaie de faire ce qu'on doit faire
36 | Write-Ezlog -Category INF -Message "On fait les actions qu'on a à faire sur $Name"
37 | Remove-Item -Path $Name -ErrorAction stop # je fais une action totalement bidon juste pour générer une erreur
38 | }
39 | Catch
40 | { # si ça claque des erreurs, on les logs également
41 | $ErrorMessage = $_.Exception.Message
42 | Write-EZLog -Category ERR -Message "Le message d'erreur est $ErrorMessage"
43 | }
44 | }
45 | End
46 | {
47 | Write-EZLog -Category INF -Message "Les actions sont terminées"
48 | }
49 | }
50 |
51 | #### MAIN SCRIPT
52 |
53 | # On initie la création du fichier de log
54 | Write-EZLog -Header
55 | # On appelle la fonction
56 | Myfunction -Name toto
57 | Write-EZLog -Footer # on ajoute le footer et ça ferme le fichier de log en ajoutant des infos qui vont bien
58 |
59 | <# La tête du fichier de log
60 | +----------------------------------------------------------------------------------------+
61 | Script fullname :
62 | When generated : 2019-06-12 11:50:36
63 | Current user : INTRA\OF773298
64 | Current computer : IS220050
65 | Operating System : Microsoft Windows 7 Entreprise
66 | OS Architecture : 64 bits
67 | +----------------------------------------------------------------------------------------+
68 |
69 | 2019-06-12 11:50:36; INF; Le nom qui a été saisi est toto
70 | 2019-06-12 11:50:36; INF; On fait les actions qu'on a à faire sur toto
71 | 2019-06-12 11:50:36; ERR; Le message d'erreur est Cannot find path 'C:\Temp\toto' because it does not exist.
72 | 2019-06-12 11:50:36; INF; Les actions sont terminées
73 |
74 | +----------------------------------------------------------------------------------------+
75 | End time : 2019-06-12 11:50:36
76 | Total duration (seconds) : 0
77 | Total duration (minutes) : 0
78 | +----------------------------------------------------------------------------------------+
79 | #>
80 | # C'est propre, rapide et efficace !
--------------------------------------------------------------------------------
/Tips - Working with collections-System.collections.stack.ps1:
--------------------------------------------------------------------------------
1 | $MyStack = New-Object System.collections.stack
2 | $MyStack.Push("A")
3 | $MyStack.Push("B")
4 | $MyStack.Push("C")
5 | $MyStack.Push("D")
6 | $MyStack
7 | # on peut constater que on a quelque chose qui ressemble à un array
8 | # on peut constater également, que dans le stack, le dernier objet ajouté et le premier retourné.
9 |
10 | # la méthode "Push" alimente le stack en mode FILO (first In, last Out)
11 | $MyStack.pop()
12 | $MyStack
13 | # La méthode Pop récupère le dernier objet qui a été ajouté dans le stack, et Il n'est plus dans la stack après.
14 | $MyStack.peek()
15 | $MyStack
16 | # la méthode Peek récupère le dernier objet qui a été ajouté dans le stack, mais il reste dans la stack après.
17 | $MyStack.count
18 | # la propriété count (c'est une propriété, pas une méthode), retourne le nombre d'objets dans la collection
19 |
20 |
21 | ######## Les usages possibles :
22 | # Affichage des éléments de $Mystack et vidange
23 | Write-Host 'Affichage des éléments de $Mystack et vidange' -ForegroundColor Cyan
24 | while($mystack.count -gt 0)
25 | {
26 | Write-Host "Returning Element -> $($mystack.Peek())" -ForegroundColor Green
27 | start-sleep -Seconds 3
28 | $mystack.Pop()
29 | }
30 | Write-Host "End of example" -ForegroundColor Cyan
31 |
32 | # Plus courant : se déplacer dans une arborescence vers une répertoire enfant et remonter
33 | $MyStack = New-Object System.collections.stack
34 | $Parent = "c:\temp"
35 | $MyStack.Push($Parent) # Ma position actuelle
36 | $MyStack
37 | $Enfant = "C:\Temp\Archives"
38 | $MyStack.push($Enfant)
39 | $MyStack
40 | Set-Location c:\ # ma position actuelle
41 | Get-Location
42 | set-location ($MyStack.Pop()) # me voici dans l'enfant
43 | Get-Location
44 | Set-Location ($MyStack.Pop()) # me voici dans le parent
45 | Get-Location
46 | # On peut ainsi descendre et remonter facilement dans une arborescence
47 |
48 | $MyStack.psbase | Get-Member
--------------------------------------------------------------------------------
/Tips - [System.Convert] et [System.Text.Encoding].ps1:
--------------------------------------------------------------------------------
1 | # obtenir la liste des methodes de conversion via la classe .Net [System.Convert]
2 | [System.Convert]
3 | [System.Convert] | Get-Member -Static
4 |
5 | # On veut "cacher" (offuscation) une chaine de texte. Ex. :
6 | $Data2Encode = ‘PowerShell is Great!’
7 | # Il existe dans la classe .Net [System.Convert] une méthode ToBase64String, essayons dessus
8 | $EncodedText = [System.Convert]::ToBase64String($Data2Encode)
9 | # no way ! Cette méthode requiert que l'entrée soit convertie en Byte avant
10 | [System.Convert] | Get-Member -Static -Name ToBase64String | Select-Object -ExpandProperty definition
11 | # ToBase64String Method static string ToBase64String(byte[] inArray), static string ToBase64String(byte[] inArray, System.Base64Form...
12 |
13 | # Transformons donc $Data2Encode [SystemString] en Bytes
14 | $Bytes = [System.Text.Encoding]::Unicode.GetBytes($Data2Encode)
15 | # Maintenant on est bien en [System.Byte]
16 | # transformons cela en base64
17 | $EncodedText = [System.Convert]::ToBase64String($Bytes)
18 | $EncodedText
19 | # $EncodedText a été transformé en [System.string]
20 | $EncodedText | Get-Member
21 |
22 | <#
23 | Pour cacher une chaine de caractères, il faut donc
24 | 1 - Encode cette chaine en Byte via [System.Text.Encoding]::Unicode.GetBytes
25 | 2 - Convertir ces Bytes en Base64String via [System.Convert]::ToBase64String
26 |
27 | Cette chaine de caractères peut être une simple chaine de texte comme présenté, mais également
28 | des commandes exécutables, par ex.
29 | $command = 'Start-BitsTransfer -Source "http://www.funnycatpix.com/_pics/Playing_A_Game.jpg" -Destination "$env:USERPROFILE\desktop\cat.jpg"'
30 | $bytes = [System.Text.Encoding]::Unicode.GetBytes($command)
31 | Lignes de commande qui peut être encodées en Base64String
32 | $encodedCommand = [Convert]::ToBase64String($bytes)
33 | $EncodedCommand
34 | afin de la cacher, ou de répondre à une exigence particulière (ex. création d'une clé de registre qui attend une string)
35 | New-ItemProperty -Path HKLM:\software -Name "updater" -Value $encodedCommand -PropertyType multistring
36 | #>
37 |
38 | # pour décoder tout cela, il faut faire les opérations inverses
39 | # chargement des données encodées à décoder
40 |
41 | $Data2Decode = $EncodedText # pour une clé de registre cela serait (Get-ItemProperty HKLM:\software).updater
42 | # conversion depuis Base64String vers Byte
43 | $bytes = [System.Convert]::FromBase64String($Data2Decode)
44 | $Bytes
45 | # transformation de Byte en String
46 | $DecodedText = [System.Text.Encoding]::Unicode.GetString($bytes)
47 | $DecodedText
48 |
--------------------------------------------------------------------------------
/Tips - how to look for all installed software or a specific one.ps1:
--------------------------------------------------------------------------------
1 | ###### Gather all Installed programs ######
2 | # They could be located at these 2 registry paths
3 | $paths = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*',
4 | 'HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
5 | # Here the properties that interessed me.
6 | $props = @(
7 | @{label ='Architecture'; expression = { if($_.PsParentPath -match 'SysWow'){'32-bit'}else{'64-bit'}}},
8 | 'Publisher',
9 | 'Version',
10 | 'DisplayName',
11 | 'UninstallString'
12 | )
13 | # And now Gather thel all
14 | Get-ItemProperty $paths | Select-Object $props
15 |
16 | # a another way is the following : assign the output to a variable and call the interesting property
17 | $Products = Get-ItemProperty $paths | Select-Object $props
18 | $Products.DisplayName
19 |
20 | # And now to look for a specific software
21 | $SoftwareTitle = "Mozilla"
22 | $Product = Get-ItemProperty $paths | where {$_.DisplayName -match $SoftwareTitle} | Select-Object $props
23 | $Product.DisplayName
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Tips - how to move a bunch of files based on the name.md:
--------------------------------------------------------------------------------
1 | # THE PROBLEM
2 | In a folder we have a bunch of files. Theses files are part of video movies and the different parts of the same movie begin by the same number 1, 2, 3, ...)
3 |
4 | i.e. :
5 | 01-movieABC-Part1.xxx - 01-movieABC-Part2.xxx - 01-movieABC-Part3.xxx ... are part from the same video file.
6 | 02-movieXYZ-Part1.xxx - 02-movieXYZ-Part2.xxx - 02-movieXYZ-Part3.xxx ... are part from another video file
7 |
8 | Question from a Reddit user I answered.
9 |
10 | # THE QUESTION
11 | **How to move these files to another folder with sub-folders named by the number ?**
12 |
13 | # THE SOLUTION
14 | Of course the solution is to use some lines of powershell to do this.
15 |
16 | ## First Step : Define some variables
17 | Cause, you know, as every good admin, I'm a lazy man. :-)
18 | ````powershell
19 | $TargetFolder = "C:\Temp2" # Path where the files will be moved
20 | $SuffixForFolder = "Lectures" # Suffix for the Target subfolders (Naming convention)
21 | $NbrFoldersToCreate = "45" # Number of folders to create
22 | $PathToFiles = "c:\temp" # Path where are currently located videos files before moving them
23 | ````
24 | >|Nota] About the naming convention : All the Target sub-folders will be like this : `01-lectures`, `02-Lectures`, `03-Lectures`
25 | > But, you can choose every naming convention you want according to your need
26 |
27 |
28 | ## Second Step : Creating the Target folders
29 | ````powershell
30 | 1..$NbrFoldersToCreate |
31 | foreach {
32 | New-Item -Path $TargetFolder\$_-$SuffixForFolder -ItemType Directory
33 | }
34 | ````
35 | Add at the end `| Out-Null` if you won't any output in console
36 |
37 | >[Nota] : And now, do you understand why i've defined some variables in the beginning ? :-)
38 |
39 | ## Third Step : Gather the list of file and Moving them to the corresponding folder
40 | ````Powershell
41 | # Gather the list of files
42 | $Files = Get-ChildItem -Path $PathToFiles -File
43 | ````
44 | Collecting only files, use `-recurse` only if you have sub-folders on the current path
45 |
46 | # And now moving
47 | ````powershell
48 | foreach ($file in $files)
49 | {
50 | $begin = ($file.Name).Split('-')[0]
51 | # At this step we have identified the beginning of the file, then we can move it easily to the appropriate folder
52 | Move-Item -Path $file.FullName -Destination "$TargetFolder\$begin-$SuffixForFolder"
53 | Write-Host "$($file.FullName)" -ForegroundColor Yellow -NoNewline
54 | Write-Host " has been moved to " -ForegroundColor Green -NoNewline
55 | Write-Host "$TargetFolder\$begin-$SuffixForFolder" -ForegroundColor Yellow
56 | }
57 | Write-Host "All files in" -ForegroundColor Cyan -NoNewline
58 | Write-Host " $PathToFiles " -ForegroundColor Magenta -NoNewline
59 | Write-Host "has been moved to "-ForegroundColor Cyan -NoNewline
60 | Write-Host "$TargetFolder" -ForegroundColor Magenta
61 | ````
62 | Explanations :
63 | Extracting the first letter/number of the name.
64 | Assume that the file are named like this : 11-textQFF. Modify to the need if necessary
65 | `($file.Name).Split('-')[0]` : This return only fist part separate by the split character `-` then this return only the number
66 |
67 | >[Nota] : I used Write-Host to have a nice presentation in the console. If this should be planned, it isn't in the best-practices to use this cmdlet (See PSScriptAnalyzer rules)
68 |
69 |
70 | # CONCLUSION
71 | How it's simple to do something (specially when this is a boring task) with powershell when the problem is well identified.
--------------------------------------------------------------------------------
/Tips - how to use Invoke-Expression cmdlet.ps1:
--------------------------------------------------------------------------------
1 | # how to use Invoke-Expression cmdlet
2 | # ref : https://adamtheautomator.com/invoke-expression/
3 |
4 |
5 |
6 | #Run a PowerShell command via Invoke-Expression
7 | $Command = 'Get-Process'
8 | Invoke-Expression -Command $Command
9 |
10 | #Execute a script via Invoke-Expression
11 | $MyScript = '.\MyScript.ps1'
12 | Invoke-Expression -Command $MyScript
13 |
14 |
15 | # These don't work
16 | $MyScript = "C:\Folder Path\MyScript.ps1"
17 | #or
18 | $MyScript = "'C:\Folder Path\MyScript.ps1'"
19 | Invoke-Expression $MyScript
20 |
21 | # this works
22 | $MyScript = "C:\'Folder Path'\MyScript.ps1"
23 | Invoke-Expression $MyScript
24 |
25 | # another way
26 | $scriptPath = 'C:\Scripts\MyScript.ps1'
27 | $params = '-Path "C:\file.txt" -Force'
28 | Invoke-Expression "$scriptPath $params"
29 | # or
30 | $string = 'C:\Scripts\MyScript.ps1 -Path "C:\file.txt" -Force'
31 | Invoke-Expression $string
32 |
33 | $a = "Get-Process"
34 | ## Doesn't work
35 | & "$a pwsh"
36 | # this works
37 | Invoke-Expression -Command "$a pwsh"
38 |
39 | <# Invoke-Command vs Invoke-Expression
40 | Invoke-Command is preferable if you are writing the executed commands now,
41 | as you retain intellisense in your IDE
42 | whereas Invoke-Expression would be preferable if you wanted to call another script from within your current one.
43 | #>
44 | #These both work the same way, but we lost our intellisense with the Invoke-Expression example.
45 | Invoke-Command -ScriptBlock {
46 | Get-Process Chrome
47 | Get-Process Powershell
48 | }
49 |
50 | Invoke-Expression -Command "
51 | Get-Process Chrome
52 | Get-Process Powershell
53 | "
54 |
55 | <# If you have multiple commands to execute, even though Invoke-Expression only accepts a string rather than an array,
56 | we can use the PowerShell pipeline to send objects down the pipeline one at a time.
57 | #>
58 | # Doesn't work
59 | $MyCollection = @(
60 | 'Get-Process firefox',
61 | 'Get-Service bits'
62 | )
63 | Invoke-Expression $MyCollection
64 |
65 | # Works
66 | 'Get-Process firefox', 'Get-Service bits' | Invoke-Expression
67 | #this works too
68 | $MyCollection | Invoke-Expression
69 | <#
70 | You should be very cautious with using Invoke-Expression with user input.
71 | If you allow a prompt to a user in a way that gives them access outside of the command you are intending to execute,
72 | it could create an unwanted vulnerability. Here is one way you can safely implement user input with Invoke-Expression.
73 | #>
74 | do{
75 | $Response = Read-Host "Please enter a process name"
76 | $RunningProcesses = Get-Process
77 |
78 | #Validate the user input here before proceeding
79 | if($Response -notin $RunningProcesses.Name){
80 | Write-Host "That process wasn't found, please try again.`n" #Avoid using $Response here
81 | }
82 | } until ($Response -in $RunningProcesses.Name)
83 |
84 | $Command = "Get-Process $Response"
85 | Invoke-Expression $Command
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/Tips - infinite ping in powershell.md:
--------------------------------------------------------------------------------
1 | # The pb : you would do like the old DOS command : infinite ping with -t parameter
2 | ````powershell
3 | ping google.com -t
4 | ````
5 | ... but in powershell. how do you do that ?
6 |
7 | The answer is : "With `Test-connection`" of course, with parameter `-Count` but what integer ?
8 |
9 | # Let's see this quickly together
10 | >[Nota] : Remember in the Quick Tips : https://github.com/myusefulrepo/Tips/blob/master/Tips%20-%20Get%20a%20list%20of%20the%20available%20special%20folders%20with%20the%20%5BEnum%5D%20Accelerator.md
11 | > We had seen that we had these accelerators [Int], [Int16], [Int32], [Int64]
12 | > These accelerators have some properties (to be honest, only 2) MinValue and MaxValue
13 |
14 | Let's test theses accelerators with MaxValue property
15 | ````powershell
16 | [int]::MaxValue
17 | 2147483647
18 | [int16]::MaxValue
19 | 32767
20 | [int32]::MaxValue
21 | 2147483647
22 | [int64]::MaxValue
23 | 223372036854775807
24 | ````
25 | As we can see, [int] is an [Int32]. We can use it for the -Count parameter in the Test-Connection cmdlet.
26 | The only precaution is to ***surround by a parenthesis***
27 | ````
28 | Test-Connection google.com -Count ([int]::MaxValue)
29 | Source Destination IPV4Address IPV6Address Bytes Time(ms)
30 | ------ ----------- ----------- ----------- ----- --------
31 | xxxxxx google.com 216.58.201.238 32 7
32 | xxxxxx google.com 216.58.201.238 32 13
33 | xxxxxx google.com 216.58.201.238 32 8
34 | xxxxxx google.com 216.58.201.238 32 7
35 | xxxxxx google.com 216.58.201.238 32 9
36 | xxxxxx google.com 216.58.201.238 32 9
37 | xxxxxx google.com 216.58.201.238 32 7
38 | xxxxxx google.com 216.58.201.238 32 9
39 | `````
40 |
41 | Simple, isn't it ?
42 |
43 | Hope this helpful
--------------------------------------------------------------------------------
/Tips -Detecting applications, services and systems using LDAP instead of LDAPS.md:
--------------------------------------------------------------------------------
1 | # Detecting applications, services and systems using LDAP instead of LDAPS
2 |
3 | ## principle
4 | Active Directory Domain Services (AD DS) offers many ways to integrate applications and services.
5 |
6 | Traditionally, the Lightweight Directory Access Protocol (LDAP) was used by software developers to integrate. While Kerberos-based Integrated Windows Authentication (IWA) can also be used, LDAP has kept a certain foothold for software solutions, as it is also available on non-Windows and non-IIS-based solutions and can be used to integrate with other directories, besides AD DS.
7 |
8 | LDAP, however, was never envisioned from the start as a protocol for open networks. Eventually, LDAP over SSL (commonly abbreviated as LDAPS and described in RFC 2830) was introduced in 2000 to address the plain-text nature of the original LDAP (LDAPv3, described in RFC 2251).
9 |
10 | Many of the software packages supporting LDAPS have no issues connecting using LDAP, thus removing the need to work with certificates. As appealing as this sounds to AD admins, it should be avoided as the service accounts used to poke around in AD DS through LDAP often have significant privileges. These privileges can be asserted after a malicious person has acquired them through a Meddle in the Middle (MitM) attack.
11 |
12 | LDAP Channel Binding has been introduced to counteract MitM and replay attacks, but it only work when using LDAPS. LDAP should be a thing of the past. All LDAP communications to domain controllers should be LDAPS.
13 |
14 |
15 | [Note] : Using Microsoft Defender for Identity, detecting apps and services using LDAP instead of LDAPS is simple, as there is a built-in detection. However the license requirements for Microsoft Defender for Identity may be considered too steep to answer just this one question.
16 |
17 | Domain Controllers with default settings do not provide the information needed to detect non-S LDAP connections. The 16 LDAP Interface Events diagnostic logging needs to be enabled. This can be achieved using Group Policy or using Windows PowerShell. We use the most efficient way to do this : GPO
18 |
19 |
20 |
21 | # Create GPO
22 | - Open Group Policy management Console
23 | - Create a New Policy called "Enabling LDAP Diagnostics" in the Group Policy Objects container
24 | - Edit the New GPO
25 | - Browse to Computer Configuration ==> Preferences ==> Registry
26 | - Add a new Entry in HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Diagnostics\
27 | Name : "16 LDAP Interface Events"
28 | Value : 2
29 | Type : DWORD
30 | - And validate
31 | - Link the New GPO to the Domain Controller OU
32 |
33 | At this step, All DCs will have the New settings.
34 |
35 | ## Detecting applications, services and systems using LDAP instead of LDAPS
36 | to do this, a simple Powershell Script query EventLog in All DC is the way
37 |
38 | ````Powershell
39 | $Hours = 24
40 | # retreive All DCs
41 | $DCs = Get-ADDomainController -filter *
42 | $InsecureLDAPBinds = @() # Init Array
43 |
44 | # Query EventLog (Directory Service) for All DCs
45 | $FilterHashTable = @{Logname='Directory Service'
46 | Id=2889
47 | StartTime=(Get-Date).AddHours("-$Hours")
48 | }
49 |
50 | ForEach ($DC in $DCs)
51 | {
52 | $Events = Get-WinEvent -ComputerName $DC.Hostname -FilterHashtable @FilterHashTable | Out-null # Out-Null is only to avoid Console display
53 | # Parsing Events
54 | ForEach ($Event in $Events)
55 | {
56 | $eventXML = [xml]$Event.ToXml()
57 | $Client = ($eventXML.event.EventData.Data[0])
58 | $IPAddress = $Client.SubString(0,$Client.LastIndexOf(":"))
59 | $User = $eventXML.event.EventData.Data[1]
60 | Switch ($eventXML.event.EventData.Data[2])
61 | {
62 | 0 {$BindType = "Unsigned"}
63 | 1 {$BindType = "Simple"}
64 | }
65 | # Adding Headers
66 | $Row = "" | select IPAddress,User,BindType
67 | # Populate $Row var
68 | $Row.IPAddress = $IPAddress
69 | $Row.User = $User
70 | $Row.BindType = $BindType
71 | # Adding $row to $InsecureLDAPBinds
72 | $InsecureLDAPBinds += $Row
73 | } #end foreach
74 | } # end foreach
75 | # Display Result in Out-GrivView
76 | $InsecureLDAPBinds | Out-Gridview
77 | ````
78 | Feel free to adjust the code to your need
79 |
80 |
81 | ## Disabling LDAP diagnostics
82 | TO disabled LDAP Diagnostic, modifiy the previous GPO like in the following "HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Diagnostics\"
83 | Name "16 LDAP Interface Events"
84 | Value : 0
85 | Type : DWORD
86 |
87 |
88 |
89 | ### reference
90 | https://dirteam.com/sander/2022/05/30/howto-detect-apps-and-services-using-ldap-instead-of-ldaps/
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/Tips -Get all of the administrators for a collection of remote computers.md:
--------------------------------------------------------------------------------
1 | # Get all of the administrators for a collection of remote computers
2 |
3 | Never mind how you get the list of remote computers : we have a list (and we have previously store it in a var called $Computers), then use it.
4 |
5 | ## Principle
6 | For this, we're looking in the CIM Class *Win32_Group* a well-known SID : **S-1-5-32-544**
7 | This is the DIS of the Administrators (local) group.
8 | Then, in this group, we're looking for the CIM Class associated : *Win32_UserAccount*
9 |
10 | ## In action
11 |
12 | ````powershell
13 | # Gathering info
14 | $Result = Get-CimInstance -ClassName Win32_Group -Filter 'SID = "S-1-5-32-544"' -ComputerName $computerName |
15 | Get-CimAssociatedInstance -ResultClassName Win32_UserAccount
16 |
17 | # Exporting
18 | $Result | Select-Object -Properties * | Export-Csv -Path C:\temp\localAdmins.csv -Delimiter ";" -NoTypeInformation
19 | ````
20 |
21 | [Nota ] : Feel free to select only the properties you would like to have.
22 |
23 | ## Out-dated WMF
24 | If the target servers are 2008R2 or older they may have outdated WMF, in which case you'd have to use a cim session. It's a quick step, though you probably don't need it :
25 |
26 | ````powershell
27 | # Gathering Info
28 | $cimSession = New-CimSession -ComputerName $computerName -SessionOption (New-CimSessionOption -Protocol Dcom)
29 | $Result = Get-CimInstance -ClassName Win32_Group -Filter 'SID = "S-1-5-32-544"' -CimSession $cimSession |
30 | Get-CimAssociatedInstance -ResultClassName Win32_UserAccount
31 |
32 | # Exporting
33 | $Result | Select-Object -Properties * | Export-Csv -Path C:\temp\localAdmins.csv -Delimiter ";" -NoTypeInformation
34 | ````
35 | Like the previous sample, feel free to select the needed properties before exporting.
36 |
37 |
38 | Hope this help
39 |
--------------------------------------------------------------------------------
/Tips -How to play with quser with powershel.md:
--------------------------------------------------------------------------------
1 | # How to play with quser with powershell
2 |
3 | ````quser```` is a legacy DOS tool to identify user logged into a computer
4 | If we want to exploit the result of a ````quser```` command, the result isn't clean for a later use in Powershell
5 |
6 | ## Quick reminder about quser
7 |
8 | ````powershell
9 | $quserResult = quser /server:$Computer 2>&1
10 | ````
11 |
12 | This returns something like the following
13 |
14 | ```` powershell
15 | USERNAME SESSIONNAME ID STATE IDLE TIME LOGON TIME
16 | User console 1 Active none 8/14/2019 6:52 AM
17 | ````
18 |
19 | ## How to works with this
20 |
21 | The solution is to transform the previous result with regex
22 |
23 | ````powershell
24 | $quserRegex = $quserResult | ForEach-Object -Process { $_ -replace '\s{2,}',',' }
25 | ````
26 |
27 | Then, convert the regex result with ````ConvertFrom-Csv```` cmdlet
28 |
29 | ````powershell
30 | $quserObject = $quserRegex | ConvertFrom-Csv
31 | ````
32 |
33 | At this step, wa have a clean object to work with it in Powershell. :-)
34 |
35 | ## Play with the quserobject to logoff connected users
36 |
37 | The simple command is the following
38 |
39 | ````powershell
40 | Logoff $quserObject.ID /server:Computer
41 | ````
42 |
43 | and we can runs this for all connected users with a foreach loop
44 |
45 | ````powershell
46 | foreach ($User in $quserObject)
47 | {
48 | Logoff $User.ID /server:$Computer
49 | ````
50 |
51 | Of course ````$Computer```` is a variable previously define. It could be also an array of computers.
52 |
53 | With an Object, Powershell can run efficiciently. The previous code is just an example.
54 |
55 | We can also build new objects by joining the results of different objects in Powershell
56 |
57 | Hope this help
58 |
59 | Ref source :
60 |
--------------------------------------------------------------------------------
/Tips -How to replace a line break char with a space char.md:
--------------------------------------------------------------------------------
1 | # The Problem
2 | A existing file is like the followwing
3 | ````powershell
4 | Get-Content -Path C:\Temp\test.txt
5 | a
6 | b
7 | c
8 | d
9 | `````
10 |
11 | I would like to transform it like this
12 | ````powershell
13 | a b c d
14 | ````
15 | # First way : use ````-replace````
16 | ````powershell
17 | (Get-Content C:\Temp\test.txt -Raw) -replace '\r\n', ' '
18 | a b c d
19 | # And now update the file
20 | (Get-Content C:\Temp\test.txt -Raw) -replace '\r\n', ' ' | Set-Content -Path c:\temp\Test.txt
21 | ````
22 | >[Explanation] : the ````-Raw```` flag with ````Get-Content````, separates by default the lines into an array of strings, so you never see the new line char.
23 |
24 |
25 | # 2nd way : use the ````-join```` operator
26 | By omitting ````-Raw````, ````Get-Content```` returns an *array of strings* by default which allows you to use the ````-join```` operator.
27 | ````powershell
28 | (Get-Content C:\Temp\test.txt) -join ' '
29 | a b c d
30 | # And now update the file
31 | (Get-Content C:\Temp\test.txt) -join ' ' | Set-Content -Path c:\temp\Test.txt
32 | ````
33 | Remember that the end of a line consists of both a new line and a character return (\n and \r respectively). You need to account for both.
--------------------------------------------------------------------------------
/Tips -recreate-Shares-US.ps1:
--------------------------------------------------------------------------------
1 | <#
2 | .SYNOPSIS
3 | Get the shares (except administrative shares) and the access permissions and build the same shares on another computer
4 |
5 | .DESCRIPTION
6 | Get the shares (except administrative shares) and the access permissions and build the same shares on another computer
7 | Could be useful in a Data migration between 2 servers as a preliminary step (before copy data from source server to target server)
8 |
9 | .EXAMPLE
10 | .\recreate-shares-US.ps1 -TargetComputer RemoteComputer
11 | Run the script locally on the source computer and create same shares on the target computer as a parameter
12 |
13 | .EXAMPLE
14 | .\recreate-shares-US.ps1
15 | Run the script locally on the source computer and create same shares on the target computer with the default value for the Remote Computer
16 |
17 | .INPUTS
18 | none
19 |
20 | .OUTPUTS
21 | none
22 |
23 | .NOTES
24 | Author : Olivier FERRIERE
25 | Date : 25/08/2020
26 | Version : 1.0 - final version after somme comments on reddi (Thanks To Tim Curwick aka MadwithPowershell)
27 | #>
28 |
29 | Param
30 | (
31 | # Remote Computer
32 | [Parameter(Mandatory = $true,
33 | ValueFromPipeline = $true,
34 | ValueFromPipelineByPropertyName = $true)]
35 | $TargetComputer = "RemoteComputer"
36 | )
37 |
38 | # Get All shares except admin share (special) on the current computer
39 | $Shares = Get-SmbShare -Special $false -ErrorAction SilentlyContinue
40 | # Get all ShareAccess Properties
41 | $SharesAccess = foreach ($Share in $Shares)
42 | {
43 | Get-SmbShareAccess -Name $($Share.Name)
44 | }
45 |
46 | # At this step, jump to the Target Computer
47 | Try
48 | {
49 | Invoke-Command -ComputerName $TargetComputer -ScriptBlock {
50 | foreach ($Share in $Using:Shares)
51 | {
52 | # Check if the target directory is still existing : create if necessary
53 | if (-not (Test-Path -Path $Share.Path) )
54 | {
55 | # Path doesn't exist : create
56 | try
57 | {
58 | New-Item -Path $Share.Path -ErrorAction Stop
59 | Write-Output "Path $($Share.Path) has been created"
60 | }
61 | catch
62 | {
63 | Write-Output "an error is occured : $_"
64 | }
65 | }
66 | # Check if the target share is still existing and set access
67 | else
68 | {
69 | Try
70 | {
71 | New-SmbShare -Name $Share.Name -Description $Share.Description -Path $Share.Path -ErrorAction stop
72 | Write-Output "Share $($Share.Name) has been recreated"
73 | # revoke default grand access when you create a new share
74 | Revoke-SmbShareAccess -Name $Share.Name -AccountName "tout le monde" -Force
75 | # add the ref share access
76 | $ShareAccessParams = @{ Name = $Share.Name
77 | AccountName = ($using:SharesAccess | Where-Object { $_.name -eq $share.Name } | Select-Object -ExpandProperty AccountName)
78 | AccessRight = ($using:SharesAccess | Where-Object { $_.name -eq $share.Name } | Select-Object -ExpandProperty AccessRight)
79 | ScopeName = "*"
80 | Force = $true
81 | }
82 | Grant-SmbShareAccess @ShareAccessParams
83 | Write-Output "Access rights on share $($Share.Name) has been replaced"
84 | }
85 | catch
86 | {
87 | Write-Output "an error is occured : $_"
88 | }
89 | }
90 | }
91 | }
92 | }
93 | catch
94 | {
95 | Write-Output "An error is occured : $_"
96 | }
97 |
--------------------------------------------------------------------------------
/Tips- Restrieve Last Day of Month and First Day Next Month.md:
--------------------------------------------------------------------------------
1 | # Le challenge
2 | Déterminer le 1er jour du mois suivant et le dernier jour du du mois courant, pour la date courante
3 |
4 | ## Way 1 :
5 |
6 | ````powershell
7 | $NowMonth = Get-Date -Day 1 -hour 0 -Minute 0 -Second 0
8 | $NowMonth
9 | lundi 1 février 2021 00:00:00
10 | $FirstDayNextMonth = $NowMonth.AddMonths(1)
11 | $FirstDayNextMonth
12 | lundi 1 mars 2021 00:00:00
13 | ````
14 |
15 | ## Way 2 :
16 | ````powershell
17 | $Date = Get-Date
18 | $Date
19 | jeudi 4 février 2021 09:10:11
20 | $NowFirstDayOfMonth = Get-Date -Year $Date.Year -Month $Date.Month -Day 1 -Hour 0 -Minute 0 -Second 0
21 | $NowFirstDayOfMonth
22 | $FirstDayNextMonth = $NowFirstDayOfMonth.addMonths(1)
23 | $FirstDayNextMonth
24 | lundi 1 mars 2021 00:00:00
25 | $LastDayofMonth = $FirstDayNextMonth.addSeconds(-1)
26 | dimanche 28 février 2021 23:59:59
27 | ````
28 |
29 | ## Way 3 :
30 |
31 | ````powershell
32 | [DateTime] $Date = Get-Date
33 | jeudi 4 février 2021 09:26:50
34 | $DaysInMonth = [System.DateTime]::DaysInMonth($Date.Year, $Date.Month)
35 | $DaysInMonth
36 | 28
37 | $LastDayCurrentMonth = Get-Date -Day $DaysInMonth -Year $Date.Year -Month $Date.Month
38 | $LastDayCurrentMonth
39 | dimanche 28 février 2021 09:19:23
40 | $FirstDayNextMonth = $LastDayCurrentMonth.addDays(1)
41 | $FirstDayNextMonth
42 | lundi 1 mars 2021 09:19:23
43 | ````
44 |
45 | ## Way 4 :
46 |
47 | ````powershell
48 | $Date = Get-Date
49 | $Date
50 | jeudi 4 février 2021 09:22:11
51 | $NowFirstDayOfMonth = Get-Date -Year $Date.Year -Month $Date.Month -Day 1 -Hour 0 -Minute 0 -Second 0
52 | $NowFirstDayOfMonth
53 | lundi 1 février 2021 00:00:00
54 | $FirstDayNextMonth = $NowFirstDayOfMonth.addMonths(1)
55 | $FirstDayNextMonth
56 | lundi 1 mars 2021 00:00:00
57 | $LastDayofMonth = $FirstDayNextMonth.addSeconds(-1)
58 | $LastDayofMonth
59 | dimanche 28 février 2021 23:59:59
60 | ````
61 |
62 | ## Way 5 :
63 |
64 | ````Powershell
65 | $FirstDaynextMonth = (Get-Date -Day 1).AddMonths(1)
66 | $FirstDayNextMonth
67 | lundi 1 mars 2021 09:32:32
68 | $LastDayOfMonth = $FirstDayNextMonth.addSeconds(-1)
69 | $LastDayOfMonth
70 | lundi 1 mars 2021 09:32:31
71 | ````
72 |
--------------------------------------------------------------------------------
/Tips- Useful Events to Monitor for Security Reasons.ps1:
--------------------------------------------------------------------------------
1 | # Useful Events to monitor
2 |
3 | # 1 - System Events
4 | <#
5 | ID 104 An event log was cleared
6 | #>
7 | $SystemEvents = Get-WinEvent -FilterHashtable @{
8 | LogName = "System"
9 | id = "104"
10 | } -MaxEvents 50 -ErrorAction SilentlyContinue
11 |
12 | # 2 - Security Events
13 | <#
14 | ID 4656 - Auditing of configured files, registry keys:
15 | PowerShell profiles (*profile*.ps1)
16 | Security settings (HKLM:\Software\Policies\*)
17 | #>
18 | $SecurityEvents = Get-WinEvent -FilterHashtable @{
19 | LogName = "Security"
20 | id = "4656"
21 | } -MaxEvents 50 -ErrorAction SilentlyContinue
22 |
23 | # 3 - Windows Powershell Events
24 | <#
25 | ID 400 - PowerShell Startup, including hosting application, version
26 | ID 800 - Command and Parameter Logging
27 | #>
28 | $PSEvents = Get-WinEvent -FilterHashtable @{
29 | LogName = "Windows Powershell"
30 | id = "400", "800"
31 | } -MaxEvents 50 -ErrorAction SilentlyContinue
32 |
33 | # 3 - Microsoft-Windows-PowerShell/Operational Events
34 | <#
35 | ID 4104 - Warning - ScriptBlock automatic logging – used APIs or techniques commonly associated with malware
36 | ID 4104 - Verbose - ScriptBlock logging
37 | ID 53507 - PowerShell debugger attached to a process
38 |
39 | Nota : 4104/Warning is NOT a replacement for an intrusion detection system.
40 | #>
41 | $PSOperationalEvents = Get-WinEvent -FilterHashtable @{
42 | LogName = "Microsoft-Windows-Powershell/Operational"
43 | id = "4104","53507"
44 | } -ErrorAction SilentlyContinue
45 |
46 | # 4 - Microsoft-Windows-WinRM/Operational Events
47 | <#
48 | ID 91 - User connected to system with PowerShell Remoting
49 | #>
50 |
51 | $WinRMEvents = Get-WinEvent -FilterHashtable @{
52 | LogName = "Microsoft-Windows-WinRM/Operational"
53 | id = "91"
54 | } -ErrorAction SilentlyContinue
55 |
56 | $Result =@()
57 | $Result += $SecurityEvents
58 | $Result += $PSEvents
59 | $Result += $PSOperationalEvents
60 | $Result += $WinRMEvents
61 | $Result
62 |
63 | # Source Jon Fow in "Security 102 - Defending Against Powershell Attacks" (SEC102-PowerShell-Attacks.pptx)
64 | # Adapted from a presentation by Lee Holmes, Lead Security Architect, Azure Management
65 | # Ref. : https://github.com/rtpsug/PowerShell-Saturday/blob/master/2019-NC.State/2019-09-21-PowerShell.Security.102-Jon.Fox/SEC102-PowerShell-Attacks.pptx
66 |
67 |
68 | # ADDENDUM
69 | <#
70 | The PowerShell Injection Hunter module looks for many instances of unsafe coding practices when
71 | PowerShell scripts are exposed to untrusted input
72 |
73 | https://devblogs.microsoft.com/powershell/powershell-injection-hunter-security-auditing-for-powershell-scripts/
74 |
75 | - Install Module : Install-Module InjectionHunter
76 | - Retreive Install Path : Get-Module InjectionHunter -List | Foreach-Object Path
77 | - Create a file Named PSScriptAnalyzerSettings.psd1 in a location of your choice and put the following content inside it :
78 | @{
79 | IncludeDefaultRules = $true
80 | CustomRulePath = "Your\Retreive\Install\Path\InjectionHunter.psd1"
81 | }
82 | - In VS code, Modify the User Settings with the following line
83 | "powershell.scriptAnalysis.settingsPath": "Your\Path\ToFile\PSScriptAnalyzerSettings.psd1"
84 |
85 | When you open a PowerShell script with possible code injection risks,
86 | you should now see Script Analyzer warnings that highlight what they are and how to fix them.
87 |
88 | Ref. : https://devblogs.microsoft.com/powershell/powershell-injection-hunter-security-auditing-for-powershell-scripts/
89 | #>
--------------------------------------------------------------------------------
/Tips- how to work with network settings.md:
--------------------------------------------------------------------------------
1 | # How to work with network settings
2 |
3 | Source :
4 |
5 | ## Enable and Disable NIC
6 |
7 | ### List all network adapters
8 |
9 | ````Get-NetAdapter````
10 |
11 | ### Disable a specific network adapter, for instance the Wi-Fi adapter
12 |
13 | ***by name***
14 |
15 | ````Disable-NetAdapter -Name "Wi-Fi"````
16 |
17 | ***by piping a specific adapter***
18 |
19 | ````Disable-NetAdapter -Name "Wi-Fi"````
20 | ````Get-NetAdapter -InterfaceIndex 5 | Disable-NetAdapter````
21 |
22 | ### Activate a specific network adapter
23 |
24 | ***by name***
25 |
26 | ````Enable-NetAdapter -Name "Wi-Fi"````
27 |
28 | ***by piping a specific adapter***
29 |
30 | ````Get-NetAdapter -InterfaceIndex 5 | Enable-NetAdapter````
31 |
32 | ## Get and set IP address
33 |
34 | ### Get the IP-address of a specific adapter
35 |
36 | ````Get-NetIPAddress -InterfaceIndex 5````
37 |
38 | ### Get just the IPv4-address
39 |
40 | ````Get-NetIPAddress -InterfaceIndex 5 -AddressFamily IPv4````
41 |
42 | ### Just the address itself
43 |
44 | ````(Get-NetIPAddress -InterfaceIndex 5 -AddressFamily IPv4).IPAddress````
45 |
46 | ### Set IPv4-address, using splatting for better readability
47 |
48 | ````powershell
49 | $ipParameter = @{
50 | InterfaceIndex = 22
51 | IPAddress = "10.0.0.22"
52 | PrefixLength = 24
53 | AddressFamily = "IPv4"
54 | }
55 | New-NetIPAddress @ipParameter
56 | ````
57 |
58 | ### Set the adapter to DHCP
59 |
60 | ````Set-NetIPInterface -InterfaceIndex 22 -Dhcp Enabled````
61 |
62 | ## Set DNS server for NIC and reset DNS Cache
63 |
64 | ### Set DNS-server addresses on a specific NIC
65 |
66 | ````powershell
67 | $dnsParameter = @{
68 | InterfaceIndex = 5
69 | ServerAddresses = ("8.8.8.8","8.8.4.4")
70 | }
71 | Set-DnsClientServerAddress @dnsParameter
72 | ````
73 |
74 | ### Clear DNS cache
75 |
76 | ````Clear-DnsClientCache````
77 |
--------------------------------------------------------------------------------
/Tips-Complete the file name on condition.md:
--------------------------------------------------------------------------------
1 | # Complete file name on condition
2 |
3 | ## the objective
4 |
5 | We have a collection of files named like IMG1.jpg, IMG2.jpg, ... IMG5085.jpg
6 | We would mike to rename them if the name has less than 6 characters.
7 |
8 | i.e. :
9 | Before : IMG1.jpg - After : IMG000006.jpg
10 |
11 | ````powershell
12 | Get-ChildItem C:\IMG | ForEach-Object{
13 | $NewName = ([int]$_.BaseName).PadLeft('6','0') + $_.Extension
14 | Rename-Item -Path $_.FullName -NewName $NewName
15 | ````
16 |
17 | ## Explanations
18 |
19 | We're using the PadLeft Method to returns a new string of a specified length in which the beginning of the current string is padded with spaces or with a specified Unicode character.
20 |
21 | [Nota : ] There is another method called PadRight doing a similar job. It's not the answser for the current objective but just to keep in mind for another uses.
22 |
23 |
24 | Hope this help
--------------------------------------------------------------------------------