Copied from Rob Sickler's Wiki
Powershell is quickly replacing the Command Prompt as the go-to command line interface for many IT admins. While I don't think PS will ever replace CMD, I do feel it will come very close to doing so. For the most part, CMD was written for batch files and basic commands and, as such, has a fair amount of limitations. However, PS was built on the .Net Framework and is now open-source. Between the programming language back-end and the open source community support, PS has far more potential!
Some commands only work in a certain environment. Some commands only work in certain versions. It important to know these things when you try to run various commands or need to troubleshoot scripts.
Knowing the version of Powershell is important. Again, some commands may not work in certain versions. The Windows OS will ship with a version that can likely be updated over the life of the OS so don't think you're stuck with the same version forever.
host
Name : Windows PowerShell ISE Host Version : 5.1.16299.98 InstanceId : 24d2b47e-52f8-4787-a977-d152fdc22529 UI : System.Management.Automation.Internal.Host.InternalHostUserInterface CurrentCulture : en-US CurrentUICulture : en-US PrivateData : Microsoft.PowerShell.Host.ISE.ISEOptions DebuggerEnabled : True IsRunspacePushed : False Runspace : System.Management.Automation.Runspaces.LocalRunspace
If ($host.Version -ige "2.0") {Write-Host "Now we're cooking with gas!"}
If you try to run a script, it may nag about the Execution Policy and fail to run. In those cases, you may need to set the policy appropriately or try bypassing it altogether.
Get-ExecutionPolicy
Set-ExecutionPolicy RemoteSigned
Setting the Execution Policy on a system may not always be the way to go about it. Maybe you're in a production environment and, for security reasons, they don't want to allow PS scripts to run all the time. A good example of a scenario like this is one I worked in before. I ended up creating a Scheduled Task that called Powershell.exe from cmd.exe and did so with a parameter to bypass the policy - just for that execution.
powershell.exe -ExecutionPolicy Bypass -Command "C:\MyAwesomeScript.ps1"
powershell.exe -ExecutionPolicy Bypass -Command "& {Enable-PSRemoting -Force}"
Powershell has the ability to use aliases. You can even define your own! You may run across scripts that contain aliases and you may wonder what they stand for. Below, you'll find examples of how to go about finding aliases and what those aliases stand for. By and large, I don't like to use them in documentation due to readability reasons but I will use them to save some keystrokes when I'm doing something quick & dirty that I didn't write a script for.
Get-Alias
%, before; it's pretty common. If you wanted to know what it meant, you could check it like this: Get-Alias -Name %
Get-Alias -Definition ForEach-Object
For those of you who are playing along at home, if you ran all the commands I just mentioned, you probably saw something like this:
PS C:\WINDOWS\system32> Get-Alias -Name % CommandType Name Version Source ----------- ---- ------- ------ Alias % -> ForEach-Object PS C:\WINDOWS\system32> Get-Alias -Definition ForEach-Object CommandType Name Version Source ----------- ---- ------- ------ Alias % -> ForEach-Object Alias foreach -> ForEach-Object
Using an alias is easy enough; you just use it in lieu of the full cmdlet. For instance, the following three commands will do the exact same thing:
C:\_Drivers\VMware with the use of the ForEach-Object cmdlet: 1..4 | ForEach-Object {mkdir "C:\_Drivers\VMware\test$_"}
ForEach-Object has been substituted with the alias of ForEach: 1..4 | ForEach {mkdir "C:\_Drivers\VMware\test$_"}
ForEach-Object has been substituted with the alias of %: 1..4 | % {mkdir "C:\_Drivers\VMware\test$_"}
Generally speaking, I hate mapped drives in corporate environments. Unless great care is taken to ensure consistency, they quickly become a mess. Think about it… When mapping a drive, you can choose any unused drive letter you want. So, I could map the network share, \\H2P-IOLWEB1\Code, to drive R:\ and tell you that the file you wanted was on said drive. However, you don't have that path mapped the same way I do or, in some cases, you don't have the path mapped at all! So, if I told you to take a look at R:\MyAwesomeScripts\MapDrive.ps1 for some great examples, you wouldn't be able to. Or, at best, you'd need to adjust your path. Moreover, if a path isn't available when the machine starts up, it can throw errors unless you've accounted for that as well. However, there are a few cases where a mapped drive is still useful. For instance, capturing a WIM file and dumping it to a network share from WinPE would be a good example of when using a mapped drive makes sense. You can also map drives via Group Policy and that would allow for some consistency. But, for the most part, I avoid mapped drives unless I'm in WinPE.
New-PSDrive -Name "R" -PSProvider "FileSystem" -Root "\\H2P-IOLWEB1\Code" -Credential rsickler@autotrader.int
-Persist parameter allows it to be visible in Explorer and the drive will still be there after a reboot. However, if you run the command via an elevated prompt, it will not show up in your Explore windows as you've technically mapped the drive for the Administrator's profile: New-PSDrive -Name "R" -PSProvider "FileSystem" -Root "\\H2P-IOLWEB1\Code" -Credential rsickler@autotrader.int -Persist
Remove-PSDrive -Name "R" -Force
This is a very basic function for most people as they tend to just use Explorer to drag-and-drop. However, if you're on a Server Core machine or you're using remote commands through PS, you can copy files via other means.
Copy-Item -Recurse -Path "C:\_Drivers\VMware" -Destination "C:\_Temp2"
Copy-Item -Path "C:\sickler.txt" -Destination "\\h2t11-iolweb1\c$"
Copy-Item -Recurse -Path "C:\sickler" -Destination "\\h2t11-iolweb1\c$" -Credential rsickler@autotrader.int
Copy-Item -Path "\\nas1.homenet.local\it\Software\VMWare\VMware vCenter Server\v6.0.0u1b\VMware-VIMSetup-all-6.0.0-3343019.iso" -Destination "C:\Users\RSICKLER\Desktop"
Copy-Item -Path "$env:SystemRoot\System32\Sysprep\unattend.xml" -Destination "$env:SystemRoot\System32\Sysprep\unattend_original.xml"
Again, this is very basic but it helps to know how to do it via PS.
Move-Item -Path "C:\_Drivers\VMware" -Destination "C:\_Temp2"
Move-Item -Path "C:\_Temp\unattend.xml" -Destination "$env:SystemRoot\System32\Sysprep" -Force
Move-Item -Path "C:\_Temp\unattend.xml" -Destination "$env:SystemRoot\System32\Sysprep\Working_unattend.xml"
Move-Item -Recurse -Path "C:\_Drivers\VMware" -Destination "\\h2t11-iolweb1\c$\_Drivers" -Credential autotrader\rsickler
Again, this is very basic but it helps to know how to do it via PS. Furthermore, this cmdlet can be used by many other providers so you can use it to delete files, folders, registry keys, variables, etc.
Remove-Item -Path "C:\_Drivers\VMware\Info.txt"
Remove-Item -Path "C:\_Drivers\VMware\Info.txt" -Force
Remove-Item -Path "C:\_Drivers\VMware\Win10" -Force -Recurse
This too is a simple task but it's handy to know how to do this via a quick command.
Rename-Item -Path "C:\_Drivers\VMware\Daily.log" -NewName "Monday.log"
Rename-Item -Path "C:\_Drivers\VMware" -NewName "VirtualBox"
Get-ChildItem "C:\_Drivers\VMware\*.log" | Rename-Item -NewName { $_.Name -Replace '\.log$','.txt' }
Dir "C:\Users\User1\VirtualBox VMs\Server2012\WDS_Setup\*.png" | ForEach-Object -begin { $count=1 } -process { Rename-Item $_ -NewName "WDS_Setup_$count.png"; $count++ }
Server 2012 has built-in support for mounting ISOs & VHDs. Sometimes you can just right-click one and mount the file. Worst case, you can also do this via Powershell.
Mount-DiskImage -ImagePath C:\_Temp\SW_DVD5_Lync_2013_32-BIT_X64_English_MLF_X18-54527.ISO
DisMount-DiskImage -ImagePath C:\_Temp\SW_DVD5_Lync_2013_32-BIT_X64_English_MLF_X18-54527.ISO
One can use PS to show running processes. It won't be real-time like what you'd see with Windows Task Manager but it's still helpful for easily finding the PID and other stats since the list won't be bouncing around as other processes come and go. It's even more helpful with processes on a remote machine since you don't need to RDP into it to check the running processes.
Get-Process
Get-Process -Name Explorer
Get-Process -Name Exp*
Get-Process -Name Exp* | Select ProcessName,ID
Get-Process -Name Exp*,Note*
Get-Process | Select ProcessName,Company,FileVersion
Get-Process -ComputerName H2P-IOLRPC2
Get-Process -Name MS* -ComputerName H2P-IOLRPC2
Get-Process -Name MS*,SVC* -ComputerName H2P-IOLRPC2
Killing a local process is very straight forward. Killing a remote process isn't as clean as I'd like it to be but it still works. So long as you have the correct permissions, killing a remote process can be achieved with the Invoke-Command cmdlet.
Stop-Process -Name DesktopInfo* -Force
Invoke-Command -ScriptBlock { Stop-Process -Name DesktopInfo* -Force } -ComputerName h2s2-netappfp1 -Credential autotrader\rsickler
One can use PS to get info on services. It's also helpful with services on a remote machine since you don't need to RDP into it to check the running processes.
Get-Service
Get-Service | Format-Table -AutoSize
Get-Service | Format-List
Get-Service -Name "wuauserv" | Format-Table -AutoSize
As previously mentioned, this is handy for checking on services on remote machines.
Get-Service -ComputerName "wds12" | Format-Table -AutoSize
Get-Service -ComputerName "wds12" -Name "wuauserv" | Format-Table -AutoSize
Sometimes, you need to start, stop or restart a service on a local or remote machine1).
Start-Service -Name "wuauserv"
Stop-Service -Name "wuauserv"
Restart-Service -Name "wuauserv"
Invoke-Command -ComputerName "wds12" -ScriptBlock {Restart-Service -Name "WDSServer" -Force}
PS can do all kinds of things in AD but I typically use it for quick and dirty reporting. From time to time, I'll also use it for bulk actions like cleaning up old computer and user objects.
Import-Module ActiveDirectory
Get-ADComputer –Filter *
Get-ADComputer –Filter * | Format-Table Name,ObjectClass,DistinguishedName -AutoSize
FT is short-hand for Format-Table. the -a is short for Auto-Size: Get-ADComputer –Filter * | FT Name,ObjectClass,DistinguishedName -a
Get-ADComputer -Filter { OperatingSystem -Like '*Windows Server*' } -Properties OperatingSystem | Select-Object Name, OperatingSystem | Export-CSV $env:USERPROFILE\Desktop\Servers.csv -NoTypeInformation
Get-ADComputer -Filter { OperatingSystem -Like '*Windows Server*' } -Properties OperatingSystem | Select-Object Name, OperatingSystem | Format-Table -AutoSize
Get-ADObject -Filter {ObjectClass -eq "Computer"} -SearchBase "OU=STAGE3,OU=Environments,OU=ATC Member Servers,OU=ATC_DD,DC=HOMENET,DC=local" | Format-Table Name
Get-ADComputer -Filter { OperatingSystem -Like 'Windows Server 2003*' -and (Enabled -eq "True") } -Properties OperatingSystem | Select-Object Name, OperatingSystem | Measure-Object | Select-Object Count
Get-ADComputer -Filter {OperatingSystem -notLike '*SERVER*' } -Properties lastlogondate,operatingsystem |select name,lastlogondate,operatingsystem | Sort-Object lastlogondate | FT -AutoSize
$.) and calls upon them in order to calculate and show you the machines running a Windows desktop OS, with names starting with MS, that have not authenticated to the domain in 180 days. The list gets sorted by the LastLogonDate property. Something like this could later be piped through a command to disable or delete the object. However, in this case, it will only spit out a sorted list: $DaysInactive = 180 $Time = (Get-Date).AddDays(-($DaysInactive)) Get-ADComputer -Filter {(LastLogonTimeStamp -lt $Time) -and (OperatingSystem -notLike '*SERVER*') -and (SamAccountName -like "ms*")} -Properties LastLogonDate, OperatingSystem | Select-Object Name, LastLogonDate, OperatingSystem | Sort-Object LastLogonDate
Get-ADComputer -filter {enabled -eq $False}
Get-ADComputer -filter {enabled -eq $True} | Select-Object Name | Export-CSV "c:\temp\disabled-machines.csv" -NoTypeInformation
Get-ADComputer -filter {enabled -eq $False} | Select-Object Name | Out-File "c:\temp\disabled-machines.txt"
Get-ADComputer -Searchbase "OU=Workstations,OU=HN Computers,OU=HomeNet,DC=HOMENET,DC=local" -Filter { (OperatingSystem -NotLike "*Windows Server*") -and (Enabled -eq "True") }
Get-ADComputer -Searchbase "OU=Workstations,OU=HN Computers,OU=HomeNet,DC=HOMENET,DC=local" -Filter { (OperatingSystem -NotLike "*Windows Server*") -and (Enabled -eq "True") -and (Name -NotLike "CONF-*") } | Select-Object Name | Export-CSV "c:\client-pcs.csv" -NoTypeInformation
Get-ADComputer -Searchbase "OU=Workstations,OU=HN Computers,OU=HomeNet,DC=HOMENET,DC=local" -Filter { (OperatingSystem -NotLike "*Windows Server*") -and (Enabled -eq "True") } -Properties OperatingSystem | Format-Table Name,OperatingSystem -AutoSize
Get-ADUser -filter {enabled -eq $False} | Select-Object Name
Search-ADAccount cmdlet: Search-ADAccount –AccountDisabled –UsersOnly | FT Name
Get-ADUser -Searchbase "OU=Valley Creek,OU=HN Users,OU=HomeNet,DC=HOMENET,DC=local" -Filter { (Enabled -eq "True") } | Select-Object Name | Export-CSV "C:\users.csv" -NoTypeInformation
(Get-ADForest).Domains | ForEach-Object { Get-ADDomainController -Discover -DomainName $_ } | ForEach-Object { Get-ADDomainController -server $_.Name -filter * } | ForEach-Object { Get-ADUser -Identity "rsickler" -Properties BadLogonCount, badPWDCount, LastBadPasswordAttempt, LastLogonDate, LockedOut, Enabled -Server $_ } | Out-GridView
ACADEMICPDC, ACADEMICDC2 & ACADEMICDC3: ("ACADEMICPDC","ACADEMICDC2","ACADEMICDC3") | ForEach-Object {Get-ADUser -Identity rsickler -Properties BadLogonCount, badPWDCount, LastBadPasswordAttempt, LastLogonDate, LockedOut, Enabled -Server $_} | Out-GridView
Get-ADUser rsickler -Properties *
Get-ADUser fmiller -Properties * | Select-Object Name, EmailAddress, Enabled, LockedOut, PasswordExpired, PasswordLastSet, PasswordNeverExpires, AccountExpirationDate
Search-ADAccount -LockedOut -UsersOnly -SearchBase "OU=Employees,OU=NonSCE,OU=Users,OU=HMN,DC=na,DC=autotrader,DC=int"
Search-ADAccount -LockedOut -UsersOnly -SearchBase "OU=Employees,OU=NonSCE,OU=Users,OU=HMN,DC=na,DC=autotrader,DC=int" | Where-Object { $_.Enabled -eq $true }
Search-ADAccount -LockedOut -UsersOnly -SearchBase "OU=Employees,OU=NonSCE,OU=Users,OU=HMN,DC=na,DC=autotrader,DC=int" | Unlock-ADAccount
Search-ADAccount -LockedOut -UsersOnly -SearchBase "OU=Employees,OU=NonSCE,OU=Users,OU=HMN,DC=na,DC=autotrader,DC=int" | Unlock-ADAccount -Confirm
(Get-Aduser "rlsickler" -Properties LockedOut).LockedOut
$Account = "rlsickler" $LockedUser = Get-ADUser -Identity $Account -Properties LockedOut If ($LockedUser.lockedout -eq $true){ Write-Warning -Message "The account, $Account, is locked!" Unlock-ADAccount $Account } else { Write-Host -ForegroundColor Green "The account, $Account, is not locked." }
$Account = "rlsickler" $LockedUser = Get-ADUser -Identity $Account -Properties LockedOut $timeout = New-TimeSpan -Minutes 10 $sw = [diagnostics.stopwatch]::StartNew() While ($sw.elapsed -lt $timeout){ If ($LockedUser.lockedout -eq $true){ Write-Warning -Message "The account, $Account, is locked!" Unlock-ADAccount $Account } else { Write-Host -ForegroundColor Green "The account, $Account, is not locked." } Start-Sleep -seconds 20 } Write-Host "Time limit reached."
Get-ADGroup -Filter { (GroupCategory -eq 'security') } -SearchBase "OU=Security,OU=Groups,OU=HMN,DC=na,DC=autotrader,DC=int" | Select-Object Name, GroupCategory | Export-CSV "C:\Groups.csv" -NoTypeInformation
Get-ADGroup -Filter { (GroupCategory -eq 'Distribution') } -SearchBase "OU=Distribution Lists,OU=Groups,OU=HMN,DC=na,DC=autotrader,DC=int" | Format-Table Name -AutoSize
Get-ADGroup -Filter { (GroupCategory -eq "security") } -SearchBase "OU=Security,OU=Groups,OU=HMN,DC=na,DC=autotrader,DC=int" -Properties Name, Description | Sort-Object “Name” | FT Name, Description
Get-ADGroup -Filter { (Name -Like "*DEV_INFRASTRUCTURE_*") } | Select Name
Get-ADGroupMember "IOL_Users" | Sort-Object "Name" | Format-Table "Name"
Get-ADGroupMember "HMN_STCOSD14_HMS_MODIFY" -Recursive | FT "Name" -AutoSize
FT is short for Format-Table.Get-ADGroupMember "HMN_STCODD14_ARKONA_MODIFY" | Select-Object "Name" | Export-CSV "c:\HMN_STCODD14_ARKONA_MODIFY.csv" -NoTypeInformation
Get-ADGroupMember "~HMN - Employees" | Select-Object "SamAccountName" | %{Get-ADUser $_.samaccountname -Properties mail} | Select-Object "Name", "Mail" | Export-CSV "C:\Emails.csv" -NoTypeInformation
Select-Object, to give us custom table-headings. In this case, it allows us to put the group's name on the line with the user for a clean & easy-to-read look. It also allows us to rename the default properties of Name and SamAccountName when outputting the data to the CSV: $Groups = "HMN_PRODNAS1_INBOUND_MODIFY","HMN_PRODNAS1_INBOUND_READ" $ResultsArray =@() ForEach ($Group in $Groups) { $ResultsArray += Get-ADGroupMember -Id $Group | Select-Object @{Expression={$group};Label="Group"},@{Expression={$_.Name};Label="Member's Name"},@{Expression={$_.SamAccountName};Label="Member's SAM Account Name"} } $ResultsArray | Export-CSV -Path "C:\GroupListResult.csv" -NoTypeInformation
$Groups = Get-Content "C:\Groups.txt" $ResultsArray =@() ForEach ($Group in $Groups) { $ResultsArray += Get-ADGroupMember -Id $Group | Select-Object @{Expression={$group};Label="Group"},@{Expression={$_.Name};Label="Member's Name"},@{Expression={$_.SamAccountName};Label="Member's SAM Account Name"} } $ResultsArray | Export-CSV -Path "C:\GroupListResult.csv" -NoTypeInformation
New-ADGroup -GroupCategory Security -Name "IOL_APPLICATIONS_PROD_READ" -Path "OU=HN Groups,OU=HomeNet,DC=HOMENET,DC=local" -GroupScope DomainLocal -Description "Grants read only access to the resource." -Server dc1.homenet.local
New-ADGroup -GroupCategory Distribution -Name "DEVOPPS" -Path "OU=HN Groups,OU=HomeNet,DC=HOMENET,DC=local" -GroupScope DomainLocal -Description "DevOpps Team" -Server dc1.homenet.local
Search-ADAccount -AccountDisabled -ComputersOnly | Sort-Object | Remove-ADComputer
Search-ADAccount -AccountDisabled -SearchBase "OU=Workstations,OU=HN Computers,OU=HomeNet,DC=HOMENET,DC=local" -ComputersOnly | Sort-Object | Remove-ADComputer
Get-ADUser -SearchBase 'OU=HN Users,OU=HomeNet,DC=HOMENET,DC=local' -filter * -Properties LastLogonDate, PasswordLastSet | Where-Object { $_.Enabled -eq $true -and $_.LastLogonDate -le (Get-Date).AddDays(-90) -and $_.PasswordLastSet -le (Get-Date).AddDays(-90) } | Set-ADUser -Enabled $false
Get-ADComputer -SearchBase "OU=VM Computers,OU=GV Computers,OU=GVSD,DC=gvsd,DC=org" -filter * -Properties LastLogonDate | Where-Object { $_.enabled -eq $true -and $_.LastLogonDate -le (Get-Date).AddDays(-120) } | Set-ADComputer -Enabled $false
With IIS 7 and later, you can use Powershell to do a lot. I've barely scratched the surface with the few commands listed here. However, to use a lot of these, you need to have the Management Scripting Tools installed for IIS.
Import-Module WebAdministration
Get-WebSite | Select-Object Name, ApplicationPool | Export-Csv -NoTypeInformation -Path "C:\_Temp\WebSites.csv"
One can use PS remotely much like one can SSH into a Linux system and run commands as though they were local to the machine. Like with most things, you need to have all the correct permissions and settings for this to work.
To get this to work, you need to enable PSRemoting. This is done on the server/host machine. It can be done a multitude of ways but we'll go into the manual method here.
With Linux, you need to allow remote access. Well, it's the same with Windows. Luckily, one or two commands will normally allow the access you need. First, the network category needs to be set to DomainAuthenticated or Private; if it's set to Public, this won't work. So, check that first and set it as needed. Obviously, these commands require an elevated prompt.
Get-NetConnectionProfile
Name : gvsd.org InterfaceAlias : Wi-Fi InterfaceIndex : 7 NetworkCategory : DomainAuthenticated IPv4Connectivity : Internet IPv6Connectivity : NoTraffic
Set-NetConnectionProfile -Name gvsd.org -NetworkCategory Private
Enable-PSRemoting
Once your server/host is set correctly, you should be able to connect to it from a remote client so long as your network allows the traffic.
Enter-PSSession -ComputerName H2S3-IOLWEB2 -Credential homenet\rsickler
Exit-PSSession
Once connected via a remote session, you can run most commands you'd run from the console if you were sitting in front of it. Some won't work and you'll see a message on the screen if you try to run those.
PS C:\WINDOWS\system32> Enter-PSSession -ComputerName daedalus.gvsd.org -Credential [email protected] [daedalus.gvsd.org]: PS C:\Users\RSickler2\Documents> Get-CimInstance Win32_OperatingSystem | Select-Object Caption, InstallDate, ServicePackMajorVersion, OSArchitecture, BootDevice, BuildNumber, CSName | Format-List Caption : Microsoft Windows Server 2012 R2 Standard InstallDate : 5/27/2015 11:05:51 PM ServicePackMajorVersion : 0 OSArchitecture : 64-bit BootDevice : \Device\HarddiskVolume1 BuildNumber : 9600 CSName : DAEDALUS [daedalus.gvsd.org]: PS C:\Users\RSickler2\Documents> Exit-PSSession PS C:\WINDOWS\system32>
Once PSRemoting has been enabled, you can also invoke various commands without entering a PSSession. There's another site that has a lot of examples as well.
<# This will enable RDP access - remotely - so long as PSRemoting has been enabled. Otherwise, you'd likely need to do this by hand. It pulls computer names from a text file. Adjust paths as needed. #> $Computers = Get-Content "C:\_Temp\Machines.txt" $Creds = Get-Credential rsickler2@gvsd.org ForEach-Object ($Computer in $Computers) { if (Test-Connection -ComputerName $Computer -Count 1 -BufferSize 16 -ErrorAction SilentlyContinue -Quiet) { Invoke-Command –Computername $Computer –ScriptBlock { Set-ItemProperty -Path "HKLM:\System\CurrentControlSet\Control\Terminal Server" -Name "fDenyTSConnections" –Value 0 } -Credential $Creds } else { Write-Output "$computer is offline" | Out-File "C:\_Temp\CopyErrors.txt" -Append } }
-ThrottleLimit parameter - if you see fit: # This allowed me to run a local script on remote machines. $Computers = Get-Content "C:\_Temp\Machines.txt" $Creds = Get-Credential rsickler2@gvsd.org foreach ($Computer in $Computers) { if (Test-Connection -ComputerName $Computer -Count 1 -BufferSize 16 -ErrorAction SilentlyContinue -Quiet) { Invoke-Command -ComputerName "$($Computer)" -FilePath "$env:USERPROFILE\MyAwesomeScript.ps1" -Credential $Creds } else { Write-Output "$computer is offline" | Out-File "C:\_Temp\CopyErrors.txt" -Append } }
I'm no expert with Regular Expressions (Regex) but I've found it to be useful on many occasions. I've used it for manipulating text in Notepad++ but, now and then, you find yourself using it in various scripts. Here are some examples that I've used over the years.
I used to help manage a SmarterMail mail server. Our config would capture potential SPAM emails and drop them into a holding cell. We'd go through the folder daily and weed out the few messages that were accidentally flagged as SPAM. I wrote a script to handle it and it uses some RegEx
When the raw mail files (EML and HDR) would get flagged as SPAM, they'd get prefixed with the sender's egress IP. So, the message would go from something like My cat photos.eml and My cat photos.hdr to 64.206.246.226IP.My cat photos.eml and 64.206.246.226IP.My cat photos.hdr. To drop the messages back into the processing queue, we'd manually rename the files, stripping out the 64.206.246.226IP. piece of it, and move them into the proper folder. Doing this for hundreds of files a day is mind-numbing! My script below seeks out any file with said preceding text and moves it - renaming it on the fly.
# Useful for dealing with the SPAM spools on SmarterMail $Source = "\\h2p-mail1\Smartermail\Spool\spam\hold2" $Dest = "\\h2p-mail1\Smartermail\Spool" Get-ChildItem $Source\* -Include "*.eml","*.hdr" | Foreach-Object { Move-Item $Source\$($_.Name) $Dest\$($_.Name -Replace '(\d{1,3}\.){3}(\d{1,3}IP)\.','') }
I'll do my best to break this down. Note the use of the -Include parameter for Get-ChildItem is telling the code to only touch the following two file-types: EML and HDR.
'(\d{1,3}\.){3}(\d{1,3}IP)\.',''
255 and that consists of three digits. In our example IP, 64.206.246.226, the first octet only has two digits but the last three octets have three digits each: \d{1,3}
\.
(\d{1,3}\.) - three times. An IPv4 IP address has four octets. However, the last octet doesn't end with just a period; it ends with IP. so, the last bit of code - (\d{1,3}IP)\. - is searching for that on the back end of the other three octets: {3}
''
The following commands will list certain info for your NICs. Some of the info is needed to make changes so some of these commands are generally used prior to managing your NICs.
Get-NetIPInterface
Get-NetAdapter
Sort-Object to sort the data based on those columns. In the example below, I sorted the data via the ifIndex property but I could have chosen any of the other valid properties for the cmdlet: Get-NetIPInterface | Sort-Object "ifIndex" | Format-Table -AutoSize
Once you've used something similar to the commands above, you can use some of that info to make changes. Setting a static IP is one of those things you can do.
New-NetIPAddress –InterfaceIndex 5 –IPAddress "10.224.211.35" –PrefixLength 24 –DefaultGateway "10.224.211.1"
Set-DNSClientServerAddress –InterfaceIndex 5 -ServerAddresses "10.224.210.7,10.224.210.8"
Set-DnsClientServerAddress –InterfaceIndex 5 –ResetServerAddresses
Set-NetIPInterface -InterfaceIndex 5 -Dhcp Enabled
This is something I was tasked with years ago. I had to make changes to NICs on hundreds of servers. I was not about to do it by hand so I wrote scripts to do it. The servers had multiple NICs configured so I had to make sure I was changing the correct NIC's config and that complicated matters a bit.
# Changes the DNS servers for a NIC with a specific gateway so it doesn't change another NIC's DNS servers. $computer = Get-Content C:\temp\servers.txt $NICs = Get-WMIObject Win32_NetworkAdapterConfiguration -ComputerName $computer | Where-Object {$_.DefaultIPGateway -eq "192.168.0.1"} ForEach-Object ($NIC in $NICs) { $DNSServers = “10.224.202.236",”10.224.202.237" $NIC.SetDNSServerSearchOrder($DNSServers) $NIC.SetDynamicDNSRegistration(“TRUE”) }
# Changes the DNS servers for a NIC with a specific subnet so it doesn't change another NIC's DNS servers. $computer = Get-Content C:\temp\servers.txt $NICs = Get-WMIObject Win32_NetworkAdapterConfiguration -ComputerName $computer | Where-Object {$_.IPAddress -like "172.16.234.*"} ForEach-Object ($NIC in $NICs) { $NIC.SetDNSServerSearchOrder() $NIC.SetDynamicDNSRegistration(“TRUE”) }
When your PC connects to a network, via a cable or a wireless network, Windows tries to place the network in one of three categories. Certain settings are then tweaked based on these categories - Public, Private and DomainAuthenticated. For instance, if it's set as a Public network, the Windows Firewall blocks more traffic than it would had you selected a Private network. The Private network profile will allow for things like shared folder and printer access but, if the profile is set to DomainAuthenticated, certain ports that are used for remote admin work get opened. I've had to change this from time to time and, frankly, I forget where to make these changes in the GUI so I always fall back to PS.
Get-NetConnectionProfile
Set-NetConnectionProfile -InterfaceIndex 5 -NetworkCategory Private
I've used Get-NetConnectionProfile in a logon script for installing printers. Basically, it would make sure the user was connected to the proper network before trying to install printers the network printers. So, if the user was at home, it wouldn't waste any time trying to make sure the printers were installed. Below is the script I used. Never mind the reasons why it was used; it's just another example of pulling info from a NIC and using it accordingly.
<# This should work in Windows 10 & Server 2016. To use this script, create a shortcut in common startup (START > RUN > SHELL:COMMON STARTUP). The shortcut should call PS something like this: powershell.exe -ExecutionPolicy Bypass -WindowStyle Hidden -NonInteractive -NoLogo -File "C:\Scripts\add_printers_logon.ps1" #> # I've added a sleep-timer to avoid a timing issue with getting the system and its networking up and running. # This can be adjusted to suit your needs. Start-Sleep -Seconds 15 # Testing for an active network connection. $upTest = ( Get-NetConnectionProfile | Where-Object {$_.IPv4Connectivity -ne "NoTraffic"} ) # Print server. Some prefer an IP or NetBIOS name here. $PrintServer = "prntsrvr2.gvsd.org" # If the active network connection is on the correct domain, the printer installations will begin. if ($upTest.NetworkCategory -eq "DomainAuthenticated" -and $upTest.Name -ieq "gvsd.org" ) { Add-Printer -ConnectionName "\\$PrintServer\Color402_q" Add-Printer -ConnectionName "\\$PrintServer\Copy Center B-W_Q" Add-Printer -ConnectionName "\\$PrintServer\Copy Center Color_Q" Add-Printer -ConnectionName "\\$PrintServer\Print_Q" } else { exit }
I don't particularly like having a miscellaneous section but I'd rather not create subsections for every little command or script I've ever used when they don't fit in with the rest of this wiki.
These are very simple commands but also very useful. In a properly configured domain environment, they're very useful for working with remote machines.
Restart-Computer -Force
Restart-Computer -Force -ComputerName H2P-IOLDP45 -Credential rsickler@homenet.local
Stop-Computer -Force
Stop-Computer -Force -ComputerName H2P-IOLDP45 -Credential homenet\rsickler
These commands can be used locally or, in a properly configured domain, you can use them remotely.
Rename-Computer
Rename-Computer -NewName ms-sped-240 -Restart
Rename-Computer -ComputerName ms-sped-24 -DomainCredential rsickler2@gvsd.org -NewName ms-sped-240 -Restart -PassThru -Force
Add-Computer
Add-Computer -DomainName gvsd.org
Add-Computer -DomainName gvsd.org -ComputerName sb2 -LocalCredential "sb2\Administrator" -Restart -Credential rsickler@gvsd.org -Verbose
Remove-Computer
Remove-Computer -UnjoinDomainCredential rsickler2@gvsd.org -ComputerName sb2 -PassThru -Restart -Force
With relative ease, one can use PS to manage Features & Roles. I last used many of these on Server 2012-R2.
Get-WindowsFeature
Get-WindowsFeature | FT -AutoSize
Get-WindowsFeature | Where { $_.Name -Like "server-gui-*" } | FT -AutoSize
Get-WindowsFeature -Name Web-Mgmt-Console
Get-WindowsFeature | FT Name, InstallState -AutoSize
One can use PS to do one-at-a-time installs or one could write a PS script to handle it. Generally speaking, if you're just installing a small number of packages at a time, Server 2012-R2 is smart enough to tell you when a package installation can't complete due to a dependency.
Install-WindowsFeature Telnet-Client
Install-WindowsFeature Telnet-Client, Telnet-Server
Install-WindowsFeature server-gui-shell -source:wim:d:\sources\install.wim:2 -restart:$true
Like with the installations, one can use PS to do one-at-a-time uninstalls or one could write a PS script to handle it.
Uninstall-WindowsFeature server-gui-shell, server-gui-mgmt-infra -restart:$true
Uninstall-WindowsFeature server-gui-shell, server-gui-mgmt-infra -restart:$false
Uninstall-WindowsFeature server-gui-shell, server-gui-mgmt-infra, Telnet-Client, Telnet-Server -remove
Get-WindowsFeature | Where-Object { $_.InstallState -Eq “Available” } | Uninstall-WindowsFeature -Remove
I had seen an example of this in a signature out on a forum. It took some digging but I got it working.
"[email protected]" | Measure-Object -Character | Select-Object Characters
[int[]][char[]]"[email protected]"
[int[]][char[]]"[email protected]" -join " "
80 105 110 101 86 97 108 108 101 121 84 111 109 101 64 103 109 97 105 108 46 99 111 109
([int[]][char[]]"[email protected]" | %{ $_ - 30 }) -join " "
50 75 80 71 56 67 78 78 71 91 54 81 79 71 34 73 79 67 75 78 16 69 81 79
([int[]][char[]]"[email protected]" | %{ $_ - 30 }) -join ""
507580715667787871915481797134737967757816698179
0..23. If you had enough fingers and you were to count from 0 through 23, you'd actually use 24 fingers - the number of characters in my email address. In the code below, we're adding 30 to that long number: 30+.[string](0..23|%{[char][int](30+("507580715667787871915481797134737967757816698179").substring(($_*2),2))})-replace " "
[email protected]