PowerShell - Cleaning up in progress

I find I'm using PowerShell more and more often, but I still need to look up examples.

On this page I have general notes and some of the scripts I've used in the past to refresh my memory.

Enable Remote PowerShell

Basic tasks:


Disable-NetAdapter -Name 'Ethernet' -Confirm:$false

Tricks with ping: 

From cmd: powershell -command "0..500|foreach {$_;ping -n 1 server1;start-sleep -s 2}"

while ($true) { $ct++; $result=ping -n 1 google.com|Select-String "Lost";$ds=Get-Date;$show=$result.ToString();write-host $ct $ds $show.Substring($show.length - 10,10) to google;Start-Sleep -Seconds 1 }

Or save a script:

$target=$args[0]

Write-Output "Target: $target"

0..100000|%{

 If (-not (Test-Connection -ComputerName $target -BufferSize 16 -Count 1 -Quiet)){

  $dropcount+=1

  Write-Output "$(Get-Date) Packet to $target was dropped. Drop count: $dropcount"  

 }

 if (($_ % 60) -eq 0){Get-Date}

 Start-Sleep -Seconds 1

}

 

Merge hyper-v drive snapshots, one by one:

PS C:\Hyper-V\Virtual hard disks> function fakemerge { Param ([string]$targetpath);$targetvhd=Get-VHD -Path "$targetpath";$parentpath=$targetvhd.ParentPath;Write-Host "Merge: Merge-VHD -Path" $targetpath "-DestinationPath" $parentpath "-ErrorAction Stop";st

art-sleep -s 3;if ($parentpath){fakemerge "$parentpath"}else{Write-Host "No parent path for $targetpath"} };fakemerge "C:\hyper-v\Virtual Hard Disks\nameserver-drivename-datatype_EE77DAC6-B30D-40B0-BB8D-F719993EEE84.avhdx

Get IP address info for a list of machines:

PS C:\> $servers = @("server1","server2","serverd");foreach ($server in $servers) {write-host "$server";Invoke-Command -Script {Get-NetIPInterface -AddressFamily "IPv4"|where {$_.InterfaceAlias -notmatch 'Loopback'}; (Get-NetIPAddress |where AddressFamily -eq "IPv4" |where IPAddress -ne '127.0.0.1').ipaddress} -ComputerName "$server"}

Check Active Directory against a list for lack of entries in the list:

Get-ADUser -Filter 'enabled -eq $true' -Properties UserPrincipalName|Select-Object -Property UserPrincipalName|%{$uid=($_.UserPrincipalName -split '@')[0];$uid;if (!(Select-String -Path C:\temp\list.csv $uid)){"Not Found $uid"}}

Set DNS servers

PS C:\> Set-DnsClientServerAddress -InterfaceIndex 23 -ServerAddresses ("10.168.1.5","10.168.1.15")

Remove a file

PS C:\> Get-ADComputer -Filter *|Select-Object -ExpandProperty name|foreach { Write-Output "$_"; if(Test-Connection -ComputerName $_ -BufferSize 16 -Count 1 ){ Invoke-Command -Script {if (Test-Path '\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp\StupidToHaveHereAnyway.lnk'){remove-item '\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp\StupidToHaveHereAnyway.lnk'}} -ComputerName $_}}

 

Push a copy of a file

PS C:\> Get-ADComputer -Filter { OperatingSystem -like "Windows 10*" } -Properties OperatingSystem |Select-Object -ExpandProperty name|foreach { Write-Output "$_"; if(Test-Connection -ComputerName $_ -BufferSize 16 -Count 1 -Quiet ){ robocopy "\\servername\share\path" "\\$_\c`$\folder\target" filename.chm } else { Write-Output "Couldn't connect to $_" } }

Enable a Hyper-V virtual adapter

PS C:\> GET-VM | GET-VMNetworkAdapter | Connect-VMNetworkAdapter –Switchname ‘New-cool-Hyper-V-Lan’ 

Clean old profiles off of a workstation

#First look at the targets:

Get-CimInstance -ClassName win32_userprofile -ComputerName 'thiscomputername'|Select-Object -Property 'localpath'

#Then de-select any that you want to leave:

Get-CimInstance -ClassName win32_userprofile -ComputerName 'pc1'|Where-Object {($_.LocalPath.split('\')[-1] -ne 'jdoe' -and $_.LocalPath.split('\')[-1] -ne 'jsmith' -and $_.LocalPath.split('\')[-1] -ne 'pcadmin' -and $_.LocalPath.split('\')[-1] -ne 'pcadmin.MYDOMAIN'  -and $_.LocalPath.split('\')[-1] -ne 'NetworkService' -and $_.LocalPath.split('\')[-1] -ne 'LocalService' -and $_.LocalPath.split('\')[-1] -ne 'systemprofile' -and $_.LocalPath.split('\')[-1] -ne 'admin.xyz');}|Remove-CimInstance

Connect to Office 365 for Exchange management

$Cred = Get-Credential

$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell/ -Credential $Cred -Authentication Basic -AllowRedirection

Import-PSSession $Session

Remove-PSSession $Session

Adjust Azure AD

PS C:\> install-module msonline

PS C:\> connect-msolservice

PS C:\> Update-ADFSCertificate –CertificateType token-signing

PS C:\> update-msolfederateddomain -domainname example.com

PS C:\> Get-MsolFederationProperty -DomainName example.com | FL Source, TokenSigningCertificate

Adjust a username on Office 365

PS C:\> Set-MsolUserPrincipalName -UserPrincipalName mary.osgood@contoso.onmicrosoft.com -NewUserPrincipalName mary.smith@contoso.com

Install a module for Sharepoint Online and use it to unlock a file?? (Not resolved)

PS C:\> Install-Module SharePointPnPPowerShellOnline

PS C:\> Connect-PnPOnline -Url https://contoso.sharepoint.com -UseWebLogin (Maybe Not?)

> Import-Module Microsoft.Online.SharePoint.PowerShell -DisableNameChecking

> Connect-SPOService -Url https://contoso-admin.sharepoint.com

C:\> $web = Get-SPOSite -Identity https://contoso.sharepoint.com

Add a registry entry to trust a local domain

$UserRegPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Domains"

#Value 1 = Intranet

$DWord = 1

$Name = "phantomcode.org"

if (-Not (Test-Path "$UserRegPath\$Name")){

 New-Item -Path "$UserRegPath" -ItemType File -Name "$Name"

}

Set-ItemProperty -Path "$UserRegPath\$Name" -Name "http" -Value $DWord

Set-ItemProperty -Path "$UserRegPath\$Name" -Name "https" -Value $DWord

Set-ItemProperty -Path "$UserRegPath\$Name" -Name "*" -Value $DWord

Watch a text file for changes

PS C:\> get-content -tail 10 -wait '\\server\c$\temp\serversmonitored.log'

Install XPS Viewer

dism /Online /Add-Capability /CapabilityName:XPS.Viewer~~~~0.0.1.0

Remove Junk from Microsoft:

$applist=@("Microsoft.XboxSpeechToTextOverlay",

"Microsoft.Xbox.TCUI",

"Microsoft.UI.Xaml.2.0",

"Microsoft.UI.Xaml.2.0",

"Microsoft.Microsoft3DViewer",

"Microsoft.XboxApp",

"Microsoft.BingWeather",

"windows.immersivecontrolpanel",

"Microsoft.Messaging",

"Microsoft.OneConnect",

"Microsoft.WebMediaExtensions",

"Microsoft.SkypeApp",

"microsoft.windowscommunicationsapps",

"Microsoft.ZuneMusic",

"Microsoft.ZuneVideo",

"Microsoft.BingTranslator",

"Microsoft.WindowsFeedbackHub",

"Microsoft.AAD.BrokerPlugin",

"Microsoft.Windows.CloudExperienceHost",

"Microsoft.Windows.ShellExperienceHost",

"Microsoft.Windows.Cortana",

"Microsoft.Windows.ContentDeliveryManager",

"Microsoft.Wallet",

"Microsoft.Windows.OOBENetworkConnectionFlow",

"Microsoft.Windows.OOBENetworkCaptivePortal",

"Microsoft.Windows.ParentalControls",

"Microsoft.Windows.NarratorQuickStart",

"Microsoft.Windows.PeopleExperienceHost",

"Microsoft.Windows.CapturePicker",

"Windows.CBSPreview",

"Microsoft.XboxGameCallableUI",

"Microsoft.Windows.XGpuEjectDialog",

"Microsoft.Windows.SecureAssessmentBrowser",

"Microsoft.Windows.SecHealthUI",

"Microsoft.Windows.PinningConfirmationDialog",

"Microsoft.Windows.AssignedAccessLockApp",

"Microsoft.Windows.Apprep.ChxApp",

"Microsoft.Win32WebViewHost",

"Microsoft.PPIProjection",

"Microsoft.MicrosoftEdgeDevToolsClient",

"Microsoft.ECApp",

"Microsoft.CredDialogHost",

"Microsoft.BioEnrollment",

"Microsoft.AsyncTextService",

"Microsoft.AccountsControl",

"Microsoft.WebpImageExtension",

"Microsoft.Getstarted",

"Microsoft.XboxGamingOverlay",

"Microsoft.MixedReality.Portal",

"Microsoft.HEIFImageExtension",

"Microsoft.WindowsMaps",

"Microsoft.MicrosoftSolitaireCollection",

"Microsoft.People",

"Microsoft.Office.OneNote",

"Microsoft.GetHelp",

"Microsoft.YourPhone",

"Microsoft.XboxGameOverlay",

"Microsoft.Print3D",

"Microsoft.XboxIdentityProvider")

foreach ($removethis in $applist){

 Write-Host "Removing $removethis"

  Read-Host -Prompt "Press Enter to continue or Ctrl+C to exit"

 Get-AppxPackage $removethis | Remove-AppxPackage

 Write-Host "Removed $removethis"

}

Run a DOS/CMD style command

PS C:\> $command = "dir 'c:\program files' "

PS C:\> $bytes = [System.Text.Encoding]::Unicode.GetBytes($command)

PS C:\> $encodedCommand = [Convert]::ToBase64String($bytes)

PS C:\> powershell.exe -encodedCommand $encodedCommand

Use SFTP/SSH/SCP with Posh-SSH module:

PS C:\> Install-Module -Name Posh-ssh

Interactive approvals later:

PS C:\> $username = 'whateveruser'

PS C:\> $password = ConvertTo-SecureString 'WhateverPassword' -AsPlainText -Force

PS C:\> $mycredentials = New-Object System.Management.Automation.PSCredential($username,$password)

PS C:\> New-SSHSession -computername 'whateverserver.example.org' -Credential $mycredentials -Verbose

Or generate an OpenSSH public/private key pair with no password, some notes:

PS C:\> $username = 'whateveruser'

PS C:\> $nopassword = new-object System.Security.Securestring

PS C:\> $mycredentials = New-Object System.Management.Automation.PSCredential($username,$nopassword)

PS C:\> New-SSHSession -computername 'whateverserver.example.org' -Credential $mycredentials -KeyFile C:\temp\id_rsa.private -Verbose

Other command examples:

PS C:\> Get-SSHSession | fl

PS C:\> Invoke-SSHCommand -Index 0 -Command "uname -a"

PS C:\> Remove-SSHSession -Index 0 -Verbose

PS C:\> New-SFTPSession -ComputerName 'whateverserver.example.org' -Credential (Get-Credential root) -Verbose | fl

PS C:\> Set-SFTPDirectoryPath -Index 0 -Path /usr/bin

PS C:\> Get-SFTPDirectoryList -Index 0 -Path /tmp

Other commands:

Get-SFTPFile

Move-SFTPFile

Remove-SFTPFile

Set-SFTPFile (Uploads a file)

New-SFTPDirectory

Remove-SFTPDirectory

Use SFTP/FTP/SCP/FTPS with WinSCP module:

PS C:\temp> Install-Module -Name WinSCP

Interactive approvals later:

PS C:\> $sessionOption = New-WinSCPSessionOption -hostname 'whateverserver' -Protocol 'Sftp'

PS C:\> $sshHostKeyFingerprint = Get-WinSCPHostKeyFingerprint -SessionOption $sessionOption

PS C:\> $cred = Get-Credential

PS C:\> $sessionOption = New-WinSCPSessionOption -hostname 'whateverserver' -Protocol 'Sftp' -Credential $cred -SshHostKeyFingerprint $sshHostKeyFingerprint

PS C:\> $session1 = New-WinSCPSession -SessionOption $sessionOption

PS C:\> Get-WinSCPChildItem -Path '/home/netadmin'|fl

PS C:\> Remove-WinSCPSession $session1

Logging into a webpage and pulling a download file

<#example_pull.ps1

2018-11-08 B.M.C. webmaster@phantomcode.com: created to pull a CSV download

           This was rather tricky because I couldn't get the fine grain control I wanted from the normal tools like WebRequest and WebClient.

   I needed to load a page to get the session cookie, then submit data with POST (including that session cookie) from an image input.

   Then after a successful login, I needed to go to another page, still with the session cookie, and from that page click another

   image input button which results in a stream of data which is the CSV data I'm actually after.

#>

$url = "https://www.example.com/URL/URL/Login.asp"

#Load initial page to get the session cookie

$request = [System.Net.WebRequest]::Create($url)

$request.CookieContainer = New-Object System.Net.CookieContainer

$request.Method="Get"

$response = $request.GetResponse()

$requestStream = $response.GetResponseStream()

$readStream = New-Object System.IO.StreamReader $requestStream

$data=$readStream.ReadToEnd()

 

#Looping seems like it should be unnecessary, but normal pages 

#may have multiple cookies. In this case, I just needed the one.

foreach ($cook in $Response.Cookies)    {

 $lastcookie=$cook.toString()

}

#write-output "Last cookie: $lastcookie" 

#Now that we have the cookie, we post the login data back to the same page.

#I found Telerik Fiddler to be very handy for figuring out exactly what 

#my normal session was doing.

$url = "https://www.example.com/URL/URL/Login.asp"

$postData = "Name=myusername&Password=mypassword&loginButton.x=0&loginButton.y=0"

$buffer = [text.encoding]::ascii.getbytes($postData)

[net.httpWebRequest] $req = [net.webRequest]::create($url)

$req.method = "POST"

$req.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"

$req.Headers.Add("Accept-Language: en-US")

$req.Headers.Add("Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7")

$req.Headers.Add("Cookie",$lastcookie)

$req.AllowAutoRedirect = $false

$req.ContentType = "application/x-www-form-urlencoded"

$req.ContentLength = $buffer.length

$req.TimeOut = 50000

$req.KeepAlive = $true

$req.Headers.Add("Keep-Alive: 300");

$reqst = $req.getRequestStream()

$reqst.write($buffer, 0, $buffer.length)

$reqst.flush()

$reqst.close()

[net.httpWebResponse] $res = $req.getResponse()

$resst = $res.getResponseStream()

$sr = new-object IO.StreamReader($resst)

$result = $sr.ReadToEnd()

$res.close()

#$result

#The result is a 302 redirect, which means our session is now successfully authenticated

#Ignoring the redirect, we now go to the download page where we send input

#as if we'd clicked the "Download" button, which isn't an HTML button but an

#input image. As before, Fiddler came in handy.

$url = "https://www.example.com/URL/URL/PageWithDownloadButton.asp"

$postData="downloadButton.x=0&downloadButton.y=0"

$buffer = [text.encoding]::ascii.getbytes($postData)

[net.httpWebRequest] $req = [net.webRequest]::create($url)

$req.method = "POST"

$req.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"

$req.Headers.Add("Accept-Language: en-US")

$req.Headers.Add("Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7")

$req.Headers.Add("Cookie",$lastcookie)

$req.AllowAutoRedirect = $false

$req.ContentType = "application/x-www-form-urlencoded"

$req.ContentLength = $buffer.length

$req.TimeOut = 50000

$req.KeepAlive = $true

$req.Headers.Add("Keep-Alive: 300");

$reqst = $req.getRequestStream()

$reqst.write($buffer, 0, $buffer.length)

$reqst.flush()

$reqst.close()

[net.httpWebResponse] $res = $req.getResponse()

$resst = $res.getResponseStream()

$sr = new-object IO.StreamReader($resst)

$result = $sr.ReadToEnd()

$res.close()

Out-File -InputObject $result -FilePath 'result.csv'

#The resulting stream of data is a CSV file that I need for other tasks.

<#REFERENCE 

REFERENCE CODE (UNUSED)

$cc = new-object Net.CookieContainer

$req = [Net.WebRequest]::Create("https://www.example.com/service")

$req.CookieContainer = $cc

$req = [System.Net.HttpWebRequest] [System.Net.WebRequest]::Create($url)

$probe = $html.ReadToEnd() 

$cookie = $ResponseObject.Headers["Set-Cookie"]

$strt = $cookie .indexOf(';', 0)

$sessionid= $cookie.Substring(0,$strt)

$cookie= $data.Headers["Set-Cookie"]

$req.Headers.Add("Accept-Encoding: gzip,deflate")

"Cookie:"

"{0} = {1}"    -f $cook.Name, $cook.Value

"Domain      : {0}"     -f $cook.Domain

"Path        : {0}"     -f $cook.Path

"Port        : {0}"     -f $cook.Port

"Secure      : {0}"     -f $cook.Secure

"When issued : {0}"     -f $cook.TimeStamp

"Expires     : {0}"     -f $cook.expireds

"Expired?    : {0}"     -f $cook.expired

"Don't save  : {0}"     -f $cook.Discard

"Comment     : {0}"     -f $cook.Comment

"Uri for comments: {0}" -f $cook.CommentUri

"Version     : {0}"     -f $cook.Version

"String: {0} :"         -f $cook.ToString()

$data

 Print number of cookies

if ($response.Cookies.Count -gt 0) {

"{0} Cookies returned from: {1}" -f $Response.Cookies.Count,$site

""

}


Place a voice call with PowerShell and Twilio:

#Run these with your SID and Token once to set the variables

#[Environment]::SetEnvironmentVariable('TWILIO_ACCOUNT_SID', 'AXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX','User')

#[Environment]::SetEnvironmentVariable('TWILIO_AUTH_TOKEN','XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX','User')


# Pull in Twilio account info, previously set as environment variables

$sid =[Environment]::GetEnvironmentVariable('TWILIO_ACCOUNT_SID','User')

$token =[Environment]::GetEnvironmentVariable('TWILIO_AUTH_TOKEN','User')

$number = "+15555555555"


$targetnumber=@() #Using this method to handle additions to an array does not scale well, but it is easy to read

#$targetnumber += "+55555555555" #John

$targetnumber += "+15555555555" #Jane

foreach ($person in $targetnumber)

{

$url = "https://api.twilio.com/2010-04-01/Accounts/$sid/Calls.json"

$params = @{ To = "$person"; From = $number; Twiml = '<?xml version="1.0" encoding="UTF-8"?><Response><Say voice="Polly.Joey">Hello. This is an alert. Something may have gone wrong. .</Say></Response>' }

$p = $token | ConvertTo-SecureString -asPlainText -Force

$credential = New-Object System.Management.Automation.PSCredential($sid, $p)

Invoke-WebRequest $url -Method Post -Credential $credential -Body $params -UseBasicParsing | ConvertFrom-Json | Select sid, body

}


REFERENCE URLS

#https://stackoverflow.com/questions/34422255/trying-to-do-a-simple-post-request-in-powershell-v2-0-no-luck

#https://stackoverflow.com/questions/5470474/powershell-httpwebrequest-get-method-cookiecontainer-problem

#https://social.microsoft.com/Forums/en-US/837b0841-f878-498d-a830-7462bf879b4a/powershell-post-method-server-not-accepting-the-cookie?forum=Offtopic

#http://eddiejackson.net/data/powershell/Get-Cookie.ps1

Then there was this bit. I found I could do everything I wanted to all the way through clicking the download

button by launching an Internet Explorer object. This can even be hidden. However, I found there was no

way to click the Save button of Internet Exporer without isolating the object, bringing the window to the active

or foreground state, then using sendkeys to send specific keys to the window. Since I want this task to run 

even when nobody is logged into Windows, I don't trust that window manipulation would work and sendkeys is a

little dangerous as I've learned in past programming. That said, it did prove useful to have a program I could run

to record activity to Fiddler without having to do interactive typing, so I'll leave that script here in the reference

notes.

#https://westerndevs.com/simple-powershell-automation-browser-based-tasks/

$loginurl="https://www.example.com/URL/URL/Login.asp"

$membrurl="https://www.example.com/URL/URL/PageWithDownloadButton.asp"

$myusername="myusername"

$mypassword="mypassword"

$ie = new-object -ComObject "InternetExplorer.Application"

$ie.visible = $true #Do you want to see IE?

$ie.silent = $true #Do you want pop-up dialogs suppressed?

$ie.navigate($loginurl)

while($ie.Busy) { Start-Sleep -Milliseconds 2000 } #My example was something like 100... I had to increase it a LOT before it worked

$elements=$ie.Document.IHTMLDocument3_getElementsByName("txtLoginName");foreach ($element in $elements){ $element.value=$myusername }

$elements=$ie.Document.IHTMLDocument3_getElementsByName("txtPassword");foreach ($element in $elements){ $element.value=$mypassword }

$elements=$ie.Document.IHTMLDocument3_getElementsByName("btnLogin");foreach ($element in $elements){ $element.click() }

$ie.navigate($membrurl)

while($ie.Busy) { Start-Sleep -Milliseconds 2000 }

$elements=$ie.Document.IHTMLDocument3_getElementsByName("btnDownload");foreach ($element in $elements){ $element.click() }

while($ie.Busy) { Start-Sleep -Milliseconds 2000 }

#Hit "S" on the keyboard to hit the "Save" button on the download box

$obj = new-object -com WScript.Shell

$obj.AppActivate('Internet Explorer')

$obj.SendKeys('s')

#>

Find the model of a computer: (not really PowerShell)

wmic csproduct get name

Admin Powershell to check bitlocker status:

manage-bde -Status

Check the size of a folder

function sizefolder($path) { $objFSO = New-Object -com Scripting.FileSystemObject;("{0:N2}" -f (($objFSO.GetFolder($path).Size/1GB)))}

sizefolder(C:\temp)

Check out Hyper-V logs:

Get-WinEvent -FilterHashTable @{LogName ="Microsoft-Windows-Hyper-V*"; StartTime = (Get-Date).AddDays(-1);} -ComputerName Hyper-Vserver

 

Other stuff: