├── 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 | ![Infra.csv](./Images/infra-csv.png "Infra in a .csv file") 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 --------------------------------------------------------------------------------