Windows Powershell

From Ever changing code

ISE - PowerShell IDE

Untitled.ps1 - when working in ISEv2 or v4 in a script edit section, you can select code and press F8 to execute it
$_ - substitute a singular record in array
-eq, -or -and - dash means operator

Test network connection

Testing from PowerShell if NFS port on remote server is open. The test takes around 5 seconds, them prints output.

PS C:\> Test-NetConnection -ComputerName -Port 2049
ComputerName           :
RemoteAddress          :
RemotePort             : 2049
InterfaceAlias         : Ethernet
SourceAddress          :
PingSucceeded          : True
PingReplyDetails (RTT) : 2 ms
TcpTestSucceeded       : True

PowerShell of linux commands

PS1 C:\> Get-Content -Path "C:\scripts\test.log" -Wait -Tail 100
$ tail -f /mnt/c/scripts/test.log
PS1 C:\> (Invoke-WebRequest -Headers @{"Host"=""} -Uri -UseBasicParsing).statuscode
PS1 C:\>  Invoke-RestMethod -Headers @{"Host"=""} -Uri                                   
$ curl --header 'Host:'
#Optional enable latest TLS, by default it uses TLS1.0, use 'Ssl3' in edge cases
PS1 C:\> [Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls" 
PS1 C:\> Invoke-WebRequest -OutFile
$ wget -OutFile
Error: The request was aborted: Could not create SSL/TLS secure channel. - set SecurityProtocol

Set a hostname

# ideally this should be up to 15 chars long to fit NetBios name otherwise it will be truncated
Rename-Computer -NewName server-1-eu -restart

Windows features

List installed features

PS C:> DISM /online /get-features /format:table
Deployment Image Servicing and Management tool
Version: 6.3.9600.17031
Image Version: 6.3.9600.17031
Features listing for package : Microsoft-Windows-ServerCore-Package~aaaaaaaaaae35~amd64~~6.3.9600.16384
----------------------------------------------------------- | --------
Feature Name                                                | State
----------------------------------------------------------- | --------
NetFx4ServerFeatures                                        | Enabled
NetFx4Extended-ASPNET45                                     | Enabled

Install Windows feature


PS C:\> Get-WindowsFeature *nfs*
Display Name                                Name                       Install State
------------                                ----                       -------------
[ ] Server for NFS                          FS-NFS-Service                 Available
[ ] Client for NFS                          NFS-Client                     Available
[ ] Services for Network File System Man... RSAT-NFS-Admin                 Available

PS C:\> Install-WindowsFeature NFS-Client -IncludeAllSubFeature -IncludeManagementTools
Success Restart Needed Exit Code      Feature Result
------- -------------- ---------      --------------
True    No             Success        {Client for NFS}

Other features, confirmed Windows Server Base 2019 | AWS

Install-WindowsFeature -name Web-Server -IncludeManagementTools #IIS


Download a file

Bits transfer

Windows has background service to download files. The admin console is called bitsadmin where equivalent Powershell cmdlets are *-BitsTransfer

If (-Not (Test-Path $tmpPath -pathType container)) { md $tmpPath }
Start-BitsTransfer -Source $tentacle_url -Destination $tmpPath\$tentacle_file
WriteOutput "BitsTransfer - success: $?"

The important downside is that you cannot use BitsTransfer with:

  • schedule job unless you tick "Run when Logged in"
  • AWS Windows user_data scripts

This is by design it requires interactive session. From docs "BITS transfers files only when the job owner is logged on and a network connection (LAN or modem) is established. BITS processes the transfer job using the security context of the job owner. The user who created the job is considered the owner of the job. However, a user with administrator privileges can take ownership of another user's job."


A common .NET class used for downloading files is the System.Net.WebClient class.

$WebClient = New-Object System.Net.WebClient

#Or one-liner
(New-Object System.Net.WebClient).DownloadFile($url, $file)

Install an application

This is usually done using msiexec it will start installing a software in background. To have better control of it, eg. running configuration task for this software straight after installation. You can use cmd start or Powershell cmdlet Start-Process with -Wait option. It will start separate process (for eg. software installation) and wait until completed.

Start-Process msiexec.exe -ArgumentList "/i $tmpPath\$splunk_forwarder_file AGREETOLICENSE=Yes", "/quiet" -Wait
#Without -wait, following commands fail as Splunk Forwarder would not be fully installed
C:\"Program Files"\SplunkUniversalForwarder\bin\splunk set servername       ${tf_hostname} -auth admin:changeme
C:\"Program Files"\SplunkUniversalForwarder\bin\splunk set default-hostname ${tf_hostname} -auth admin:changeme

Please pay attention to a way how arguments to application msiexec.exe have been passed in "/i app.exe apparg1 apparg2", "/quiet"



Load ActiveDirectory module to have access to AD, pre-requirement to execute most commands below

Import-Module -Name ActiveDirectory

Create a big file

fsutil file createnew big1k 1024       # 1Kb, filled in with Null
fsutil file createnew big1g 1073741824 # 1Gb

Schedule task

Create a schedule task that runs on a powershell script on boot, then disables itself.

$schAction = New-ScheduledTaskAction `
  -Execute "Powershell.exe" `
  -WorkingDirectory C:\ProgramData\Amazon\EC2-Windows\Launch\Scripts `
  -Argument '-NoProfile -WindowStyle Hidden -NoLogo -NonInteractive -c "powershell .\TentacleRegister.ps1 -verbose >> .\TentacleRegisterScheduleTask.log 2>&1"'

$schTrigger   = New-ScheduledTaskTrigger   -AtStartup
$schPrincipal = New-ScheduledTaskPrincipal -UserId "NT AUTHORITY\SYSTEM" -LogonType ServiceAccount -RunLevel Highest

Register-ScheduledTask -Action $schAction -Trigger $schTrigger -TaskName "RegisterTentacle" -Description "Register OctopusDeploy" -Principal $schPrincipal

#Create .ps1 script
$TentacleRegisterFileContent = @'
$TentaclePath="C:\Program Files\Octopus Deploy\Tentacle"
if (-Not (Test-Path $TentaclePath -pathType container)) { Write-Output "Octopus path not found"; exit 1 }
cd $TentaclePath
.\Tentacle.exe create-instance --instance $env:computername --config "C:\Octopus\Tentacle.config" --console
.\Tentacle.exe new-certificate --instance $env:computername --if-blank --console
.\Tentacle.exe configure       --instance $env:computername --reset-trust --console
.\Tentacle.exe configure       --instance $env:computername --home "C:\Octopus" --app "C:\Octopus\Applications" --port "10933" --console
.\Tentacle.exe configure       --instance $env:computername --trust ${tf_octopus_fingerprint} --console
netsh advfirewall firewall add rule "name=Octopus Deploy Tentacle" dir=in action=allow protocol=TCP localport=10933
.\Tentacle.exe register-with   --instance $env:computername `
   --server "" --apiKey="${tf_octopus_token}" `
   --role   "microservices" --environment "${tf_octopus_environment}" `
   --comms-style TentaclePassive -h $localIP --force --console
.\Tentacle.exe service --instance $env:computername --install --stop --start --console

#Disable/delete scheduled task
#Unregister-ScheduledTask -TaskName "RegisterTentacle" -Confirm:$false
Disable-ScheduledTask     -TaskName "RegisterTentacle"

What is worth to note re-direction -Argument '-NoProfile -WindowStyle Hidden -NoLogo -NonInteractive -c "powershell .\TentacleRegister.ps1 -verbose >> .\TentacleRegisterScheduleTask.log 2>&1"' that allows to log execution STD and STDERR to a file. Please note that most of the schedule task scripts run in SYSTEM user context. Then it's also important that the task name match here RegisterTentacle

Extract from Active Directory

get-aduser -Filter {Samaccountname -eq "Smith"} -properties Organization
get-aduser -Filter {(Givenname -eq "Smithy") -and (Surname -eq "Smith")} 

# Build array $users with all Samaccountname(loginnames) with additional properties: Name, Description
$users = get-aduser -Filter {Samaccountname -like "*"} -properties Name, Description

# Return array object count

# Search array $users where $_ each object in array field samaccountname has a given string
$users | Where-Object {$_.samaccountname -eq "string_to_compare"}

# Build array of enabled and disabled accounts in AD where field Enabled equal $true ($true boolean is 1 $false is 0)
$enabledusers  = $users | Where-Object {$_.Enabled -eq $true }
$disabledusers = $users | Where-Object {$_.Enabled -eq $false}

# Filter array $disabledusers returning only Samaccountname, GivenName, Surname and display (ft = Format-table)
$disabledusers | Select-Object Samaccountname, GivenName, Surname | ft -AutoSize

# Build create new array from filter of $users array if name or description contains a string
$aausers = $users | Where-Object {( $_.Name -like "*aa*") -or ($_.Description -like "*bb*")}
$aausers | Select-Object Samaccountname, GivenName, Surname, Enabled | Sort-Object Enabled | ft -AutoSize

# Print a table with records matching $aauser if another AD account has the same name and surname
foreach ($aauser in $aausers) {
  $realuser = [array](get-aduser -Filter {((Givenname -eq $aauser.Givenname) -and (Surname -eq $aauser.Surname))}) 
  write-host $aauser.samaccountname "|" $ "|"  $aauser.enabled "|"$realuser[0].SamAccountname "|"$realuser[0].GivenName"|" $realuser[0].Surname"|" $realuser[0].Enabled

# Build array with GivenName, Surname that match filter of: Enabled field is false  (disabled account)
$temp = Get-ADUser -Properties GivenName, Surname -filter {Enabled -eq $false}

# Export the array to CSV file
$temp | Export-Csv temp.csv

AD Extract 2

$reportdate = Get-Date -Format yyyyMMdd-HHmm 
$csvreportfile = "ADUsers-extract-$reportdate.csv" 
Get-ADUser -SearchBase "OU=Users,DC=corp,DC=local" -Filter * -ResultSetSize 5000 | Get-ADUser -Properties * | 
          select SamAccountName,EmailAddress,Givenname,Surname,Title,Department,Enabled | 
          Export-Csv -Path $csvreportfile -NoTypeInformation

Create users from csv

Csv file BulkAddADUsers.csv

Full Name,Firstname,Surname,fsurname,,,$true,PassWord123,$true,"OU=Users,OU=Testing ,OU=USA,DC=corp-example,DC=io"


# CSV headline: Name,GivenName,Surname,SamAccountName,UserPrincipalName,EmailAddress,Enabled,AccountPassword,PasswordNeverExpires,Path
# Script            - CSV headline
# Name              - first+last name
# GivenName         - first name
# Surname           - last name
# SamAccountName    - username
# UserPrincipalName - it is user-logon-name, where you need to choose domain, eg. or
# Path - object location, use get-aduser <SamAccountName>

Import-Csv .\BulkAddADUsers.csv | % { `
New-ADUser -Name $_.Name -GivenName $_.GivenName -Surname $_.Surname -SamAccountName $_.SamAccountName `
  -UserPrincipalName $_.UserPrincipalName -EmailAddress $_.EmailAddress `
  -Enabled $true -AccountPassword (ConvertTo-SecureString $_.AccountPassword -AsPlainText -force) `
  -PasswordNeverExpires $true -Path $_.Path
#-Enabled cannot read $true value from CSV therefore it has been hard coded

Get membership of a user

Get-ADPrincipalGroupMembership username| select name

IIS - create a website

Tested on Server 2012 R2 Data Centre in Azure

$SiteName    = "WWW"
$AppPoolName = "WWWAppPool"
$SiteFolder  = Join-Path -Path 'C:\inetpub\wwwroot' -ChildPath $SiteName
$LogDir      = "d:\Logs\iis_logs\$SiteName"
$HostHeader  = ""

Import-Module WebAdministration
# create appPool
if(-Not (Test-Path IIS:\AppPools\$AppPoolName)) { New-WebAppPool -Name $AppPoolName -Force }
# create Site 
if(-Not (Test-Path $SiteFolder -pathType container)) { md $SiteFolder }
New-WebSite    -Name $SiteName -PhysicalPath $SiteFolder -Force -ApplicationPool $AppPoolName
Get-WebBinding -Name $SiteName -Port 80 | Remove-WebBinding
New-WebBinding -Name $SiteName -Protocol http -Port 80   -IPAddress * -HostHeader $HostHeader 
New-WebBinding -Name $SiteName -Protocol http -Port 8080 -IPAddress * -HostHeader $HostHeader 
# Logging dir
if (-Not (Test-Path $LogDir -pathType container)) { md $LogDir }
Set-ItemProperty "IIS:\Sites\$SiteName" -name -value $LogDir
Start-WebSite  -Name $SiteName

File content

$hostsFileContent = @" example.local 

Add-Content -Path "c:\Windows\System32\drivers\etc\hosts" -Value $hostsFileContent
Set-Content -Path "c:\Windows\System32\drivers\etc\hosts" -Value $hostsFileContent